decap-cms-backend-github 3.8.3 → 3.9.0-beta.1

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/esm/API.js CHANGED
@@ -642,22 +642,36 @@ export default class API {
642
642
  const branchesWithFilter = await Promise.all(branches.map(b => this.filterOpenAuthoringBranches(b)));
643
643
  branches = branchesWithFilter.filter(b => b.filter).map(b => b.branch);
644
644
  } else {
645
- // backwards compatibility code, get relevant pull requests and migrate them
646
- const pullRequests = await this.getPullRequests(undefined, PullRequestState.Open, pr => !pr.head.repo.fork && withoutCmsLabel(pr, this.cmsLabelPrefix));
645
+ // One fetch, two filters. `getPullRequests` re-requests the list for
646
+ // each predicate, and both calls here pass the same `head`/`state`, so
647
+ // the two below issued a byte-identical
648
+ // `GET /pulls?base=…&state=open&per_page=100` on every collection load —
649
+ // 2.0s + 1.8s of it when the request goes through a proxy. The predicate
650
+ // is applied client-side anyway, so the page only needs fetching once.
651
+ const openPullRequests = await this.getPullRequests(undefined, PullRequestState.Open, () => true);
652
+
653
+ // Backwards compatibility: entries created before the CMS labelled its
654
+ // pull requests. Effectively always empty on a repo that has only ever
655
+ // been edited by a current CMS.
656
+ const unlabeled = openPullRequests.filter(pr => !pr.head.repo.fork && withoutCmsLabel(pr, this.cmsLabelPrefix));
647
657
  let prCount = 0;
648
- for (const pr of pullRequests) {
658
+ for (const pr of unlabeled) {
649
659
  if (!migrationNotified) {
650
660
  migrationNotified = true;
651
661
  alert(oneLine`
652
- Decap CMS is adding labels to ${pullRequests.length} of your Editorial Workflow
662
+ Decap CMS is adding labels to ${unlabeled.length} of your Editorial Workflow
653
663
  entries. The "Workflow" tab will be unavailable during this migration. You may use other
654
664
  areas of the CMS during this time. Note that closing the CMS will pause the migration.
655
665
  `);
656
666
  }
657
667
  prCount = prCount + 1;
658
- await this.migratePullRequest(pr, `${prCount} of ${pullRequests.length}`);
668
+ await this.migratePullRequest(pr, `${prCount} of ${unlabeled.length}`);
659
669
  }
660
- const cmsPullRequests = await this.getPullRequests(undefined, PullRequestState.Open, pr => withCmsLabel(pr, this.cmsLabelPrefix));
670
+
671
+ // Migration is what puts the label on, so when it ran the list has to be
672
+ // read again — the page fetched above predates those labels. Conditional
673
+ // rather than unconditional precisely because that path is the rare one.
674
+ const cmsPullRequests = unlabeled.length ? await this.getPullRequests(undefined, PullRequestState.Open, pr => withCmsLabel(pr, this.cmsLabelPrefix)) : openPullRequests.filter(pr => withCmsLabel(pr, this.cmsLabelPrefix));
661
675
  branches = cmsPullRequests.map(pr => pr.head.ref);
662
676
  }
663
677
  return branches;
@@ -1218,7 +1232,7 @@ export default class API {
1218
1232
  }
1219
1233
 
1220
1234
  /**
1221
- * Constants for note formatting to aid with PR comment to note conversion
1235
+ * How a notes issue is recognised on the host.
1222
1236
  */
1223
1237
  static NOTES_LABEL = 'decap-cms-notes';
1224
1238
  static NOTE_ISSUE_PREFIX = 'Notes: ';
@@ -262,7 +262,7 @@ export default class GitHub {
262
262
  useOpenAuthoring: this.useOpenAuthoring,
263
263
  initialWorkflowStatus: this.options.initialWorkflowStatus,
264
264
  baseUrl: this.baseUrl,
265
- getUser: args => this.currentUser(args)
265
+ getUser: this.currentUser.bind(this)
266
266
  });
267
267
  const user = await this.api.user();
268
268
  const isCollab = await this.api.hasWriteAccess().catch(error => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "decap-cms-backend-github",
3
3
  "description": "GitHub backend for Decap CMS",
4
- "version": "3.8.3",
4
+ "version": "3.9.0-beta.1",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -34,9 +34,9 @@
34
34
  "lodash": "^4.17.11",
35
35
  "prop-types": "^15.7.2",
36
36
  "react": "^19.1.0",
37
- "decap-cms-lib-auth": "3.3.2",
38
- "decap-cms-lib-util": "3.8.3",
39
- "decap-cms-ui-default": "3.9.2"
37
+ "decap-cms-lib-auth": "3.3.3-beta.0",
38
+ "decap-cms-lib-util": "3.9.0-beta.1",
39
+ "decap-cms-ui-default": "3.9.3-beta.0"
40
40
  },
41
41
  "browser": {
42
42
  "path": "path-browserify"
package/src/API.ts CHANGED
@@ -899,28 +899,46 @@ export default class API {
899
899
  );
900
900
  branches = branchesWithFilter.filter(b => b.filter).map(b => b.branch);
901
901
  } else {
902
- // backwards compatibility code, get relevant pull requests and migrate them
903
- const pullRequests = await this.getPullRequests(
902
+ // One fetch, two filters. `getPullRequests` re-requests the list for
903
+ // each predicate, and both calls here pass the same `head`/`state`, so
904
+ // the two below issued a byte-identical
905
+ // `GET /pulls?base=…&state=open&per_page=100` on every collection load —
906
+ // 2.0s + 1.8s of it when the request goes through a proxy. The predicate
907
+ // is applied client-side anyway, so the page only needs fetching once.
908
+ const openPullRequests = await this.getPullRequests(
904
909
  undefined,
905
910
  PullRequestState.Open,
911
+ () => true,
912
+ );
913
+
914
+ // Backwards compatibility: entries created before the CMS labelled its
915
+ // pull requests. Effectively always empty on a repo that has only ever
916
+ // been edited by a current CMS.
917
+ const unlabeled = openPullRequests.filter(
906
918
  pr => !pr.head.repo.fork && withoutCmsLabel(pr, this.cmsLabelPrefix),
907
919
  );
908
920
  let prCount = 0;
909
- for (const pr of pullRequests) {
921
+ for (const pr of unlabeled) {
910
922
  if (!migrationNotified) {
911
923
  migrationNotified = true;
912
924
  alert(oneLine`
913
- Decap CMS is adding labels to ${pullRequests.length} of your Editorial Workflow
925
+ Decap CMS is adding labels to ${unlabeled.length} of your Editorial Workflow
914
926
  entries. The "Workflow" tab will be unavailable during this migration. You may use other
915
927
  areas of the CMS during this time. Note that closing the CMS will pause the migration.
916
928
  `);
917
929
  }
918
930
  prCount = prCount + 1;
919
- await this.migratePullRequest(pr, `${prCount} of ${pullRequests.length}`);
931
+ await this.migratePullRequest(pr, `${prCount} of ${unlabeled.length}`);
920
932
  }
921
- const cmsPullRequests = await this.getPullRequests(undefined, PullRequestState.Open, pr =>
922
- withCmsLabel(pr, this.cmsLabelPrefix),
923
- );
933
+
934
+ // Migration is what puts the label on, so when it ran the list has to be
935
+ // read again — the page fetched above predates those labels. Conditional
936
+ // rather than unconditional precisely because that path is the rare one.
937
+ const cmsPullRequests = unlabeled.length
938
+ ? await this.getPullRequests(undefined, PullRequestState.Open, pr =>
939
+ withCmsLabel(pr, this.cmsLabelPrefix),
940
+ )
941
+ : openPullRequests.filter(pr => withCmsLabel(pr, this.cmsLabelPrefix));
924
942
  branches = cmsPullRequests.map(pr => pr.head.ref);
925
943
  }
926
944
 
@@ -1556,10 +1574,10 @@ export default class API {
1556
1574
  }
1557
1575
 
1558
1576
  /**
1559
- * Constants for note formatting to aid with PR comment to note conversion
1577
+ * How a notes issue is recognised on the host.
1560
1578
  */
1561
- private static readonly NOTES_LABEL = 'decap-cms-notes';
1562
- private static readonly NOTE_ISSUE_PREFIX = 'Notes: ';
1579
+ static readonly NOTES_LABEL = 'decap-cms-notes';
1580
+ static readonly NOTE_ISSUE_PREFIX = 'Notes: ';
1563
1581
 
1564
1582
  /**
1565
1583
  * Parse a GitHub comment into a Note object
@@ -724,6 +724,42 @@ describe('github API', () => {
724
724
  });
725
725
  });
726
726
 
727
+ describe('listUnpublishedBranches', () => {
728
+ function pullRequest(ref, labels) {
729
+ return { head: { ref, repo: { fork: false } }, labels: labels.map(name => ({ name })) };
730
+ }
731
+
732
+ it('reads the open pull request list once when nothing needs migrating', async () => {
733
+ const api = new API({ branch: 'main', repo: 'owner/repo' });
734
+ api.requestAllPages = jest
735
+ .fn()
736
+ .mockResolvedValue([pullRequest('cms/posts/a-post', ['decap-cms/pending_review'])]);
737
+
738
+ await expect(api.listUnpublishedBranches()).resolves.toEqual(['cms/posts/a-post']);
739
+
740
+ // The labelled and unlabelled sets are two filters over the same
741
+ // `GET /pulls?base=…&state=open` page, so asking for both must not cost
742
+ // two identical round trips.
743
+ expect(api.requestAllPages).toHaveBeenCalledTimes(1);
744
+ });
745
+
746
+ it('re-reads the list after a migration, because migrating is what adds the label', async () => {
747
+ const api = new API({ branch: 'main', repo: 'owner/repo' });
748
+ const unlabeled = pullRequest('cms/posts/legacy', []);
749
+ api.requestAllPages = jest
750
+ .fn()
751
+ .mockResolvedValueOnce([unlabeled])
752
+ .mockResolvedValueOnce([pullRequest('cms/posts/legacy', ['decap-cms/draft'])]);
753
+ api.migratePullRequest = jest.fn().mockResolvedValue(undefined);
754
+ global.alert = jest.fn();
755
+
756
+ await expect(api.listUnpublishedBranches()).resolves.toEqual(['cms/posts/legacy']);
757
+
758
+ expect(api.migratePullRequest).toHaveBeenCalledTimes(1);
759
+ expect(api.requestAllPages).toHaveBeenCalledTimes(2);
760
+ });
761
+ });
762
+
727
763
  describe('listFiles', () => {
728
764
  it('should get files by depth', async () => {
729
765
  const api = new API({ branch: 'master', repo: 'owner/repo' });
@@ -358,7 +358,7 @@ export default class GitHub implements Implementation {
358
358
  useOpenAuthoring: this.useOpenAuthoring,
359
359
  initialWorkflowStatus: this.options.initialWorkflowStatus,
360
360
  baseUrl: this.baseUrl,
361
- getUser: args => this.currentUser(args),
361
+ getUser: this.currentUser.bind(this),
362
362
  });
363
363
  const user = await this.api!.user();
364
364
  const isCollab = await this.api!.hasWriteAccess().catch(error => {