decap-cms-backend-github 3.1.0-beta.0 → 3.1.0-beta.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/dist/esm/API.js CHANGED
@@ -77,6 +77,7 @@ class API {
77
77
  constructor(config) {
78
78
  _defineProperty(this, "apiRoot", void 0);
79
79
  _defineProperty(this, "token", void 0);
80
+ _defineProperty(this, "tokenKeyword", void 0);
80
81
  _defineProperty(this, "branch", void 0);
81
82
  _defineProperty(this, "useOpenAuthoring", void 0);
82
83
  _defineProperty(this, "repo", void 0);
@@ -90,6 +91,8 @@ class API {
90
91
  _defineProperty(this, "mergeMethod", void 0);
91
92
  _defineProperty(this, "initialWorkflowStatus", void 0);
92
93
  _defineProperty(this, "cmsLabelPrefix", void 0);
94
+ _defineProperty(this, "baseUrl", void 0);
95
+ _defineProperty(this, "getUser", void 0);
93
96
  _defineProperty(this, "_userPromise", void 0);
94
97
  _defineProperty(this, "_metadataSemaphore", void 0);
95
98
  _defineProperty(this, "commitAuthor", void 0);
@@ -122,6 +125,7 @@ class API {
122
125
  });
123
126
  this.apiRoot = config.apiRoot || 'https://api.github.com';
124
127
  this.token = config.token || '';
128
+ this.tokenKeyword = config.tokenKeyword || 'token';
125
129
  this.branch = config.branch || 'master';
126
130
  this.useOpenAuthoring = config.useOpenAuthoring;
127
131
  this.repo = config.repo || '';
@@ -137,16 +141,17 @@ class API {
137
141
  this.mergeMethod = config.squashMerges ? 'squash' : 'merge';
138
142
  this.cmsLabelPrefix = config.cmsLabelPrefix;
139
143
  this.initialWorkflowStatus = config.initialWorkflowStatus;
144
+ this.baseUrl = config.baseUrl;
145
+ this.getUser = config.getUser;
140
146
  }
141
147
  user() {
142
148
  if (!this._userPromise) {
143
- this._userPromise = this.getUser();
149
+ this._userPromise = this.getUser({
150
+ token: this.token
151
+ });
144
152
  }
145
153
  return this._userPromise;
146
154
  }
147
- getUser() {
148
- return this.request('/user');
149
- }
150
155
  async hasWriteAccess() {
151
156
  try {
152
157
  const result = await this.request(this.repoURL);
@@ -166,7 +171,7 @@ class API {
166
171
  'Content-Type': 'application/json; charset=utf-8'
167
172
  }, headers);
168
173
  if (this.token) {
169
- baseHeader.Authorization = `token ${this.token}`;
174
+ baseHeader.Authorization = `${this.tokenKeyword} ${this.token}`;
170
175
  return Promise.resolve(baseHeader);
171
176
  }
172
177
  return Promise.resolve(baseHeader);
@@ -444,7 +449,7 @@ class API {
444
449
  return;
445
450
  }
446
451
  try {
447
- const user = await this.request(`/users/${pullRequest.user.login}`);
452
+ const user = await this.user();
448
453
  return user.name || user.login;
449
454
  } catch {
450
455
  return;
@@ -58,7 +58,7 @@ class GraphQLAPI extends _API.default {
58
58
  headers: _objectSpread(_objectSpread({
59
59
  'Content-Type': 'application/json; charset=utf-8'
60
60
  }, headers), {}, {
61
- authorization: this.token ? `token ${this.token}` : ''
61
+ authorization: this.token ? `${this.tokenKeyword} ${this.token}` : ''
62
62
  })
63
63
  };
64
64
  });
@@ -45,9 +45,12 @@ class GitHub {
45
45
  _defineProperty(this, "mediaFolder", void 0);
46
46
  _defineProperty(this, "previewContext", void 0);
47
47
  _defineProperty(this, "token", void 0);
48
+ _defineProperty(this, "tokenKeyword", void 0);
48
49
  _defineProperty(this, "squashMerges", void 0);
49
50
  _defineProperty(this, "cmsLabelPrefix", void 0);
50
51
  _defineProperty(this, "useGraphql", void 0);
52
+ _defineProperty(this, "baseUrl", void 0);
53
+ _defineProperty(this, "bypassWriteAccessCheckForAppTokens", false);
51
54
  _defineProperty(this, "_currentUserPromise", void 0);
52
55
  _defineProperty(this, "_userIsOriginMaintainerPromises", void 0);
53
56
  _defineProperty(this, "_mediaDisplayURLSem", void 0);
@@ -104,6 +107,8 @@ class GitHub {
104
107
  this.branch = ((_config$backend$branc = config.backend.branch) === null || _config$backend$branc === void 0 ? void 0 : _config$backend$branc.trim()) || 'master';
105
108
  this.apiRoot = config.backend.api_root || 'https://api.github.com';
106
109
  this.token = '';
110
+ this.tokenKeyword = 'token';
111
+ this.baseUrl = config.backend.base_url;
107
112
  this.squashMerges = config.backend.squash_merges || false;
108
113
  this.cmsLabelPrefix = config.backend.cms_label_prefix || '';
109
114
  this.useGraphql = config.backend.use_graphql || false;
@@ -124,8 +129,10 @@ class GitHub {
124
129
  let auth = false;
125
130
  // no need to check auth if api is down
126
131
  if (api) {
127
- var _this$api;
128
- auth = (await ((_this$api = this.api) === null || _this$api === void 0 ? void 0 : _this$api.getUser().then(user => !!user).catch(e => {
132
+ var _this$api, _this$token;
133
+ auth = (await ((_this$api = this.api) === null || _this$api === void 0 ? void 0 : _this$api.getUser({
134
+ token: (_this$token = this.token) !== null && _this$token !== void 0 ? _this$token : ''
135
+ }).then(user => !!user).catch(e => {
129
136
  console.warn('Failed getting GitHub user', e);
130
137
  return false;
131
138
  }))) || false;
@@ -162,7 +169,7 @@ class GitHub {
162
169
  while (!repoExists) {
163
170
  repoExists = await fetch(`${this.apiRoot}/repos/${repo}`, {
164
171
  headers: {
165
- Authorization: `token ${token}`
172
+ Authorization: `${this.tokenKeyword} ${token}`
166
173
  }
167
174
  }).then(() => true).catch(err => {
168
175
  if (err && err.status === 404) {
@@ -185,7 +192,7 @@ class GitHub {
185
192
  if (!this._currentUserPromise) {
186
193
  this._currentUserPromise = fetch(`${this.apiRoot}/user`, {
187
194
  headers: {
188
- Authorization: `token ${token}`
195
+ Authorization: `${this.tokenKeyword} ${token}`
189
196
  }
190
197
  }).then(res => res.json());
191
198
  }
@@ -202,7 +209,7 @@ class GitHub {
202
209
  if (!this._userIsOriginMaintainerPromises[username]) {
203
210
  this._userIsOriginMaintainerPromises[username] = fetch(`${this.apiRoot}/repos/${this.originRepo}/collaborators/${username}/permission`, {
204
211
  headers: {
205
- Authorization: `token ${token}`
212
+ Authorization: `${this.tokenKeyword} ${token}`
206
213
  }
207
214
  }).then(res => res.json()).then(({
208
215
  permission
@@ -221,7 +228,7 @@ class GitHub {
221
228
  const repo = await fetch(`${this.apiRoot}/repos/${currentUser.login}/${repoName}`, {
222
229
  method: 'GET',
223
230
  headers: {
224
- Authorization: `token ${token}`
231
+ Authorization: `${this.tokenKeyword} ${token}`
225
232
  }
226
233
  }).then(res => res.json());
227
234
 
@@ -252,29 +259,47 @@ class GitHub {
252
259
  this.useOpenAuthoring = false;
253
260
  return Promise.resolve();
254
261
  }
255
- if (!(await this.forkExists({
262
+
263
+ // If a fork exists merge it with upstream
264
+ // otherwise create a new fork.
265
+ const currentUser = await this.currentUser({
256
266
  token
257
- }))) {
258
- await getPermissionToFork();
259
- }
260
- const fork = await fetch(`${this.apiRoot}/repos/${this.originRepo}/forks`, {
261
- method: 'POST',
262
- headers: {
263
- Authorization: `token ${token}`
264
- }
265
- }).then(res => res.json());
267
+ });
268
+ const repoName = this.originRepo.split('/')[1];
269
+ this.repo = `${currentUser.login}/${repoName}`;
266
270
  this.useOpenAuthoring = true;
267
- this.repo = fork.full_name;
268
- return this.pollUntilForkExists({
269
- repo: fork.full_name,
271
+ if (await this.forkExists({
270
272
  token
271
- });
273
+ })) {
274
+ return fetch(`${this.apiRoot}/repos/${this.repo}/merge-upstream`, {
275
+ method: 'POST',
276
+ headers: {
277
+ Authorization: `${this.tokenKeyword} ${token}`
278
+ },
279
+ body: JSON.stringify({
280
+ branch: this.branch
281
+ })
282
+ });
283
+ } else {
284
+ await getPermissionToFork();
285
+ const fork = await fetch(`${this.apiRoot}/repos/${this.originRepo}/forks`, {
286
+ method: 'POST',
287
+ headers: {
288
+ Authorization: `${this.tokenKeyword} ${token}`
289
+ }
290
+ }).then(res => res.json());
291
+ return this.pollUntilForkExists({
292
+ repo: fork.full_name,
293
+ token
294
+ });
295
+ }
272
296
  }
273
297
  async authenticate(state) {
274
298
  this.token = state.token;
275
299
  const apiCtor = this.useGraphql ? _GraphQLAPI.default : _API.default;
276
300
  this.api = new apiCtor({
277
301
  token: this.token,
302
+ tokenKeyword: this.tokenKeyword,
278
303
  branch: this.branch,
279
304
  repo: this.repo,
280
305
  originRepo: this.originRepo,
@@ -282,7 +307,9 @@ class GitHub {
282
307
  squashMerges: this.squashMerges,
283
308
  cmsLabelPrefix: this.cmsLabelPrefix,
284
309
  useOpenAuthoring: this.useOpenAuthoring,
285
- initialWorkflowStatus: this.options.initialWorkflowStatus
310
+ initialWorkflowStatus: this.options.initialWorkflowStatus,
311
+ baseUrl: this.baseUrl,
312
+ getUser: this.currentUser
286
313
  });
287
314
  const user = await this.api.user();
288
315
  const isCollab = await this.api.hasWriteAccess().catch(error => {
@@ -299,7 +326,7 @@ class GitHub {
299
326
  });
300
327
 
301
328
  // Unauthorized user
302
- if (!isCollab) {
329
+ if (!isCollab && !this.bypassWriteAccessCheckForAppTokens) {
303
330
  throw new Error('Your GitHub user account does not have access to this repo.');
304
331
  }
305
332
 
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.1.0-beta.0",
4
+ "version": "3.1.0-beta.2",
5
5
  "license": "MIT",
6
6
  "repository": "https://github.com/decaporg/decap-cms/tree/master/packages/decap-cms-backend-github",
7
7
  "bugs": "https://github.com/decaporg/decap-cms/issues",
@@ -14,7 +14,7 @@
14
14
  ],
15
15
  "sideEffects": false,
16
16
  "scripts": {
17
- "develop": "yarn build:esm --watch",
17
+ "develop": "npm run build:esm -- --watch",
18
18
  "build": "cross-env NODE_ENV=production webpack",
19
19
  "build:esm": "cross-env NODE_ENV=esm babel src --out-dir dist/esm --ignore \"**/__tests__\" --root-mode upward --extensions \".js,.jsx,.ts,.tsx\"",
20
20
  "createFragmentTypes": "node scripts/createFragmentTypes.js"
@@ -40,5 +40,5 @@
40
40
  "prop-types": "^15.7.2",
41
41
  "react": "^18.2.0"
42
42
  },
43
- "gitHead": "26f650bbfd8b20611ad9bb8957ce2d889a1a9085"
43
+ "gitHead": "d7a43e3bb45259ab0789362ea804df31fec77e21"
44
44
  }
package/src/API.ts CHANGED
@@ -50,6 +50,7 @@ export const MOCK_PULL_REQUEST = -1;
50
50
  export interface Config {
51
51
  apiRoot?: string;
52
52
  token?: string;
53
+ tokenKeyword?: string;
53
54
  branch?: string;
54
55
  useOpenAuthoring?: boolean;
55
56
  repo?: string;
@@ -57,6 +58,8 @@ export interface Config {
57
58
  squashMerges: boolean;
58
59
  initialWorkflowStatus: string;
59
60
  cmsLabelPrefix: string;
61
+ baseUrl?: string;
62
+ getUser: ({ token }: { token: string }) => Promise<GitHubUser>;
60
63
  }
61
64
 
62
65
  interface TreeFile {
@@ -173,6 +176,7 @@ let migrationNotified = false;
173
176
  export default class API {
174
177
  apiRoot: string;
175
178
  token: string;
179
+ tokenKeyword: string;
176
180
  branch: string;
177
181
  useOpenAuthoring?: boolean;
178
182
  repo: string;
@@ -186,7 +190,8 @@ export default class API {
186
190
  mergeMethod: string;
187
191
  initialWorkflowStatus: string;
188
192
  cmsLabelPrefix: string;
189
-
193
+ baseUrl?: string;
194
+ getUser: ({ token }: { token: string }) => Promise<GitHubUser>;
190
195
  _userPromise?: Promise<GitHubUser>;
191
196
  _metadataSemaphore?: Semaphore;
192
197
 
@@ -195,6 +200,7 @@ export default class API {
195
200
  constructor(config: Config) {
196
201
  this.apiRoot = config.apiRoot || 'https://api.github.com';
197
202
  this.token = config.token || '';
203
+ this.tokenKeyword = config.tokenKeyword || 'token';
198
204
  this.branch = config.branch || 'master';
199
205
  this.useOpenAuthoring = config.useOpenAuthoring;
200
206
  this.repo = config.repo || '';
@@ -213,21 +219,19 @@ export default class API {
213
219
  this.mergeMethod = config.squashMerges ? 'squash' : 'merge';
214
220
  this.cmsLabelPrefix = config.cmsLabelPrefix;
215
221
  this.initialWorkflowStatus = config.initialWorkflowStatus;
222
+ this.baseUrl = config.baseUrl;
223
+ this.getUser = config.getUser;
216
224
  }
217
225
 
218
226
  static DEFAULT_COMMIT_MESSAGE = 'Automatically generated by Decap CMS';
219
227
 
220
228
  user(): Promise<{ name: string; login: string }> {
221
229
  if (!this._userPromise) {
222
- this._userPromise = this.getUser();
230
+ this._userPromise = this.getUser({ token: this.token });
223
231
  }
224
232
  return this._userPromise;
225
233
  }
226
234
 
227
- getUser() {
228
- return this.request('/user') as Promise<GitHubUser>;
229
- }
230
-
231
235
  async hasWriteAccess() {
232
236
  try {
233
237
  const result: Octokit.ReposGetResponse = await this.request(this.repoURL);
@@ -251,7 +255,7 @@ export default class API {
251
255
  };
252
256
 
253
257
  if (this.token) {
254
- baseHeader.Authorization = `token ${this.token}`;
258
+ baseHeader.Authorization = `${this.tokenKeyword} ${this.token}`;
255
259
  return Promise.resolve(baseHeader);
256
260
  }
257
261
 
@@ -576,7 +580,7 @@ export default class API {
576
580
  }
577
581
 
578
582
  try {
579
- const user: GitHubUser = await this.request(`/users/${pullRequest.user.login}`);
583
+ const user = await this.user();
580
584
  return user.name || user.login;
581
585
  } catch {
582
586
  return;
package/src/GraphQLAPI.ts CHANGED
@@ -108,7 +108,7 @@ export default class GraphQLAPI extends API {
108
108
  headers: {
109
109
  'Content-Type': 'application/json; charset=utf-8',
110
110
  ...headers,
111
- authorization: this.token ? `token ${this.token}` : '',
111
+ authorization: this.token ? `${this.tokenKeyword} ${this.token}` : '',
112
112
  },
113
113
  };
114
114
  });
@@ -42,7 +42,7 @@ import type {
42
42
  } from 'decap-cms-lib-util';
43
43
  import type { Semaphore } from 'semaphore';
44
44
 
45
- type GitHubUser = Octokit.UsersGetAuthenticatedResponse;
45
+ export type GitHubUser = Octokit.UsersGetAuthenticatedResponse;
46
46
 
47
47
  const MAX_CONCURRENT_DOWNLOADS = 10;
48
48
 
@@ -78,9 +78,12 @@ export default class GitHub implements Implementation {
78
78
  mediaFolder: string;
79
79
  previewContext: string;
80
80
  token: string | null;
81
+ tokenKeyword: string;
81
82
  squashMerges: boolean;
82
83
  cmsLabelPrefix: string;
83
84
  useGraphql: boolean;
85
+ baseUrl?: string;
86
+ bypassWriteAccessCheckForAppTokens = false;
84
87
  _currentUserPromise?: Promise<GitHubUser>;
85
88
  _userIsOriginMaintainerPromises?: {
86
89
  [key: string]: Promise<boolean>;
@@ -119,6 +122,8 @@ export default class GitHub implements Implementation {
119
122
  this.branch = config.backend.branch?.trim() || 'master';
120
123
  this.apiRoot = config.backend.api_root || 'https://api.github.com';
121
124
  this.token = '';
125
+ this.tokenKeyword = 'token';
126
+ this.baseUrl = config.backend.base_url;
122
127
  this.squashMerges = config.backend.squash_merges || false;
123
128
  this.cmsLabelPrefix = config.backend.cms_label_prefix || '';
124
129
  this.useGraphql = config.backend.use_graphql || false;
@@ -153,7 +158,7 @@ export default class GitHub implements Implementation {
153
158
  if (api) {
154
159
  auth =
155
160
  (await this.api
156
- ?.getUser()
161
+ ?.getUser({ token: this.token ?? '' })
157
162
  .then(user => !!user)
158
163
  .catch(e => {
159
164
  console.warn('Failed getting GitHub user', e);
@@ -185,7 +190,7 @@ export default class GitHub implements Implementation {
185
190
  let repoExists = false;
186
191
  while (!repoExists) {
187
192
  repoExists = await fetch(`${this.apiRoot}/repos/${repo}`, {
188
- headers: { Authorization: `token ${token}` },
193
+ headers: { Authorization: `${this.tokenKeyword} ${token}` },
189
194
  })
190
195
  .then(() => true)
191
196
  .catch(err => {
@@ -208,7 +213,7 @@ export default class GitHub implements Implementation {
208
213
  if (!this._currentUserPromise) {
209
214
  this._currentUserPromise = fetch(`${this.apiRoot}/user`, {
210
215
  headers: {
211
- Authorization: `token ${token}`,
216
+ Authorization: `${this.tokenKeyword} ${token}`,
212
217
  },
213
218
  }).then(res => res.json());
214
219
  }
@@ -229,7 +234,7 @@ export default class GitHub implements Implementation {
229
234
  `${this.apiRoot}/repos/${this.originRepo}/collaborators/${username}/permission`,
230
235
  {
231
236
  headers: {
232
- Authorization: `token ${token}`,
237
+ Authorization: `${this.tokenKeyword} ${token}`,
233
238
  },
234
239
  },
235
240
  )
@@ -246,7 +251,7 @@ export default class GitHub implements Implementation {
246
251
  const repo = await fetch(`${this.apiRoot}/repos/${currentUser.login}/${repoName}`, {
247
252
  method: 'GET',
248
253
  headers: {
249
- Authorization: `token ${token}`,
254
+ Authorization: `${this.tokenKeyword} ${token}`,
250
255
  },
251
256
  }).then(res => res.json());
252
257
 
@@ -283,19 +288,34 @@ export default class GitHub implements Implementation {
283
288
  return Promise.resolve();
284
289
  }
285
290
 
286
- if (!(await this.forkExists({ token }))) {
291
+ // If a fork exists merge it with upstream
292
+ // otherwise create a new fork.
293
+ const currentUser = await this.currentUser({ token });
294
+ const repoName = this.originRepo.split('/')[1];
295
+ this.repo = `${currentUser.login}/${repoName}`;
296
+ this.useOpenAuthoring = true;
297
+
298
+ if (await this.forkExists({ token })) {
299
+ return fetch(`${this.apiRoot}/repos/${this.repo}/merge-upstream`, {
300
+ method: 'POST',
301
+ headers: {
302
+ Authorization: `${this.tokenKeyword} ${token}`,
303
+ },
304
+ body: JSON.stringify({
305
+ branch: this.branch,
306
+ }),
307
+ });
308
+ } else {
287
309
  await getPermissionToFork();
288
- }
289
310
 
290
- const fork = await fetch(`${this.apiRoot}/repos/${this.originRepo}/forks`, {
291
- method: 'POST',
292
- headers: {
293
- Authorization: `token ${token}`,
294
- },
295
- }).then(res => res.json());
296
- this.useOpenAuthoring = true;
297
- this.repo = fork.full_name;
298
- return this.pollUntilForkExists({ repo: fork.full_name, token });
311
+ const fork = await fetch(`${this.apiRoot}/repos/${this.originRepo}/forks`, {
312
+ method: 'POST',
313
+ headers: {
314
+ Authorization: `${this.tokenKeyword} ${token}`,
315
+ },
316
+ }).then(res => res.json());
317
+ return this.pollUntilForkExists({ repo: fork.full_name, token });
318
+ }
299
319
  }
300
320
 
301
321
  async authenticate(state: Credentials) {
@@ -303,6 +323,7 @@ export default class GitHub implements Implementation {
303
323
  const apiCtor = this.useGraphql ? GraphQLAPI : API;
304
324
  this.api = new apiCtor({
305
325
  token: this.token,
326
+ tokenKeyword: this.tokenKeyword,
306
327
  branch: this.branch,
307
328
  repo: this.repo,
308
329
  originRepo: this.originRepo,
@@ -311,6 +332,8 @@ export default class GitHub implements Implementation {
311
332
  cmsLabelPrefix: this.cmsLabelPrefix,
312
333
  useOpenAuthoring: this.useOpenAuthoring,
313
334
  initialWorkflowStatus: this.options.initialWorkflowStatus,
335
+ baseUrl: this.baseUrl,
336
+ getUser: this.currentUser,
314
337
  });
315
338
  const user = await this.api!.user();
316
339
  const isCollab = await this.api!.hasWriteAccess().catch(error => {
@@ -327,7 +350,7 @@ export default class GitHub implements Implementation {
327
350
  });
328
351
 
329
352
  // Unauthorized user
330
- if (!isCollab) {
353
+ if (!isCollab && !this.bypassWriteAccessCheckForAppTokens) {
331
354
  throw new Error('Your GitHub user account does not have access to this repo.');
332
355
  }
333
356