decap-cms-backend-github 2.15.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/CHANGELOG.md +817 -0
- package/LICENSE +22 -0
- package/README.md +17 -0
- package/dist/decap-cms-backend-github.js +312 -0
- package/dist/decap-cms-backend-github.js.LICENSE.txt +23 -0
- package/dist/decap-cms-backend-github.js.map +1 -0
- package/dist/esm/API.js +1217 -0
- package/dist/esm/AuthenticationPage.js +192 -0
- package/dist/esm/GraphQLAPI.js +807 -0
- package/dist/esm/fragmentTypes.js +947 -0
- package/dist/esm/fragments.js +98 -0
- package/dist/esm/implementation.js +585 -0
- package/dist/esm/index.js +34 -0
- package/dist/esm/mutations.js +117 -0
- package/dist/esm/queries.js +212 -0
- package/dist/esm/types/semaphore.d.js +1 -0
- package/package.json +44 -0
- package/scripts/createFragmentTypes.js +48 -0
- package/src/API.ts +1468 -0
- package/src/AuthenticationPage.js +151 -0
- package/src/GraphQLAPI.ts +709 -0
- package/src/__tests__/API.spec.js +833 -0
- package/src/__tests__/GraphQLAPI.spec.js +69 -0
- package/src/__tests__/implementation.spec.js +361 -0
- package/src/fragmentTypes.js +1 -0
- package/src/fragments.ts +92 -0
- package/src/implementation.tsx +672 -0
- package/src/index.ts +10 -0
- package/src/mutations.ts +110 -0
- package/src/queries.ts +213 -0
- package/src/types/semaphore.d.ts +5 -0
- package/webpack.config.js +3 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.treeEntry = exports.repository = exports.pullRequest = exports.object = exports.fileEntry = exports.branch = exports.blobWithText = void 0;
|
|
7
|
+
var _graphqlTag = require("graphql-tag");
|
|
8
|
+
const repository = (0, _graphqlTag.gql)`
|
|
9
|
+
fragment RepositoryParts on Repository {
|
|
10
|
+
id
|
|
11
|
+
isFork
|
|
12
|
+
}
|
|
13
|
+
`;
|
|
14
|
+
exports.repository = repository;
|
|
15
|
+
const blobWithText = (0, _graphqlTag.gql)`
|
|
16
|
+
fragment BlobWithTextParts on Blob {
|
|
17
|
+
id
|
|
18
|
+
text
|
|
19
|
+
is_binary: isBinary
|
|
20
|
+
}
|
|
21
|
+
`;
|
|
22
|
+
exports.blobWithText = blobWithText;
|
|
23
|
+
const object = (0, _graphqlTag.gql)`
|
|
24
|
+
fragment ObjectParts on GitObject {
|
|
25
|
+
id
|
|
26
|
+
sha: oid
|
|
27
|
+
}
|
|
28
|
+
`;
|
|
29
|
+
exports.object = object;
|
|
30
|
+
const branch = (0, _graphqlTag.gql)`
|
|
31
|
+
fragment BranchParts on Ref {
|
|
32
|
+
commit: target {
|
|
33
|
+
...ObjectParts
|
|
34
|
+
}
|
|
35
|
+
id
|
|
36
|
+
name
|
|
37
|
+
prefix
|
|
38
|
+
repository {
|
|
39
|
+
...RepositoryParts
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
${object}
|
|
43
|
+
${repository}
|
|
44
|
+
`;
|
|
45
|
+
exports.branch = branch;
|
|
46
|
+
const pullRequest = (0, _graphqlTag.gql)`
|
|
47
|
+
fragment PullRequestParts on PullRequest {
|
|
48
|
+
id
|
|
49
|
+
baseRefName
|
|
50
|
+
baseRefOid
|
|
51
|
+
body
|
|
52
|
+
headRefName
|
|
53
|
+
headRefOid
|
|
54
|
+
number
|
|
55
|
+
state
|
|
56
|
+
title
|
|
57
|
+
merged_at: mergedAt
|
|
58
|
+
updated_at: updatedAt
|
|
59
|
+
user: author {
|
|
60
|
+
login
|
|
61
|
+
... on User {
|
|
62
|
+
name
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
repository {
|
|
66
|
+
...RepositoryParts
|
|
67
|
+
}
|
|
68
|
+
labels(last: 100) {
|
|
69
|
+
nodes {
|
|
70
|
+
name
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
${repository}
|
|
75
|
+
`;
|
|
76
|
+
exports.pullRequest = pullRequest;
|
|
77
|
+
const treeEntry = (0, _graphqlTag.gql)`
|
|
78
|
+
fragment TreeEntryParts on TreeEntry {
|
|
79
|
+
path: name
|
|
80
|
+
sha: oid
|
|
81
|
+
type
|
|
82
|
+
mode
|
|
83
|
+
}
|
|
84
|
+
`;
|
|
85
|
+
exports.treeEntry = treeEntry;
|
|
86
|
+
const fileEntry = (0, _graphqlTag.gql)`
|
|
87
|
+
fragment FileEntryParts on TreeEntry {
|
|
88
|
+
name
|
|
89
|
+
sha: oid
|
|
90
|
+
type
|
|
91
|
+
blob: object {
|
|
92
|
+
... on Blob {
|
|
93
|
+
size: byteSize
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
`;
|
|
98
|
+
exports.fileEntry = fileEntry;
|
|
@@ -0,0 +1,585 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var React = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _semaphore = _interopRequireDefault(require("semaphore"));
|
|
9
|
+
var _trimStart = _interopRequireDefault(require("lodash/trimStart"));
|
|
10
|
+
var _commonTags = require("common-tags");
|
|
11
|
+
var _decapCmsLibUtil = require("decap-cms-lib-util");
|
|
12
|
+
var _AuthenticationPage = _interopRequireDefault(require("./AuthenticationPage"));
|
|
13
|
+
var _API = _interopRequireWildcard(require("./API"));
|
|
14
|
+
var _GraphQLAPI = _interopRequireDefault(require("./GraphQLAPI"));
|
|
15
|
+
var _core = require("@emotion/core");
|
|
16
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
17
|
+
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
18
|
+
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
19
|
+
function _extends() { _extends = Object.assign ? Object.assign.bind() : 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; }; return _extends.apply(this, arguments); }
|
|
20
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
21
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
22
|
+
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
23
|
+
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
|
|
24
|
+
function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
|
|
25
|
+
const MAX_CONCURRENT_DOWNLOADS = 10;
|
|
26
|
+
const {
|
|
27
|
+
fetchWithTimeout: fetch
|
|
28
|
+
} = _decapCmsLibUtil.unsentRequest;
|
|
29
|
+
const STATUS_PAGE = 'https://www.githubstatus.com';
|
|
30
|
+
const GITHUB_STATUS_ENDPOINT = `${STATUS_PAGE}/api/v2/components.json`;
|
|
31
|
+
const GITHUB_OPERATIONAL_UNITS = ['API Requests', 'Issues, Pull Requests, Projects'];
|
|
32
|
+
class GitHub {
|
|
33
|
+
constructor(config, options = {}) {
|
|
34
|
+
var _config$backend$branc;
|
|
35
|
+
_defineProperty(this, "lock", void 0);
|
|
36
|
+
_defineProperty(this, "api", void 0);
|
|
37
|
+
_defineProperty(this, "options", void 0);
|
|
38
|
+
_defineProperty(this, "originRepo", void 0);
|
|
39
|
+
_defineProperty(this, "repo", void 0);
|
|
40
|
+
_defineProperty(this, "openAuthoringEnabled", void 0);
|
|
41
|
+
_defineProperty(this, "useOpenAuthoring", void 0);
|
|
42
|
+
_defineProperty(this, "alwaysForkEnabled", void 0);
|
|
43
|
+
_defineProperty(this, "branch", void 0);
|
|
44
|
+
_defineProperty(this, "apiRoot", void 0);
|
|
45
|
+
_defineProperty(this, "mediaFolder", void 0);
|
|
46
|
+
_defineProperty(this, "previewContext", void 0);
|
|
47
|
+
_defineProperty(this, "token", void 0);
|
|
48
|
+
_defineProperty(this, "squashMerges", void 0);
|
|
49
|
+
_defineProperty(this, "cmsLabelPrefix", void 0);
|
|
50
|
+
_defineProperty(this, "useGraphql", void 0);
|
|
51
|
+
_defineProperty(this, "_currentUserPromise", void 0);
|
|
52
|
+
_defineProperty(this, "_userIsOriginMaintainerPromises", void 0);
|
|
53
|
+
_defineProperty(this, "_mediaDisplayURLSem", void 0);
|
|
54
|
+
_defineProperty(this, "getCursorAndFiles", (files, page) => {
|
|
55
|
+
const pageSize = 20;
|
|
56
|
+
const count = files.length;
|
|
57
|
+
const pageCount = Math.ceil(files.length / pageSize);
|
|
58
|
+
const actions = [];
|
|
59
|
+
if (page > 1) {
|
|
60
|
+
actions.push('prev');
|
|
61
|
+
actions.push('first');
|
|
62
|
+
}
|
|
63
|
+
if (page < pageCount) {
|
|
64
|
+
actions.push('next');
|
|
65
|
+
actions.push('last');
|
|
66
|
+
}
|
|
67
|
+
const cursor = _decapCmsLibUtil.Cursor.create({
|
|
68
|
+
actions,
|
|
69
|
+
meta: {
|
|
70
|
+
page,
|
|
71
|
+
count,
|
|
72
|
+
pageSize,
|
|
73
|
+
pageCount
|
|
74
|
+
},
|
|
75
|
+
data: {
|
|
76
|
+
files
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
const pageFiles = files.slice((page - 1) * pageSize, page * pageSize);
|
|
80
|
+
return {
|
|
81
|
+
cursor,
|
|
82
|
+
files: pageFiles
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
this.options = _objectSpread({
|
|
86
|
+
proxied: false,
|
|
87
|
+
API: null,
|
|
88
|
+
initialWorkflowStatus: ''
|
|
89
|
+
}, options);
|
|
90
|
+
if (!this.options.proxied && (config.backend.repo === null || config.backend.repo === undefined)) {
|
|
91
|
+
throw new Error('The GitHub backend needs a "repo" in the backend configuration.');
|
|
92
|
+
}
|
|
93
|
+
this.api = this.options.API || null;
|
|
94
|
+
this.openAuthoringEnabled = config.backend.open_authoring || false;
|
|
95
|
+
if (this.openAuthoringEnabled) {
|
|
96
|
+
if (!this.options.useWorkflow) {
|
|
97
|
+
throw new Error('backend.open_authoring is true but publish_mode is not set to editorial_workflow.');
|
|
98
|
+
}
|
|
99
|
+
this.originRepo = config.backend.repo || '';
|
|
100
|
+
} else {
|
|
101
|
+
this.repo = this.originRepo = config.backend.repo || '';
|
|
102
|
+
}
|
|
103
|
+
this.alwaysForkEnabled = config.backend.always_fork || false;
|
|
104
|
+
this.branch = ((_config$backend$branc = config.backend.branch) === null || _config$backend$branc === void 0 ? void 0 : _config$backend$branc.trim()) || 'master';
|
|
105
|
+
this.apiRoot = config.backend.api_root || 'https://api.github.com';
|
|
106
|
+
this.token = '';
|
|
107
|
+
this.squashMerges = config.backend.squash_merges || false;
|
|
108
|
+
this.cmsLabelPrefix = config.backend.cms_label_prefix || '';
|
|
109
|
+
this.useGraphql = config.backend.use_graphql || false;
|
|
110
|
+
this.mediaFolder = config.media_folder;
|
|
111
|
+
this.previewContext = config.backend.preview_context || '';
|
|
112
|
+
this.lock = (0, _decapCmsLibUtil.asyncLock)();
|
|
113
|
+
}
|
|
114
|
+
isGitBackend() {
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
async status() {
|
|
118
|
+
const api = await fetch(GITHUB_STATUS_ENDPOINT).then(res => res.json()).then(res => {
|
|
119
|
+
return res['components'].filter(statusComponent => GITHUB_OPERATIONAL_UNITS.includes(statusComponent.name)).every(statusComponent => statusComponent.status === 'operational');
|
|
120
|
+
}).catch(e => {
|
|
121
|
+
console.warn('Failed getting GitHub status', e);
|
|
122
|
+
return true;
|
|
123
|
+
});
|
|
124
|
+
let auth = false;
|
|
125
|
+
// no need to check auth if api is down
|
|
126
|
+
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 => {
|
|
129
|
+
console.warn('Failed getting GitHub user', e);
|
|
130
|
+
return false;
|
|
131
|
+
}))) || false;
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
auth: {
|
|
135
|
+
status: auth
|
|
136
|
+
},
|
|
137
|
+
api: {
|
|
138
|
+
status: api,
|
|
139
|
+
statusPage: STATUS_PAGE
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
authComponent() {
|
|
144
|
+
const wrappedAuthenticationPage = props => (0, _core.jsx)(_AuthenticationPage.default, _extends({}, props, {
|
|
145
|
+
backend: this
|
|
146
|
+
}));
|
|
147
|
+
wrappedAuthenticationPage.displayName = 'AuthenticationPage';
|
|
148
|
+
return wrappedAuthenticationPage;
|
|
149
|
+
}
|
|
150
|
+
restoreUser(user) {
|
|
151
|
+
return this.openAuthoringEnabled ? this.authenticateWithFork({
|
|
152
|
+
userData: user,
|
|
153
|
+
getPermissionToFork: () => true
|
|
154
|
+
}).then(() => this.authenticate(user)) : this.authenticate(user);
|
|
155
|
+
}
|
|
156
|
+
async pollUntilForkExists({
|
|
157
|
+
repo,
|
|
158
|
+
token
|
|
159
|
+
}) {
|
|
160
|
+
const pollDelay = 250; // milliseconds
|
|
161
|
+
let repoExists = false;
|
|
162
|
+
while (!repoExists) {
|
|
163
|
+
repoExists = await fetch(`${this.apiRoot}/repos/${repo}`, {
|
|
164
|
+
headers: {
|
|
165
|
+
Authorization: `token ${token}`
|
|
166
|
+
}
|
|
167
|
+
}).then(() => true).catch(err => {
|
|
168
|
+
if (err && err.status === 404) {
|
|
169
|
+
console.log('This 404 was expected and handled appropriately.');
|
|
170
|
+
return false;
|
|
171
|
+
} else {
|
|
172
|
+
return Promise.reject(err);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
// wait between polls
|
|
176
|
+
if (!repoExists) {
|
|
177
|
+
await new Promise(resolve => setTimeout(resolve, pollDelay));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return Promise.resolve();
|
|
181
|
+
}
|
|
182
|
+
async currentUser({
|
|
183
|
+
token
|
|
184
|
+
}) {
|
|
185
|
+
if (!this._currentUserPromise) {
|
|
186
|
+
this._currentUserPromise = fetch(`${this.apiRoot}/user`, {
|
|
187
|
+
headers: {
|
|
188
|
+
Authorization: `token ${token}`
|
|
189
|
+
}
|
|
190
|
+
}).then(res => res.json());
|
|
191
|
+
}
|
|
192
|
+
return this._currentUserPromise;
|
|
193
|
+
}
|
|
194
|
+
async userIsOriginMaintainer({
|
|
195
|
+
username: usernameArg,
|
|
196
|
+
token
|
|
197
|
+
}) {
|
|
198
|
+
const username = usernameArg || (await this.currentUser({
|
|
199
|
+
token
|
|
200
|
+
})).login;
|
|
201
|
+
this._userIsOriginMaintainerPromises = this._userIsOriginMaintainerPromises || {};
|
|
202
|
+
if (!this._userIsOriginMaintainerPromises[username]) {
|
|
203
|
+
this._userIsOriginMaintainerPromises[username] = fetch(`${this.apiRoot}/repos/${this.originRepo}/collaborators/${username}/permission`, {
|
|
204
|
+
headers: {
|
|
205
|
+
Authorization: `token ${token}`
|
|
206
|
+
}
|
|
207
|
+
}).then(res => res.json()).then(({
|
|
208
|
+
permission
|
|
209
|
+
}) => permission === 'admin' || permission === 'write');
|
|
210
|
+
}
|
|
211
|
+
return this._userIsOriginMaintainerPromises[username];
|
|
212
|
+
}
|
|
213
|
+
async forkExists({
|
|
214
|
+
token
|
|
215
|
+
}) {
|
|
216
|
+
try {
|
|
217
|
+
const currentUser = await this.currentUser({
|
|
218
|
+
token
|
|
219
|
+
});
|
|
220
|
+
const repoName = this.originRepo.split('/')[1];
|
|
221
|
+
const repo = await fetch(`${this.apiRoot}/repos/${currentUser.login}/${repoName}`, {
|
|
222
|
+
method: 'GET',
|
|
223
|
+
headers: {
|
|
224
|
+
Authorization: `token ${token}`
|
|
225
|
+
}
|
|
226
|
+
}).then(res => res.json());
|
|
227
|
+
|
|
228
|
+
// https://developer.github.com/v3/repos/#get
|
|
229
|
+
// The parent and source objects are present when the repository is a fork.
|
|
230
|
+
// parent is the repository this repository was forked from, source is the ultimate source for the network.
|
|
231
|
+
const forkExists = repo.fork === true && repo.parent && repo.parent.full_name.toLowerCase() === this.originRepo.toLowerCase();
|
|
232
|
+
return forkExists;
|
|
233
|
+
} catch {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
async authenticateWithFork({
|
|
238
|
+
userData,
|
|
239
|
+
getPermissionToFork
|
|
240
|
+
}) {
|
|
241
|
+
if (!this.openAuthoringEnabled) {
|
|
242
|
+
throw new Error('Cannot authenticate with fork; Open Authoring is turned off.');
|
|
243
|
+
}
|
|
244
|
+
const token = userData.token;
|
|
245
|
+
|
|
246
|
+
// Origin maintainers should be able to use the CMS normally. If alwaysFork
|
|
247
|
+
// is enabled we always fork (and avoid the origin maintainer check)
|
|
248
|
+
if (!this.alwaysForkEnabled && (await this.userIsOriginMaintainer({
|
|
249
|
+
token
|
|
250
|
+
}))) {
|
|
251
|
+
this.repo = this.originRepo;
|
|
252
|
+
this.useOpenAuthoring = false;
|
|
253
|
+
return Promise.resolve();
|
|
254
|
+
}
|
|
255
|
+
if (!(await this.forkExists({
|
|
256
|
+
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());
|
|
266
|
+
this.useOpenAuthoring = true;
|
|
267
|
+
this.repo = fork.full_name;
|
|
268
|
+
return this.pollUntilForkExists({
|
|
269
|
+
repo: fork.full_name,
|
|
270
|
+
token
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
async authenticate(state) {
|
|
274
|
+
this.token = state.token;
|
|
275
|
+
const apiCtor = this.useGraphql ? _GraphQLAPI.default : _API.default;
|
|
276
|
+
this.api = new apiCtor({
|
|
277
|
+
token: this.token,
|
|
278
|
+
branch: this.branch,
|
|
279
|
+
repo: this.repo,
|
|
280
|
+
originRepo: this.originRepo,
|
|
281
|
+
apiRoot: this.apiRoot,
|
|
282
|
+
squashMerges: this.squashMerges,
|
|
283
|
+
cmsLabelPrefix: this.cmsLabelPrefix,
|
|
284
|
+
useOpenAuthoring: this.useOpenAuthoring,
|
|
285
|
+
initialWorkflowStatus: this.options.initialWorkflowStatus
|
|
286
|
+
});
|
|
287
|
+
const user = await this.api.user();
|
|
288
|
+
const isCollab = await this.api.hasWriteAccess().catch(error => {
|
|
289
|
+
error.message = (0, _commonTags.stripIndent)`
|
|
290
|
+
Repo "${this.repo}" not found.
|
|
291
|
+
|
|
292
|
+
Please ensure the repo information is spelled correctly.
|
|
293
|
+
|
|
294
|
+
If the repo is private, make sure you're logged into a GitHub account with access.
|
|
295
|
+
|
|
296
|
+
If your repo is under an organization, ensure the organization has granted access to Decap CMS.
|
|
297
|
+
`;
|
|
298
|
+
throw error;
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
// Unauthorized user
|
|
302
|
+
if (!isCollab) {
|
|
303
|
+
throw new Error('Your GitHub user account does not have access to this repo.');
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Authorized user
|
|
307
|
+
return _objectSpread(_objectSpread({}, user), {}, {
|
|
308
|
+
token: state.token,
|
|
309
|
+
useOpenAuthoring: this.useOpenAuthoring
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
logout() {
|
|
313
|
+
this.token = null;
|
|
314
|
+
if (this.api && this.api.reset && typeof this.api.reset === 'function') {
|
|
315
|
+
return this.api.reset();
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
getToken() {
|
|
319
|
+
return Promise.resolve(this.token);
|
|
320
|
+
}
|
|
321
|
+
async entriesByFolder(folder, extension, depth) {
|
|
322
|
+
const repoURL = this.api.originRepoURL;
|
|
323
|
+
let cursor;
|
|
324
|
+
const listFiles = () => this.api.listFiles(folder, {
|
|
325
|
+
repoURL,
|
|
326
|
+
depth
|
|
327
|
+
}).then(files => {
|
|
328
|
+
const filtered = files.filter(file => (0, _decapCmsLibUtil.filterByExtension)(file, extension));
|
|
329
|
+
const result = this.getCursorAndFiles(filtered, 1);
|
|
330
|
+
cursor = result.cursor;
|
|
331
|
+
return result.files;
|
|
332
|
+
});
|
|
333
|
+
const readFile = (path, id) => this.api.readFile(path, id, {
|
|
334
|
+
repoURL
|
|
335
|
+
});
|
|
336
|
+
const files = await (0, _decapCmsLibUtil.entriesByFolder)(listFiles, readFile, this.api.readFileMetadata.bind(this.api), _API.API_NAME);
|
|
337
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
338
|
+
// @ts-ignore
|
|
339
|
+
files[_decapCmsLibUtil.CURSOR_COMPATIBILITY_SYMBOL] = cursor;
|
|
340
|
+
return files;
|
|
341
|
+
}
|
|
342
|
+
async allEntriesByFolder(folder, extension, depth) {
|
|
343
|
+
const repoURL = this.api.originRepoURL;
|
|
344
|
+
const listFiles = () => this.api.listFiles(folder, {
|
|
345
|
+
repoURL,
|
|
346
|
+
depth
|
|
347
|
+
}).then(files => files.filter(file => (0, _decapCmsLibUtil.filterByExtension)(file, extension)));
|
|
348
|
+
const readFile = (path, id) => {
|
|
349
|
+
return this.api.readFile(path, id, {
|
|
350
|
+
repoURL
|
|
351
|
+
});
|
|
352
|
+
};
|
|
353
|
+
const files = await (0, _decapCmsLibUtil.entriesByFolder)(listFiles, readFile, this.api.readFileMetadata.bind(this.api), _API.API_NAME);
|
|
354
|
+
return files;
|
|
355
|
+
}
|
|
356
|
+
entriesByFiles(files) {
|
|
357
|
+
const repoURL = this.useOpenAuthoring ? this.api.originRepoURL : this.api.repoURL;
|
|
358
|
+
const readFile = (path, id) => this.api.readFile(path, id, {
|
|
359
|
+
repoURL
|
|
360
|
+
}).catch(() => '');
|
|
361
|
+
return (0, _decapCmsLibUtil.entriesByFiles)(files, readFile, this.api.readFileMetadata.bind(this.api), _API.API_NAME);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Fetches a single entry.
|
|
365
|
+
getEntry(path) {
|
|
366
|
+
const repoURL = this.api.originRepoURL;
|
|
367
|
+
return this.api.readFile(path, null, {
|
|
368
|
+
repoURL
|
|
369
|
+
}).then(data => ({
|
|
370
|
+
file: {
|
|
371
|
+
path,
|
|
372
|
+
id: null
|
|
373
|
+
},
|
|
374
|
+
data: data
|
|
375
|
+
})).catch(() => ({
|
|
376
|
+
file: {
|
|
377
|
+
path,
|
|
378
|
+
id: null
|
|
379
|
+
},
|
|
380
|
+
data: ''
|
|
381
|
+
}));
|
|
382
|
+
}
|
|
383
|
+
getMedia(mediaFolder = this.mediaFolder) {
|
|
384
|
+
return this.api.listFiles(mediaFolder).then(files => files.map(({
|
|
385
|
+
id,
|
|
386
|
+
name,
|
|
387
|
+
size,
|
|
388
|
+
path
|
|
389
|
+
}) => {
|
|
390
|
+
// load media using getMediaDisplayURL to avoid token expiration with GitHub raw content urls
|
|
391
|
+
// for private repositories
|
|
392
|
+
return {
|
|
393
|
+
id,
|
|
394
|
+
name,
|
|
395
|
+
size,
|
|
396
|
+
displayURL: {
|
|
397
|
+
id,
|
|
398
|
+
path
|
|
399
|
+
},
|
|
400
|
+
path
|
|
401
|
+
};
|
|
402
|
+
}));
|
|
403
|
+
}
|
|
404
|
+
async getMediaFile(path) {
|
|
405
|
+
const blob = await (0, _decapCmsLibUtil.getMediaAsBlob)(path, null, this.api.readFile.bind(this.api));
|
|
406
|
+
const name = (0, _decapCmsLibUtil.basename)(path);
|
|
407
|
+
const fileObj = (0, _decapCmsLibUtil.blobToFileObj)(name, blob);
|
|
408
|
+
const url = URL.createObjectURL(fileObj);
|
|
409
|
+
const id = await (0, _decapCmsLibUtil.getBlobSHA)(blob);
|
|
410
|
+
return {
|
|
411
|
+
id,
|
|
412
|
+
displayURL: url,
|
|
413
|
+
path,
|
|
414
|
+
name,
|
|
415
|
+
size: fileObj.size,
|
|
416
|
+
file: fileObj,
|
|
417
|
+
url
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
getMediaDisplayURL(displayURL) {
|
|
421
|
+
this._mediaDisplayURLSem = this._mediaDisplayURLSem || (0, _semaphore.default)(MAX_CONCURRENT_DOWNLOADS);
|
|
422
|
+
return (0, _decapCmsLibUtil.getMediaDisplayURL)(displayURL, this.api.readFile.bind(this.api), this._mediaDisplayURLSem);
|
|
423
|
+
}
|
|
424
|
+
persistEntry(entry, options) {
|
|
425
|
+
// persistEntry is a transactional operation
|
|
426
|
+
return (0, _decapCmsLibUtil.runWithLock)(this.lock, () => this.api.persistFiles(entry.dataFiles, entry.assets, options), 'Failed to acquire persist entry lock');
|
|
427
|
+
}
|
|
428
|
+
async persistMedia(mediaFile, options) {
|
|
429
|
+
try {
|
|
430
|
+
await this.api.persistFiles([], [mediaFile], options);
|
|
431
|
+
const {
|
|
432
|
+
sha,
|
|
433
|
+
path,
|
|
434
|
+
fileObj
|
|
435
|
+
} = mediaFile;
|
|
436
|
+
const displayURL = fileObj ? URL.createObjectURL(fileObj) : '';
|
|
437
|
+
return {
|
|
438
|
+
id: sha,
|
|
439
|
+
name: fileObj.name,
|
|
440
|
+
size: fileObj.size,
|
|
441
|
+
displayURL,
|
|
442
|
+
path: (0, _trimStart.default)(path, '/')
|
|
443
|
+
};
|
|
444
|
+
} catch (error) {
|
|
445
|
+
console.error(error);
|
|
446
|
+
throw error;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
deleteFiles(paths, commitMessage) {
|
|
450
|
+
return this.api.deleteFiles(paths, commitMessage);
|
|
451
|
+
}
|
|
452
|
+
async traverseCursor(cursor, action) {
|
|
453
|
+
const meta = cursor.meta;
|
|
454
|
+
const files = cursor.data.get('files').toJS();
|
|
455
|
+
let result;
|
|
456
|
+
switch (action) {
|
|
457
|
+
case 'first':
|
|
458
|
+
{
|
|
459
|
+
result = this.getCursorAndFiles(files, 1);
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
462
|
+
case 'last':
|
|
463
|
+
{
|
|
464
|
+
result = this.getCursorAndFiles(files, meta.get('pageCount'));
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
case 'next':
|
|
468
|
+
{
|
|
469
|
+
result = this.getCursorAndFiles(files, meta.get('page') + 1);
|
|
470
|
+
break;
|
|
471
|
+
}
|
|
472
|
+
case 'prev':
|
|
473
|
+
{
|
|
474
|
+
result = this.getCursorAndFiles(files, meta.get('page') - 1);
|
|
475
|
+
break;
|
|
476
|
+
}
|
|
477
|
+
default:
|
|
478
|
+
{
|
|
479
|
+
result = this.getCursorAndFiles(files, 1);
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
const readFile = (path, id) => this.api.readFile(path, id, {
|
|
484
|
+
repoURL: this.api.originRepoURL
|
|
485
|
+
}).catch(() => '');
|
|
486
|
+
const entries = await (0, _decapCmsLibUtil.entriesByFiles)(result.files, readFile, this.api.readFileMetadata.bind(this.api), _API.API_NAME);
|
|
487
|
+
return {
|
|
488
|
+
entries,
|
|
489
|
+
cursor: result.cursor
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
async loadMediaFile(branch, file) {
|
|
493
|
+
const readFile = (path, id, {
|
|
494
|
+
parseText
|
|
495
|
+
}) => this.api.readFile(path, id, {
|
|
496
|
+
branch,
|
|
497
|
+
parseText
|
|
498
|
+
});
|
|
499
|
+
const blob = await (0, _decapCmsLibUtil.getMediaAsBlob)(file.path, file.id, readFile);
|
|
500
|
+
const name = (0, _decapCmsLibUtil.basename)(file.path);
|
|
501
|
+
const fileObj = (0, _decapCmsLibUtil.blobToFileObj)(name, blob);
|
|
502
|
+
return {
|
|
503
|
+
id: file.id,
|
|
504
|
+
displayURL: URL.createObjectURL(fileObj),
|
|
505
|
+
path: file.path,
|
|
506
|
+
name,
|
|
507
|
+
size: fileObj.size,
|
|
508
|
+
file: fileObj
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
async unpublishedEntries() {
|
|
512
|
+
const listEntriesKeys = () => this.api.listUnpublishedBranches().then(branches => branches.map(branch => (0, _decapCmsLibUtil.contentKeyFromBranch)(branch)));
|
|
513
|
+
const ids = await (0, _decapCmsLibUtil.unpublishedEntries)(listEntriesKeys);
|
|
514
|
+
return ids;
|
|
515
|
+
}
|
|
516
|
+
async unpublishedEntry({
|
|
517
|
+
id,
|
|
518
|
+
collection,
|
|
519
|
+
slug
|
|
520
|
+
}) {
|
|
521
|
+
if (id) {
|
|
522
|
+
const data = await this.api.retrieveUnpublishedEntryData(id);
|
|
523
|
+
return data;
|
|
524
|
+
} else if (collection && slug) {
|
|
525
|
+
const entryId = this.api.generateContentKey(collection, slug);
|
|
526
|
+
const data = await this.api.retrieveUnpublishedEntryData(entryId);
|
|
527
|
+
return data;
|
|
528
|
+
} else {
|
|
529
|
+
throw new Error('Missing unpublished entry id or collection and slug');
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
getBranch(collection, slug) {
|
|
533
|
+
const contentKey = this.api.generateContentKey(collection, slug);
|
|
534
|
+
const branch = (0, _decapCmsLibUtil.branchFromContentKey)(contentKey);
|
|
535
|
+
return branch;
|
|
536
|
+
}
|
|
537
|
+
async unpublishedEntryDataFile(collection, slug, path, id) {
|
|
538
|
+
const branch = this.getBranch(collection, slug);
|
|
539
|
+
const data = await this.api.readFile(path, id, {
|
|
540
|
+
branch
|
|
541
|
+
});
|
|
542
|
+
return data;
|
|
543
|
+
}
|
|
544
|
+
async unpublishedEntryMediaFile(collection, slug, path, id) {
|
|
545
|
+
const branch = this.getBranch(collection, slug);
|
|
546
|
+
const mediaFile = await this.loadMediaFile(branch, {
|
|
547
|
+
path,
|
|
548
|
+
id
|
|
549
|
+
});
|
|
550
|
+
return mediaFile;
|
|
551
|
+
}
|
|
552
|
+
async getDeployPreview(collection, slug) {
|
|
553
|
+
try {
|
|
554
|
+
const statuses = await this.api.getStatuses(collection, slug);
|
|
555
|
+
const deployStatus = (0, _decapCmsLibUtil.getPreviewStatus)(statuses, this.previewContext);
|
|
556
|
+
if (deployStatus) {
|
|
557
|
+
const {
|
|
558
|
+
target_url: url,
|
|
559
|
+
state
|
|
560
|
+
} = deployStatus;
|
|
561
|
+
return {
|
|
562
|
+
url,
|
|
563
|
+
status: state
|
|
564
|
+
};
|
|
565
|
+
} else {
|
|
566
|
+
return null;
|
|
567
|
+
}
|
|
568
|
+
} catch (e) {
|
|
569
|
+
return null;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
updateUnpublishedEntryStatus(collection, slug, newStatus) {
|
|
573
|
+
// updateUnpublishedEntryStatus is a transactional operation
|
|
574
|
+
return (0, _decapCmsLibUtil.runWithLock)(this.lock, () => this.api.updateUnpublishedEntryStatus(collection, slug, newStatus), 'Failed to acquire update entry status lock');
|
|
575
|
+
}
|
|
576
|
+
deleteUnpublishedEntry(collection, slug) {
|
|
577
|
+
// deleteUnpublishedEntry is a transactional operation
|
|
578
|
+
return (0, _decapCmsLibUtil.runWithLock)(this.lock, () => this.api.deleteUnpublishedEntry(collection, slug), 'Failed to acquire delete entry lock');
|
|
579
|
+
}
|
|
580
|
+
publishUnpublishedEntry(collection, slug) {
|
|
581
|
+
// publishUnpublishedEntry is a transactional operation
|
|
582
|
+
return (0, _decapCmsLibUtil.runWithLock)(this.lock, () => this.api.publishUnpublishedEntry(collection, slug), 'Failed to acquire publish entry lock');
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
exports.default = GitHub;
|