@opentermsarchive/engine 2.3.0 → 2.3.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opentermsarchive/engine",
3
- "version": "2.3.0",
3
+ "version": "2.3.2",
4
4
  "description": "Tracks and makes visible changes to the terms of online services",
5
5
  "homepage": "https://opentermsarchive.org",
6
6
  "bugs": {
@@ -90,7 +90,7 @@
90
90
  "mongodb": "^4.9.0",
91
91
  "morgan": "^1.10.0",
92
92
  "node-fetch": "^3.1.0",
93
- "octokit": "^1.7.0",
93
+ "octokit": "2.0.2",
94
94
  "pdfjs-dist": "^2.9.359",
95
95
  "puppeteer": "^22.8.1",
96
96
  "puppeteer-extra": "^3.3.6",
@@ -16,7 +16,14 @@ export default class GitHub {
16
16
  constructor(repository) {
17
17
  const { version } = require('../../package.json');
18
18
 
19
- this.octokit = new Octokit({ auth: process.env.OTA_ENGINE_GITHUB_TOKEN, userAgent: `opentermsarchive/${version}` });
19
+ this.octokit = new Octokit({
20
+ auth: process.env.OTA_ENGINE_GITHUB_TOKEN,
21
+ userAgent: `opentermsarchive/${version}`,
22
+ throttle: {
23
+ onRateLimit: () => false, // Do not retry after hitting a rate limit error
24
+ onSecondaryRateLimit: () => false, // Do not retry after hitting a secondary rate limit error
25
+ },
26
+ });
20
27
 
21
28
  const [ owner, repo ] = repository.split('/');
22
29
 
@@ -25,173 +32,132 @@ export default class GitHub {
25
32
 
26
33
  async initialize() {
27
34
  this.MANAGED_LABELS = require('./labels.json');
28
-
29
- const existingLabels = await this.getRepositoryLabels();
30
- const existingLabelsNames = existingLabels.map(label => label.name);
31
- const missingLabels = this.MANAGED_LABELS.filter(label => !existingLabelsNames.includes(label.name));
32
-
33
- if (missingLabels.length) {
34
- logger.info(`🤖 Following required labels are not present on the repository: ${missingLabels.map(label => `"${label.name}"`).join(', ')}. Creating them…`);
35
-
36
- for (const label of missingLabels) {
37
- await this.createLabel({ /* eslint-disable-line no-await-in-loop */
38
- name: label.name,
39
- color: label.color,
40
- description: `${label.description} ${MANAGED_BY_OTA_MARKER}`,
41
- });
35
+ try {
36
+ const existingLabels = await this.getRepositoryLabels();
37
+ const existingLabelsNames = existingLabels.map(label => label.name);
38
+ const missingLabels = this.MANAGED_LABELS.filter(label => !existingLabelsNames.includes(label.name));
39
+
40
+ if (missingLabels.length) {
41
+ logger.info(`Following required labels are not present on the repository: ${missingLabels.map(label => `"${label.name}"`).join(', ')}. Creating them…`);
42
+
43
+ for (const label of missingLabels) {
44
+ await this.createLabel({ /* eslint-disable-line no-await-in-loop */
45
+ name: label.name,
46
+ color: label.color,
47
+ description: `${label.description} ${MANAGED_BY_OTA_MARKER}`,
48
+ });
49
+ }
42
50
  }
51
+ } catch (error) {
52
+ logger.error(`Failed to handle repository labels: ${error.message}`);
43
53
  }
44
54
  }
45
55
 
46
56
  async getRepositoryLabels() {
47
- try {
48
- const { data: labels } = await this.octokit.request('GET /repos/{owner}/{repo}/labels', { ...this.commonParams });
57
+ const { data: labels } = await this.octokit.request('GET /repos/{owner}/{repo}/labels', { ...this.commonParams });
49
58
 
50
- return labels;
51
- } catch (error) {
52
- logger.error(`🤖 Could not get labels: ${error}`);
53
- }
59
+ return labels;
54
60
  }
55
61
 
56
62
  async createLabel({ name, color, description }) {
57
- try {
58
- await this.octokit.request('POST /repos/{owner}/{repo}/labels', {
59
- ...this.commonParams,
60
- name,
61
- color,
62
- description,
63
- });
64
-
65
- logger.info(`🤖 Created repository label "${name}"`);
66
- } catch (error) {
67
- logger.error(`🤖 Could not create label "${name}": ${error}`);
68
- }
63
+ await this.octokit.request('POST /repos/{owner}/{repo}/labels', {
64
+ ...this.commonParams,
65
+ name,
66
+ color,
67
+ description,
68
+ });
69
69
  }
70
70
 
71
71
  async createIssue({ title, description: body, labels }) {
72
- try {
73
- const { data: issue } = await this.octokit.request('POST /repos/{owner}/{repo}/issues', {
74
- ...this.commonParams,
75
- title,
76
- body,
77
- labels,
78
- });
79
-
80
- logger.info(`🤖 Created GitHub issue #${issue.number} "${title}": ${issue.html_url}`);
81
-
82
- return issue;
83
- } catch (error) {
84
- logger.error(`🤖 Could not create GitHub issue "${title}": ${error}`);
85
- }
72
+ const { data: issue } = await this.octokit.request('POST /repos/{owner}/{repo}/issues', {
73
+ ...this.commonParams,
74
+ title,
75
+ body,
76
+ labels,
77
+ });
78
+
79
+ return issue;
86
80
  }
87
81
 
88
- async setIssueLabels({ issue, labels }) {
89
- try {
90
- await this.octokit.request('PUT /repos/{owner}/{repo}/issues/{issue_number}/labels', {
91
- ...this.commonParams,
92
- issue_number: issue.number,
93
- labels,
94
- });
82
+ async updateIssue(issue, { state, labels }) {
83
+ const { data: updatedIssue } = await this.octokit.request('PATCH /repos/{owner}/{repo}/issues/{issue_number}', {
84
+ ...this.commonParams,
85
+ issue_number: issue.number,
86
+ state,
87
+ labels,
88
+ });
95
89
 
96
- logger.info(`🤖 Updated labels to GitHub issue #${issue.number}`);
97
- } catch (error) {
98
- logger.error(`🤖 Could not update GitHub issue #${issue.number} "${issue.title}": ${error}`);
99
- }
100
- }
101
-
102
- async openIssue(issue) {
103
- try {
104
- await this.octokit.request('PATCH /repos/{owner}/{repo}/issues/{issue_number}', {
105
- ...this.commonParams,
106
- issue_number: issue.number,
107
- state: GitHub.ISSUE_STATE_OPEN,
108
- });
109
-
110
- logger.info(`🤖 Opened GitHub issue #${issue.number}`);
111
- } catch (error) {
112
- logger.error(`🤖 Could not update GitHub issue #${issue.number} "${issue.title}": ${error}`);
113
- }
114
- }
115
-
116
- async closeIssue(issue) {
117
- try {
118
- await this.octokit.request('PATCH /repos/{owner}/{repo}/issues/{issue_number}', {
119
- ...this.commonParams,
120
- issue_number: issue.number,
121
- state: GitHub.ISSUE_STATE_CLOSED,
122
- });
123
-
124
- logger.info(`🤖 Closed GitHub issue #${issue.number}`);
125
- } catch (error) {
126
- logger.error(`🤖 Could not update GitHub issue #${issue.number} "${issue.title}": ${error}`);
127
- }
90
+ return updatedIssue;
128
91
  }
129
92
 
130
93
  async getIssue({ title, ...searchParams }) {
131
- try {
132
- const issues = await this.octokit.paginate('GET /repos/{owner}/{repo}/issues', {
133
- ...this.commonParams,
134
- per_page: 100,
135
- ...searchParams,
136
- }, response => response.data);
94
+ const issues = await this.octokit.paginate('GET /repos/{owner}/{repo}/issues', {
95
+ ...this.commonParams,
96
+ per_page: 100,
97
+ ...searchParams,
98
+ }, response => response.data);
137
99
 
138
- const [issue] = issues.filter(item => item.title === title); // since only one is expected, use the first one
100
+ const [issue] = issues.filter(item => item.title === title); // Since only one is expected, use the first one
139
101
 
140
- return issue;
141
- } catch (error) {
142
- logger.error(`🤖 Could not find GitHub issue "${title}": ${error}`);
143
- }
102
+ return issue;
144
103
  }
145
104
 
146
105
  async addCommentToIssue({ issue, comment: body }) {
147
- try {
148
- const { data: comment } = await this.octokit.request('POST /repos/{owner}/{repo}/issues/{issue_number}/comments', {
149
- ...this.commonParams,
150
- issue_number: issue.number,
151
- body,
152
- });
106
+ const { data: comment } = await this.octokit.request('POST /repos/{owner}/{repo}/issues/{issue_number}/comments', {
107
+ ...this.commonParams,
108
+ issue_number: issue.number,
109
+ body,
110
+ });
153
111
 
154
- logger.info(`🤖 Added comment to GitHub issue #${issue.number}: ${comment.html_url}`);
155
-
156
- return comment;
157
- } catch (error) {
158
- logger.error(`🤖 Could not add comment to GitHub issue #${issue.number} "${issue.title}": ${error}`);
159
- }
112
+ return comment;
160
113
  }
161
114
 
162
115
  async closeIssueWithCommentIfExists({ title, comment }) {
163
- const openedIssue = await this.getIssue({ title, state: GitHub.ISSUE_STATE_OPEN });
116
+ try {
117
+ const openedIssue = await this.getIssue({ title, state: GitHub.ISSUE_STATE_OPEN });
164
118
 
165
- if (!openedIssue) {
166
- return;
167
- }
119
+ if (!openedIssue) {
120
+ return;
121
+ }
168
122
 
169
- await this.addCommentToIssue({ issue: openedIssue, comment });
123
+ await this.addCommentToIssue({ issue: openedIssue, comment });
124
+ logger.info(`Added comment to issue #${openedIssue.number}: ${openedIssue.html_url}`);
170
125
 
171
- return this.closeIssue(openedIssue);
126
+ await this.updateIssue(openedIssue, { state: GitHub.ISSUE_STATE_CLOSED });
127
+ logger.info(`Closed issue #${openedIssue.number}: ${openedIssue.html_url}`);
128
+ } catch (error) {
129
+ logger.error(`Failed to update issue "${title}": ${error.message}`);
130
+ }
172
131
  }
173
132
 
174
133
  async createOrUpdateIssue({ title, description, label }) {
175
- const issue = await this.getIssue({ title, state: GitHub.ISSUE_STATE_ALL });
134
+ try {
135
+ const issue = await this.getIssue({ title, state: GitHub.ISSUE_STATE_ALL });
176
136
 
177
- if (!issue) {
178
- return this.createIssue({ title, description, labels: [label] });
179
- }
137
+ if (!issue) {
138
+ const createdIssue = await this.createIssue({ title, description, labels: [label] });
180
139
 
181
- if (issue.state == GitHub.ISSUE_STATE_CLOSED) {
182
- await this.openIssue(issue);
183
- }
140
+ return logger.info(`Created issue #${createdIssue.number} "${title}": ${createdIssue.html_url}`);
141
+ }
184
142
 
185
- const managedLabelsNames = this.MANAGED_LABELS.map(label => label.name);
186
- const [managedLabel] = issue.labels.filter(label => managedLabelsNames.includes(label.name)); // it is assumed that only one specific reason for failure is possible at a time, making managed labels mutually exclusive
143
+ const managedLabelsNames = this.MANAGED_LABELS.map(label => label.name);
144
+ const labelsNotManagedToKeep = issue.labels.map(label => label.name).filter(label => !managedLabelsNames.includes(label));
145
+ const [managedLabel] = issue.labels.filter(label => managedLabelsNames.includes(label.name)); // It is assumed that only one specific reason for failure is possible at a time, making managed labels mutually exclusive
187
146
 
188
- if (managedLabel?.name == label) { // if the label is already assigned to the issue, the error is redundant with the one already reported and no further action is necessary
189
- return;
190
- }
147
+ if (issue.state !== GitHub.ISSUE_STATE_CLOSED && managedLabel?.name === label) {
148
+ return;
149
+ }
191
150
 
192
- const labelsNotManagedToKeep = issue.labels.map(label => label.name).filter(label => !managedLabelsNames.includes(label));
151
+ await this.updateIssue(issue, {
152
+ state: GitHub.ISSUE_STATE_OPEN,
153
+ labels: [ label, ...labelsNotManagedToKeep ],
154
+ });
155
+ logger.info(`Updated issue #${issue.number}: ${issue.html_url}`);
156
+ await this.addCommentToIssue({ issue, comment: description });
193
157
 
194
- await this.setIssueLabels({ issue, labels: [ label, ...labelsNotManagedToKeep ] });
195
- await this.addCommentToIssue({ issue, comment: description });
158
+ logger.info(`Added comment to issue #${issue.number}: ${issue.html_url}`);
159
+ } catch (error) {
160
+ logger.error(`Failed to update issue "${title}": ${error.message}`);
161
+ }
196
162
  }
197
163
  }
@@ -118,66 +118,6 @@ describe('GitHub', function () {
118
118
  });
119
119
  });
120
120
 
121
- describe('#setIssueLabels', () => {
122
- let scope;
123
- const ISSUE_NUMBER = 123;
124
- const LABELS = [ 'bug', 'enhancement' ];
125
-
126
- before(async () => {
127
- scope = nock('https://api.github.com')
128
- .put(`/repos/owner/repo/issues/${ISSUE_NUMBER}/labels`, { labels: LABELS })
129
- .reply(200);
130
-
131
- await github.setIssueLabels({ issue: { number: ISSUE_NUMBER }, labels: LABELS });
132
- });
133
-
134
- after(nock.cleanAll);
135
-
136
- it('sets labels on the issue', () => {
137
- expect(scope.isDone()).to.be.true;
138
- });
139
- });
140
-
141
- describe('#openIssue', () => {
142
- let scope;
143
- const ISSUE = { number: 123 };
144
- const EXPECTED_REQUEST_BODY = { state: 'open' };
145
-
146
- before(async () => {
147
- scope = nock('https://api.github.com')
148
- .patch(`/repos/owner/repo/issues/${ISSUE.number}`, EXPECTED_REQUEST_BODY)
149
- .reply(200);
150
-
151
- await github.openIssue(ISSUE);
152
- });
153
-
154
- after(nock.cleanAll);
155
-
156
- it('opens the issue', () => {
157
- expect(scope.isDone()).to.be.true;
158
- });
159
- });
160
-
161
- describe('#closeIssue', () => {
162
- let scope;
163
- const ISSUE = { number: 123 };
164
- const EXPECTED_REQUEST_BODY = { state: 'closed' };
165
-
166
- before(async () => {
167
- scope = nock('https://api.github.com')
168
- .patch(`/repos/owner/repo/issues/${ISSUE.number}`, EXPECTED_REQUEST_BODY)
169
- .reply(200);
170
-
171
- await github.closeIssue(ISSUE);
172
- });
173
-
174
- after(nock.cleanAll);
175
-
176
- it('closes the issue', () => {
177
- expect(scope.isDone()).to.be.true;
178
- });
179
- });
180
-
181
121
  describe('#getIssue', () => {
182
122
  let scope;
183
123
  let result;
@@ -376,9 +316,8 @@ describe('GitHub', function () {
376
316
  };
377
317
 
378
318
  context('when issue is closed', () => {
379
- let setIssueLabelsScope;
319
+ let updateIssueScope;
380
320
  let addCommentScope;
381
- let openIssueScope;
382
321
 
383
322
  const GITHUB_RESPONSE_FOR_EXISTING_ISSUE = {
384
323
  number: 123,
@@ -394,12 +333,8 @@ describe('GitHub', function () {
394
333
  .query(true)
395
334
  .reply(200, [GITHUB_RESPONSE_FOR_EXISTING_ISSUE]);
396
335
 
397
- openIssueScope = nock('https://api.github.com')
398
- .patch(`/repos/owner/repo/issues/${GITHUB_RESPONSE_FOR_EXISTING_ISSUE.number}`, { state: GitHub.ISSUE_STATE_OPEN })
399
- .reply(200);
400
-
401
- setIssueLabelsScope = nock('https://api.github.com')
402
- .put(`/repos/owner/repo/issues/${GITHUB_RESPONSE_FOR_EXISTING_ISSUE.number}/labels`, { labels: ['location'] })
336
+ updateIssueScope = nock('https://api.github.com')
337
+ .patch(`/repos/owner/repo/issues/${GITHUB_RESPONSE_FOR_EXISTING_ISSUE.number}`, { state: GitHub.ISSUE_STATE_OPEN, labels: ['location'] })
403
338
  .reply(200);
404
339
 
405
340
  addCommentScope = nock('https://api.github.com')
@@ -409,12 +344,8 @@ describe('GitHub', function () {
409
344
  await github.createOrUpdateIssue(ISSUE);
410
345
  });
411
346
 
412
- it('reopens the issue', () => {
413
- expect(openIssueScope.isDone()).to.be.true;
414
- });
415
-
416
- it("updates the issue's label", () => {
417
- expect(setIssueLabelsScope.isDone()).to.be.true;
347
+ it('reopens the issue and its labels', () => {
348
+ expect(updateIssueScope.isDone()).to.be.true;
418
349
  });
419
350
 
420
351
  it('adds comment to the issue', () => {
@@ -423,9 +354,8 @@ describe('GitHub', function () {
423
354
  });
424
355
 
425
356
  context('when issue is already opened', () => {
426
- let setIssueLabelsScope;
427
357
  let addCommentScope;
428
- let openIssueScope;
358
+ let updateIssueScope;
429
359
 
430
360
  const GITHUB_RESPONSE_FOR_EXISTING_ISSUE = {
431
361
  number: 123,
@@ -441,12 +371,8 @@ describe('GitHub', function () {
441
371
  .query(true)
442
372
  .reply(200, [GITHUB_RESPONSE_FOR_EXISTING_ISSUE]);
443
373
 
444
- openIssueScope = nock('https://api.github.com')
445
- .patch(`/repos/owner/repo/issues/${GITHUB_RESPONSE_FOR_EXISTING_ISSUE.number}`, { state: GitHub.ISSUE_STATE_OPEN })
446
- .reply(200);
447
-
448
- setIssueLabelsScope = nock('https://api.github.com')
449
- .put(`/repos/owner/repo/issues/${GITHUB_RESPONSE_FOR_EXISTING_ISSUE.number}/labels`, { labels: ['location'] })
374
+ updateIssueScope = nock('https://api.github.com')
375
+ .patch(`/repos/owner/repo/issues/${GITHUB_RESPONSE_FOR_EXISTING_ISSUE.number}`, { state: GitHub.ISSUE_STATE_OPEN, labels: ['location'] })
450
376
  .reply(200);
451
377
 
452
378
  addCommentScope = nock('https://api.github.com')
@@ -456,12 +382,8 @@ describe('GitHub', function () {
456
382
  await github.createOrUpdateIssue(ISSUE);
457
383
  });
458
384
 
459
- it('does not change the issue state', () => {
460
- expect(openIssueScope.isDone()).to.be.false;
461
- });
462
-
463
- it("updates the issue's label", () => {
464
- expect(setIssueLabelsScope.isDone()).to.be.true;
385
+ it("updates the issue's labels", () => {
386
+ expect(updateIssueScope.isDone()).to.be.true;
465
387
  });
466
388
 
467
389
  it('adds comment to the issue', () => {