decap-cms-backend-github 3.8.1 → 3.9.0-beta.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/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;
@@ -263,7 +263,7 @@ export default class GitHub {
263
263
  useOpenAuthoring: this.useOpenAuthoring,
264
264
  initialWorkflowStatus: this.options.initialWorkflowStatus,
265
265
  baseUrl: this.baseUrl,
266
- getUser: args => this.currentUser(args)
266
+ getUser: this.currentUser.bind(this)
267
267
  });
268
268
  const user = await this.api.user();
269
269
  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.1",
4
+ "version": "3.9.0-beta.0",
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-util": "3.8.1",
38
- "decap-cms-ui-default": "3.9.1",
39
- "decap-cms-lib-auth": "3.3.1"
37
+ "decap-cms-lib-util": "3.9.0-beta.0",
38
+ "decap-cms-lib-auth": "3.3.3-beta.0",
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
@@ -896,28 +896,46 @@ export default class API {
896
896
  );
897
897
  branches = branchesWithFilter.filter(b => b.filter).map(b => b.branch);
898
898
  } else {
899
- // backwards compatibility code, get relevant pull requests and migrate them
900
- const pullRequests = await this.getPullRequests(
899
+ // One fetch, two filters. `getPullRequests` re-requests the list for
900
+ // each predicate, and both calls here pass the same `head`/`state`, so
901
+ // the two below issued a byte-identical
902
+ // `GET /pulls?base=…&state=open&per_page=100` on every collection load —
903
+ // 2.0s + 1.8s of it when the request goes through a proxy. The predicate
904
+ // is applied client-side anyway, so the page only needs fetching once.
905
+ const openPullRequests = await this.getPullRequests(
901
906
  undefined,
902
907
  PullRequestState.Open,
908
+ () => true,
909
+ );
910
+
911
+ // Backwards compatibility: entries created before the CMS labelled its
912
+ // pull requests. Effectively always empty on a repo that has only ever
913
+ // been edited by a current CMS.
914
+ const unlabeled = openPullRequests.filter(
903
915
  pr => !pr.head.repo.fork && withoutCmsLabel(pr, this.cmsLabelPrefix),
904
916
  );
905
917
  let prCount = 0;
906
- for (const pr of pullRequests) {
918
+ for (const pr of unlabeled) {
907
919
  if (!migrationNotified) {
908
920
  migrationNotified = true;
909
921
  alert(oneLine`
910
- Decap CMS is adding labels to ${pullRequests.length} of your Editorial Workflow
922
+ Decap CMS is adding labels to ${unlabeled.length} of your Editorial Workflow
911
923
  entries. The "Workflow" tab will be unavailable during this migration. You may use other
912
924
  areas of the CMS during this time. Note that closing the CMS will pause the migration.
913
925
  `);
914
926
  }
915
927
  prCount = prCount + 1;
916
- await this.migratePullRequest(pr, `${prCount} of ${pullRequests.length}`);
928
+ await this.migratePullRequest(pr, `${prCount} of ${unlabeled.length}`);
917
929
  }
918
- const cmsPullRequests = await this.getPullRequests(undefined, PullRequestState.Open, pr =>
919
- withCmsLabel(pr, this.cmsLabelPrefix),
920
- );
930
+
931
+ // Migration is what puts the label on, so when it ran the list has to be
932
+ // read again — the page fetched above predates those labels. Conditional
933
+ // rather than unconditional precisely because that path is the rare one.
934
+ const cmsPullRequests = unlabeled.length
935
+ ? await this.getPullRequests(undefined, PullRequestState.Open, pr =>
936
+ withCmsLabel(pr, this.cmsLabelPrefix),
937
+ )
938
+ : openPullRequests.filter(pr => withCmsLabel(pr, this.cmsLabelPrefix));
921
939
  branches = cmsPullRequests.map(pr => pr.head.ref);
922
940
  }
923
941
 
@@ -723,6 +723,42 @@ describe('github API', () => {
723
723
  });
724
724
  });
725
725
 
726
+ describe('listUnpublishedBranches', () => {
727
+ function pullRequest(ref, labels) {
728
+ return { head: { ref, repo: { fork: false } }, labels: labels.map(name => ({ name })) };
729
+ }
730
+
731
+ it('reads the open pull request list once when nothing needs migrating', async () => {
732
+ const api = new API({ branch: 'main', repo: 'owner/repo' });
733
+ api.requestAllPages = jest
734
+ .fn()
735
+ .mockResolvedValue([pullRequest('cms/posts/a-post', ['decap-cms/pending_review'])]);
736
+
737
+ await expect(api.listUnpublishedBranches()).resolves.toEqual(['cms/posts/a-post']);
738
+
739
+ // The labelled and unlabelled sets are two filters over the same
740
+ // `GET /pulls?base=…&state=open` page, so asking for both must not cost
741
+ // two identical round trips.
742
+ expect(api.requestAllPages).toHaveBeenCalledTimes(1);
743
+ });
744
+
745
+ it('re-reads the list after a migration, because migrating is what adds the label', async () => {
746
+ const api = new API({ branch: 'main', repo: 'owner/repo' });
747
+ const unlabeled = pullRequest('cms/posts/legacy', []);
748
+ api.requestAllPages = jest
749
+ .fn()
750
+ .mockResolvedValueOnce([unlabeled])
751
+ .mockResolvedValueOnce([pullRequest('cms/posts/legacy', ['decap-cms/draft'])]);
752
+ api.migratePullRequest = jest.fn().mockResolvedValue(undefined);
753
+ global.alert = jest.fn();
754
+
755
+ await expect(api.listUnpublishedBranches()).resolves.toEqual(['cms/posts/legacy']);
756
+
757
+ expect(api.migratePullRequest).toHaveBeenCalledTimes(1);
758
+ expect(api.requestAllPages).toHaveBeenCalledTimes(2);
759
+ });
760
+ });
761
+
726
762
  describe('listFiles', () => {
727
763
  it('should get files by depth', async () => {
728
764
  const api = new API({ branch: 'master', repo: 'owner/repo' });
@@ -357,7 +357,7 @@ export default class GitHub implements Implementation {
357
357
  useOpenAuthoring: this.useOpenAuthoring,
358
358
  initialWorkflowStatus: this.options.initialWorkflowStatus,
359
359
  baseUrl: this.baseUrl,
360
- getUser: args => this.currentUser(args),
360
+ getUser: this.currentUser.bind(this),
361
361
  });
362
362
  const user = await this.api!.user();
363
363
  const isCollab = await this.api!.hasWriteAccess().catch(error => {