feat(core): align GitHub metadata handling with other backends (#3316)
* Revert "Revert "feat(core): Align GitHub metadata handling with other backends (#3292)"" This reverts commit 5bdd3df9ccbb5149c22d79987ebdcd6cab4b261f. * fix(backend-github): fix migration code * test(backend-github): fix test * test(e2e): shorten wait time * test(e2e): try and fix test on CI
This commit is contained in:
@ -23,6 +23,7 @@ import {
|
||||
PreviewState,
|
||||
FetchError,
|
||||
parseContentKey,
|
||||
branchFromContentKey,
|
||||
} from 'netlify-cms-lib-util';
|
||||
import { oneLine } from 'common-tags';
|
||||
import { parse } from 'what-the-diff';
|
||||
@ -449,8 +450,8 @@ export default class API {
|
||||
}
|
||||
|
||||
async editorialWorkflowGit(files: (Entry | AssetProxy)[], entry: Entry, options: PersistOptions) {
|
||||
const contentKey = this.generateContentKey(options.collectionName as string, entry.slug);
|
||||
const branch = this.branchFromContentKey(contentKey);
|
||||
const contentKey = generateContentKey(options.collectionName as string, entry.slug);
|
||||
const branch = branchFromContentKey(contentKey);
|
||||
const unpublished = options.unpublished || false;
|
||||
if (!unpublished) {
|
||||
const defaultBranchSha = await this.branchCommitSha(this.branch);
|
||||
@ -497,18 +498,6 @@ export default class API {
|
||||
);
|
||||
};
|
||||
|
||||
generateContentKey(collectionName: string, slug: string) {
|
||||
return generateContentKey(collectionName, slug);
|
||||
}
|
||||
|
||||
contentKeyFromBranch(branch: string) {
|
||||
return branch.substring(`${CMS_BRANCH_PREFIX}/`.length);
|
||||
}
|
||||
|
||||
branchFromContentKey(contentKey: string) {
|
||||
return `${CMS_BRANCH_PREFIX}/${contentKey}`;
|
||||
}
|
||||
|
||||
async isFileExists(path: string, branch: string) {
|
||||
const fileExists = await this.readFile(path, null, { branch })
|
||||
.then(() => true)
|
||||
@ -559,7 +548,7 @@ export default class API {
|
||||
|
||||
async retrieveMetadata(contentKey: string) {
|
||||
const { collection, slug } = parseContentKey(contentKey);
|
||||
const branch = this.branchFromContentKey(contentKey);
|
||||
const branch = branchFromContentKey(contentKey);
|
||||
const pullRequest = await this.getBranchPullRequest(branch);
|
||||
const diff = await this.getDifferences(branch);
|
||||
const { newPath: path, newFile } = diff.find(d => !d.binary) as {
|
||||
@ -609,8 +598,8 @@ export default class API {
|
||||
}
|
||||
|
||||
async updateUnpublishedEntryStatus(collection: string, slug: string, newStatus: string) {
|
||||
const contentKey = this.generateContentKey(collection, slug);
|
||||
const branch = this.branchFromContentKey(contentKey);
|
||||
const contentKey = generateContentKey(collection, slug);
|
||||
const branch = branchFromContentKey(contentKey);
|
||||
const pullRequest = await this.getBranchPullRequest(branch);
|
||||
|
||||
await this.addPullRequestComment(pullRequest, statusToLabel(newStatus));
|
||||
@ -632,8 +621,8 @@ export default class API {
|
||||
}
|
||||
|
||||
async publishUnpublishedEntry(collectionName: string, slug: string) {
|
||||
const contentKey = this.generateContentKey(collectionName, slug);
|
||||
const branch = this.branchFromContentKey(contentKey);
|
||||
const contentKey = generateContentKey(collectionName, slug);
|
||||
const branch = branchFromContentKey(contentKey);
|
||||
const pullRequest = await this.getBranchPullRequest(branch);
|
||||
|
||||
await this.mergePullRequest(pullRequest);
|
||||
@ -654,8 +643,8 @@ export default class API {
|
||||
}
|
||||
|
||||
async deleteUnpublishedEntry(collectionName: string, slug: string) {
|
||||
const contentKey = this.generateContentKey(collectionName, slug);
|
||||
const branch = this.branchFromContentKey(contentKey);
|
||||
const contentKey = generateContentKey(collectionName, slug);
|
||||
const branch = branchFromContentKey(contentKey);
|
||||
const pullRequest = await this.getBranchPullRequest(branch);
|
||||
|
||||
await this.declinePullRequest(pullRequest);
|
||||
@ -674,8 +663,8 @@ export default class API {
|
||||
}
|
||||
|
||||
async getStatuses(collectionName: string, slug: string) {
|
||||
const contentKey = this.generateContentKey(collectionName, slug);
|
||||
const branch = this.branchFromContentKey(contentKey);
|
||||
const contentKey = generateContentKey(collectionName, slug);
|
||||
const branch = branchFromContentKey(contentKey);
|
||||
const pullRequest = await this.getBranchPullRequest(branch);
|
||||
const statuses = await this.getPullRequestStatuses(pullRequest);
|
||||
|
||||
|
@ -34,6 +34,8 @@ import {
|
||||
getLargeMediaFilteredMediaFiles,
|
||||
FetchError,
|
||||
blobToFileObj,
|
||||
contentKeyFromBranch,
|
||||
generateContentKey,
|
||||
} from 'netlify-cms-lib-util';
|
||||
import NetlifyAuthenticator from 'netlify-cms-lib-auth';
|
||||
import AuthenticationPage from './AuthenticationPage';
|
||||
@ -445,7 +447,7 @@ export default class BitbucketBackend implements Implementation {
|
||||
async unpublishedEntries() {
|
||||
const listEntriesKeys = () =>
|
||||
this.api!.listUnpublishedBranches().then(branches =>
|
||||
branches.map(branch => this.api!.contentKeyFromBranch(branch)),
|
||||
branches.map(branch => contentKeyFromBranch(branch)),
|
||||
);
|
||||
|
||||
const readUnpublishedBranchFile = (contentKey: string) =>
|
||||
@ -462,7 +464,7 @@ export default class BitbucketBackend implements Implementation {
|
||||
this.loadEntryMediaFiles(branch, files),
|
||||
} = {},
|
||||
) {
|
||||
const contentKey = this.api!.generateContentKey(collection, slug);
|
||||
const contentKey = generateContentKey(collection, slug);
|
||||
const data = await this.api!.readUnpublishedBranchFile(contentKey);
|
||||
const mediaFiles = await loadEntryMediaFiles(
|
||||
data.metaData.branch,
|
||||
|
@ -71,6 +71,20 @@ export default class API extends GithubAPI {
|
||||
return Promise.resolve({ login: '', ...this.commitAuthor });
|
||||
}
|
||||
|
||||
async getHeadReference(head: string) {
|
||||
if (!this.repoOwner) {
|
||||
// get the repo owner from the branch url
|
||||
// this is required for returning the full head reference, e.g. owner:head
|
||||
// when filtering pull requests based on the head
|
||||
const branch = await this.getDefaultBranch();
|
||||
const self = branch._links.self;
|
||||
const regex = new RegExp('https?://.+?/repos/(.+?)/');
|
||||
const owner = self.match(regex);
|
||||
this.repoOwner = owner ? owner[1] : '';
|
||||
}
|
||||
return super.getHeadReference(head);
|
||||
}
|
||||
|
||||
commit(message: string, changeTree: { parentSha?: string; sha: string }) {
|
||||
const commitParams: {
|
||||
message: string;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -9,17 +9,19 @@ import { createHttpLink } from 'apollo-link-http';
|
||||
import { setContext } from 'apollo-link-context';
|
||||
import {
|
||||
APIError,
|
||||
EditorialWorkflowError,
|
||||
readFile,
|
||||
localForage,
|
||||
DEFAULT_PR_BODY,
|
||||
branchFromContentKey,
|
||||
CMS_BRANCH_PREFIX,
|
||||
} from 'netlify-cms-lib-util';
|
||||
import { trim } from 'lodash';
|
||||
import introspectionQueryResultData from './fragmentTypes';
|
||||
import API, { Config, BlobArgs, PR, API_NAME } from './API';
|
||||
import API, { Config, BlobArgs, API_NAME, PullRequestState, MOCK_PULL_REQUEST } from './API';
|
||||
import * as queries from './queries';
|
||||
import * as mutations from './mutations';
|
||||
import { GraphQLError } from 'graphql';
|
||||
import { Octokit } from '@octokit/rest';
|
||||
|
||||
const NO_CACHE = 'no-cache';
|
||||
const CACHE_FIRST = 'cache-first';
|
||||
@ -48,25 +50,37 @@ interface TreeFile {
|
||||
name: string;
|
||||
}
|
||||
|
||||
type GraphQLPullRequest = {
|
||||
id: string;
|
||||
baseRefName: string;
|
||||
baseRefOid: string;
|
||||
body: string;
|
||||
headRefName: string;
|
||||
headRefOid: string;
|
||||
number: number;
|
||||
state: string;
|
||||
title: string;
|
||||
mergedAt: string | null;
|
||||
labels: { nodes: { name: string }[] };
|
||||
};
|
||||
|
||||
const transformPullRequest = (pr: GraphQLPullRequest) => {
|
||||
return {
|
||||
...pr,
|
||||
labels: pr.labels.nodes,
|
||||
head: { ref: pr.headRefName, sha: pr.headRefOid },
|
||||
base: { ref: pr.baseRefName, sha: pr.baseRefOid },
|
||||
};
|
||||
};
|
||||
|
||||
type Error = GraphQLError & { type: string };
|
||||
|
||||
export default class GraphQLAPI extends API {
|
||||
repoOwner: string;
|
||||
repoName: string;
|
||||
originRepoOwner: string;
|
||||
originRepoName: string;
|
||||
client: ApolloClient<NormalizedCacheObject>;
|
||||
|
||||
constructor(config: Config) {
|
||||
super(config);
|
||||
|
||||
const [repoParts, originRepoParts] = [this.repo.split('/'), this.originRepo.split('/')];
|
||||
this.repoOwner = repoParts[0];
|
||||
this.repoName = repoParts[1];
|
||||
|
||||
this.originRepoOwner = originRepoParts[0];
|
||||
this.originRepoName = originRepoParts[1];
|
||||
|
||||
this.client = this.getApolloClient();
|
||||
}
|
||||
|
||||
@ -214,7 +228,64 @@ export default class GraphQLAPI extends API {
|
||||
}
|
||||
}
|
||||
|
||||
async getStatuses(sha: string) {
|
||||
async getPullRequests(
|
||||
head: string | undefined,
|
||||
state: PullRequestState,
|
||||
predicate: (pr: Octokit.PullsListResponseItem) => boolean,
|
||||
) {
|
||||
const { originRepoOwner: owner, originRepoName: name } = this;
|
||||
let states;
|
||||
if (state === PullRequestState.Open) {
|
||||
states = ['OPEN'];
|
||||
} else if (state === PullRequestState.Closed) {
|
||||
states = ['CLOSED', 'MERGED'];
|
||||
} else {
|
||||
states = ['OPEN', 'CLOSED', 'MERGED'];
|
||||
}
|
||||
const { data } = await this.query({
|
||||
query: queries.pullRequests,
|
||||
variables: {
|
||||
owner,
|
||||
name,
|
||||
...(head ? { head } : {}),
|
||||
states,
|
||||
},
|
||||
});
|
||||
const {
|
||||
pullRequests,
|
||||
}: {
|
||||
pullRequests: {
|
||||
nodes: GraphQLPullRequest[];
|
||||
};
|
||||
} = data.repository;
|
||||
|
||||
const mapped = pullRequests.nodes.map(transformPullRequest);
|
||||
|
||||
return ((mapped as unknown) as Octokit.PullsListResponseItem[]).filter(
|
||||
pr => pr.head.ref.startsWith(`${CMS_BRANCH_PREFIX}/`) && predicate(pr),
|
||||
);
|
||||
}
|
||||
|
||||
async getCmsBranches() {
|
||||
const { repoOwner: owner, repoName: name } = this;
|
||||
const { data } = await this.query({
|
||||
query: queries.cmsBranches,
|
||||
variables: {
|
||||
owner,
|
||||
name,
|
||||
},
|
||||
});
|
||||
|
||||
return data.repository.refs.nodes.map(({ name, prefix }: { name: string; prefix: string }) => ({
|
||||
ref: `${prefix}${name}`,
|
||||
}));
|
||||
}
|
||||
|
||||
async getStatuses(collectionName: string, slug: string) {
|
||||
const contentKey = this.generateContentKey(collectionName, slug);
|
||||
const branch = branchFromContentKey(contentKey);
|
||||
const pullRequest = await this.getBranchPullRequest(branch);
|
||||
const sha = pullRequest.head.sha;
|
||||
const { originRepoOwner: owner, originRepoName: name } = this;
|
||||
const { data } = await this.query({ query: queries.statues, variables: { owner, name, sha } });
|
||||
if (data.repository.object) {
|
||||
@ -265,76 +336,6 @@ export default class GraphQLAPI extends API {
|
||||
}
|
||||
}
|
||||
|
||||
async listUnpublishedBranches() {
|
||||
if (this.useOpenAuthoring) {
|
||||
return super.listUnpublishedBranches();
|
||||
}
|
||||
console.log(
|
||||
'%c Checking for Unpublished entries',
|
||||
'line-height: 30px;text-align: center;font-weight: bold',
|
||||
);
|
||||
const { repoOwner: owner, repoName: name } = this;
|
||||
const { data } = await this.query({
|
||||
query: queries.unpublishedPrBranches,
|
||||
variables: { owner, name },
|
||||
});
|
||||
const { nodes } = data.repository.refs as {
|
||||
nodes: {
|
||||
associatedPullRequests: { nodes: { headRef: { prefix: string; name: string } }[] };
|
||||
}[];
|
||||
};
|
||||
if (nodes.length > 0) {
|
||||
const branches = [] as { ref: string }[];
|
||||
nodes.forEach(({ associatedPullRequests }) => {
|
||||
associatedPullRequests.nodes.forEach(({ headRef }) => {
|
||||
branches.push({ ref: `${headRef.prefix}${headRef.name}` });
|
||||
});
|
||||
});
|
||||
|
||||
return await Promise.all(branches.map(branch => this.migrateBranch(branch)));
|
||||
} else {
|
||||
console.log(
|
||||
'%c No Unpublished entries',
|
||||
'line-height: 30px;text-align: center;font-weight: bold',
|
||||
);
|
||||
throw new APIError('Not Found', 404, 'GitHub');
|
||||
}
|
||||
}
|
||||
|
||||
async readUnpublishedBranchFile(contentKey: string) {
|
||||
// retrieveMetadata(contentKey) rejects in case of no metadata
|
||||
const metaData = await this.retrieveMetadata(contentKey).catch(() => null);
|
||||
if (metaData && metaData.objects && metaData.objects.entry && metaData.objects.entry.path) {
|
||||
const { path } = metaData.objects.entry;
|
||||
const { repoOwner: headOwner, repoName: headRepoName } = this;
|
||||
const { originRepoOwner: baseOwner, originRepoName: baseRepoName } = this;
|
||||
|
||||
const { data } = await this.query({
|
||||
query: queries.unpublishedBranchFile,
|
||||
variables: {
|
||||
headOwner,
|
||||
headRepoName,
|
||||
headExpression: `${metaData.branch}:${path}`,
|
||||
baseOwner,
|
||||
baseRepoName,
|
||||
baseExpression: `${this.branch}:${path}`,
|
||||
},
|
||||
});
|
||||
if (!data.head.object) {
|
||||
throw new EditorialWorkflowError('content is not under editorial workflow', true);
|
||||
}
|
||||
const result = {
|
||||
metaData,
|
||||
fileData: data.head.object.text,
|
||||
isModification: !!data.base.object,
|
||||
slug: this.slugFromContentKey(contentKey, metaData.collection),
|
||||
};
|
||||
return result;
|
||||
} else {
|
||||
throw new EditorialWorkflowError('content is not under editorial workflow', true);
|
||||
}
|
||||
}
|
||||
|
||||
getBranchQualifiedName(branch: string) {
|
||||
return `refs/heads/${branch}`;
|
||||
}
|
||||
@ -414,7 +415,10 @@ export default class GraphQLAPI extends API {
|
||||
// https://developer.github.com/v4/enum/pullrequeststate/
|
||||
// GraphQL state: [CLOSED, MERGED, OPEN]
|
||||
// REST API state: [closed, open]
|
||||
const state = data.repository.pullRequest.state === 'OPEN' ? 'open' : 'closed';
|
||||
const state =
|
||||
data.repository.pullRequest.state === 'OPEN'
|
||||
? PullRequestState.Open
|
||||
: PullRequestState.Closed;
|
||||
return {
|
||||
...data.repository.pullRequest,
|
||||
state,
|
||||
@ -424,7 +428,6 @@ export default class GraphQLAPI extends API {
|
||||
getPullRequestAndBranchQuery(branch: string, number: number) {
|
||||
const { repoOwner: owner, repoName: name } = this;
|
||||
const { originRepoOwner, originRepoName } = this;
|
||||
|
||||
return {
|
||||
query: queries.pullRequestAndBranch,
|
||||
variables: {
|
||||
@ -448,7 +451,7 @@ export default class GraphQLAPI extends API {
|
||||
return { branch: repository.branch, pullRequest: origin.pullRequest };
|
||||
}
|
||||
|
||||
async openPR({ number }: PR) {
|
||||
async openPR(number: number) {
|
||||
const pullRequest = await this.getPullRequest(number);
|
||||
|
||||
const { data } = await this.mutate({
|
||||
@ -467,10 +470,10 @@ export default class GraphQLAPI extends API {
|
||||
},
|
||||
});
|
||||
|
||||
return data!.closePullRequest;
|
||||
return data!.reopenPullRequest;
|
||||
}
|
||||
|
||||
async closePR({ number }: PR) {
|
||||
async closePR(number: number) {
|
||||
const pullRequest = await this.getPullRequest(number);
|
||||
|
||||
const { data } = await this.mutate({
|
||||
@ -495,13 +498,12 @@ export default class GraphQLAPI extends API {
|
||||
async deleteUnpublishedEntry(collectionName: string, slug: string) {
|
||||
try {
|
||||
const contentKey = this.generateContentKey(collectionName, slug);
|
||||
const branchName = this.generateBranchName(contentKey);
|
||||
|
||||
const branchName = branchFromContentKey(contentKey);
|
||||
const metadata = await this.retrieveMetadata(contentKey);
|
||||
if (metadata && metadata.pr) {
|
||||
if (metadata.pullRequest.number !== MOCK_PULL_REQUEST) {
|
||||
const { branch, pullRequest } = await this.getPullRequestAndBranch(
|
||||
branchName,
|
||||
metadata.pr.number,
|
||||
metadata.pullRequest.number,
|
||||
);
|
||||
|
||||
const { data } = await this.mutate({
|
||||
@ -631,7 +633,7 @@ export default class GraphQLAPI extends API {
|
||||
},
|
||||
});
|
||||
const { pullRequest } = data!.createPullRequest;
|
||||
return { ...pullRequest, head: { sha: pullRequest.headRefOid } };
|
||||
return (transformPullRequest(pullRequest) as unknown) as Octokit.PullsCreateResponse;
|
||||
}
|
||||
|
||||
async getFileSha(path: string, { repoURL = this.repoURL, branch = this.branch } = {}) {
|
||||
|
@ -21,28 +21,35 @@ describe('github API', () => {
|
||||
describe('editorialWorkflowGit', () => {
|
||||
it('should create PR with correct base branch name when publishing with editorial workflow', () => {
|
||||
let prBaseBranch = null;
|
||||
const api = new API({ branch: 'gh-pages', repo: 'my-repo' });
|
||||
let labels = null;
|
||||
const api = new API({
|
||||
branch: 'gh-pages',
|
||||
repo: 'owner/my-repo',
|
||||
initialWorkflowStatus: 'draft',
|
||||
});
|
||||
const responses = {
|
||||
'/repos/my-repo/branches/gh-pages': () => ({ commit: { sha: 'def' } }),
|
||||
'/repos/my-repo/git/trees/def': () => ({ tree: [] }),
|
||||
'/repos/my-repo/git/trees': () => ({}),
|
||||
'/repos/my-repo/git/commits': () => ({}),
|
||||
'/repos/my-repo/git/refs': () => ({}),
|
||||
'/repos/my-repo/pulls': pullRequest => {
|
||||
prBaseBranch = JSON.parse(pullRequest.body).base;
|
||||
return { head: { sha: 'cbd' } };
|
||||
'/repos/owner/my-repo/branches/gh-pages': () => ({ commit: { sha: 'def' } }),
|
||||
'/repos/owner/my-repo/git/trees/def': () => ({ tree: [] }),
|
||||
'/repos/owner/my-repo/git/trees': () => ({}),
|
||||
'/repos/owner/my-repo/git/commits': () => ({}),
|
||||
'/repos/owner/my-repo/git/refs': () => ({}),
|
||||
'/repos/owner/my-repo/pulls': req => {
|
||||
prBaseBranch = JSON.parse(req.body).base;
|
||||
return { head: { sha: 'cbd' }, labels: [], number: 1 };
|
||||
},
|
||||
'/repos/owner/my-repo/issues/1/labels': req => {
|
||||
labels = JSON.parse(req.body).labels;
|
||||
return {};
|
||||
},
|
||||
'/user': () => ({}),
|
||||
'/repos/my-repo/git/blobs': () => ({}),
|
||||
'/repos/my-repo/git/refs/meta/_netlify_cms': () => ({ object: {} }),
|
||||
};
|
||||
mockAPI(api, responses);
|
||||
|
||||
return expect(
|
||||
api
|
||||
.editorialWorkflowGit([], { slug: 'entry', sha: 'abc' }, null, {})
|
||||
.then(() => prBaseBranch),
|
||||
).resolves.toEqual('gh-pages');
|
||||
api.editorialWorkflowGit([], { slug: 'entry', sha: 'abc' }, null, {}).then(() => ({
|
||||
prBaseBranch,
|
||||
labels,
|
||||
})),
|
||||
).resolves.toEqual({ prBaseBranch: 'gh-pages', labels: ['netlify-cms/draft'] });
|
||||
});
|
||||
});
|
||||
|
||||
@ -291,39 +298,64 @@ describe('github API', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('migrateBranch', () => {
|
||||
it('should migrate to version 1 when no version', async () => {
|
||||
describe('migratePullRequest', () => {
|
||||
it('should migrate to pull request labels when no version', async () => {
|
||||
const api = new API({ branch: 'master', repo: 'owner/repo' });
|
||||
|
||||
const newBranch = { ref: 'refs/heads/cms/posts/2019-11-11-post-title' };
|
||||
api.migrateToVersion1 = jest.fn().mockResolvedValue(newBranch);
|
||||
const pr = {
|
||||
head: { ref: 'cms/2019-11-11-post-title' },
|
||||
title: 'pr title',
|
||||
number: 1,
|
||||
labels: [],
|
||||
};
|
||||
const metadata = { type: 'PR' };
|
||||
api.retrieveMetadata = jest.fn().mockResolvedValue(metadata);
|
||||
api.retrieveMetadataOld = jest.fn().mockResolvedValue(metadata);
|
||||
const newBranch = 'cms/posts/2019-11-11-post-title';
|
||||
const migrateToVersion1Result = {
|
||||
metadata: { ...metadata, branch: newBranch, version: '1' },
|
||||
pullRequest: { ...pr, number: 2 },
|
||||
};
|
||||
api.migrateToVersion1 = jest.fn().mockResolvedValue(migrateToVersion1Result);
|
||||
api.migrateToPullRequestLabels = jest.fn();
|
||||
|
||||
const branch = { ref: 'refs/heads/cms/2019-11-11-post-title' };
|
||||
await expect(api.migrateBranch(branch)).resolves.toBe(newBranch);
|
||||
await api.migratePullRequest(pr);
|
||||
|
||||
expect(api.migrateToVersion1).toHaveBeenCalledTimes(1);
|
||||
expect(api.migrateToVersion1).toHaveBeenCalledWith(branch, metadata);
|
||||
expect(api.migrateToVersion1).toHaveBeenCalledWith(pr, metadata);
|
||||
|
||||
expect(api.retrieveMetadata).toHaveBeenCalledTimes(1);
|
||||
expect(api.retrieveMetadata).toHaveBeenCalledWith('2019-11-11-post-title');
|
||||
expect(api.migrateToPullRequestLabels).toHaveBeenCalledTimes(1);
|
||||
expect(api.migrateToPullRequestLabels).toHaveBeenCalledWith(
|
||||
migrateToVersion1Result.pullRequest,
|
||||
migrateToVersion1Result.metadata,
|
||||
);
|
||||
|
||||
expect(api.retrieveMetadataOld).toHaveBeenCalledTimes(1);
|
||||
expect(api.retrieveMetadataOld).toHaveBeenCalledWith('2019-11-11-post-title');
|
||||
});
|
||||
|
||||
it('should not migrate to version 1 when version is 1', async () => {
|
||||
it('should migrate to pull request labels when version is 1', async () => {
|
||||
const api = new API({ branch: 'master', repo: 'owner/repo' });
|
||||
|
||||
api.migrateToVersion1 = jest.fn();
|
||||
const pr = {
|
||||
head: { ref: 'cms/posts/2019-11-11-post-title' },
|
||||
title: 'pr title',
|
||||
number: 1,
|
||||
labels: [],
|
||||
};
|
||||
const metadata = { type: 'PR', version: '1' };
|
||||
api.retrieveMetadata = jest.fn().mockResolvedValue(metadata);
|
||||
api.retrieveMetadataOld = jest.fn().mockResolvedValue(metadata);
|
||||
api.migrateToPullRequestLabels = jest.fn().mockResolvedValue(pr, metadata);
|
||||
|
||||
const branch = { ref: 'refs/heads/cms/posts/2019-11-11-post-title' };
|
||||
await expect(api.migrateBranch(branch)).resolves.toBe(branch);
|
||||
await api.migratePullRequest(pr);
|
||||
|
||||
expect(api.migrateToVersion1).toHaveBeenCalledTimes(0);
|
||||
|
||||
expect(api.retrieveMetadata).toHaveBeenCalledTimes(1);
|
||||
expect(api.retrieveMetadata).toHaveBeenCalledWith('posts/2019-11-11-post-title');
|
||||
expect(api.migrateToPullRequestLabels).toHaveBeenCalledTimes(1);
|
||||
expect(api.migrateToPullRequestLabels).toHaveBeenCalledWith(pr, metadata);
|
||||
|
||||
expect(api.retrieveMetadataOld).toHaveBeenCalledTimes(1);
|
||||
expect(api.retrieveMetadataOld).toHaveBeenCalledWith('posts/2019-11-11-post-title');
|
||||
});
|
||||
});
|
||||
|
||||
@ -331,10 +363,17 @@ describe('github API', () => {
|
||||
it('should migrate to version 1', async () => {
|
||||
const api = new API({ branch: 'master', repo: 'owner/repo' });
|
||||
|
||||
const pr = {
|
||||
head: { ref: 'cms/2019-11-11-post-title', sha: 'pr_head' },
|
||||
title: 'pr title',
|
||||
number: 1,
|
||||
labels: [],
|
||||
};
|
||||
|
||||
const newBranch = { ref: 'refs/heads/cms/posts/2019-11-11-post-title' };
|
||||
api.createBranch = jest.fn().mockResolvedValue(newBranch);
|
||||
|
||||
const newPr = { number: 2, head: { sha: 'new_head' } };
|
||||
const newPr = { ...pr, number: 2 };
|
||||
api.createPR = jest.fn().mockResolvedValue(newPr);
|
||||
|
||||
api.storeMetadata = jest.fn();
|
||||
@ -342,35 +381,42 @@ describe('github API', () => {
|
||||
api.deleteBranch = jest.fn();
|
||||
api.deleteMetadata = jest.fn();
|
||||
|
||||
const branch = { ref: 'refs/heads/cms/2019-11-11-post-title' };
|
||||
const branch = 'cms/2019-11-11-post-title';
|
||||
const metadata = {
|
||||
branch: 'cms/2019-11-11-post-title',
|
||||
branch,
|
||||
type: 'PR',
|
||||
pr: { head: 'old_head' },
|
||||
pr: { head: pr.head.sha },
|
||||
commitMessage: 'commitMessage',
|
||||
collection: 'posts',
|
||||
};
|
||||
|
||||
await expect(api.migrateToVersion1(branch, metadata)).resolves.toBe(newBranch);
|
||||
|
||||
expect(api.createBranch).toHaveBeenCalledTimes(1);
|
||||
expect(api.createBranch).toHaveBeenCalledWith('cms/posts/2019-11-11-post-title', 'old_head');
|
||||
|
||||
expect(api.createPR).toHaveBeenCalledTimes(1);
|
||||
expect(api.createPR).toHaveBeenCalledWith('commitMessage', 'cms/posts/2019-11-11-post-title');
|
||||
|
||||
expect(api.storeMetadata).toHaveBeenCalledTimes(1);
|
||||
expect(api.storeMetadata).toHaveBeenCalledWith('posts/2019-11-11-post-title', {
|
||||
const expectedMetadata = {
|
||||
type: 'PR',
|
||||
pr: { head: 'new_head', number: 2 },
|
||||
pr: { head: newPr.head.sha, number: 2 },
|
||||
commitMessage: 'commitMessage',
|
||||
collection: 'posts',
|
||||
branch: 'cms/posts/2019-11-11-post-title',
|
||||
version: '1',
|
||||
};
|
||||
await expect(api.migrateToVersion1(pr, metadata)).resolves.toEqual({
|
||||
metadata: expectedMetadata,
|
||||
pullRequest: newPr,
|
||||
});
|
||||
|
||||
expect(api.createBranch).toHaveBeenCalledTimes(1);
|
||||
expect(api.createBranch).toHaveBeenCalledWith('cms/posts/2019-11-11-post-title', 'pr_head');
|
||||
|
||||
expect(api.createPR).toHaveBeenCalledTimes(1);
|
||||
expect(api.createPR).toHaveBeenCalledWith('pr title', 'cms/posts/2019-11-11-post-title');
|
||||
|
||||
expect(api.storeMetadata).toHaveBeenCalledTimes(1);
|
||||
expect(api.storeMetadata).toHaveBeenCalledWith(
|
||||
'posts/2019-11-11-post-title',
|
||||
expectedMetadata,
|
||||
);
|
||||
|
||||
expect(api.closePR).toHaveBeenCalledTimes(1);
|
||||
expect(api.closePR).toHaveBeenCalledWith(metadata.pr);
|
||||
expect(api.closePR).toHaveBeenCalledWith(pr.number);
|
||||
|
||||
expect(api.deleteBranch).toHaveBeenCalledTimes(1);
|
||||
expect(api.deleteBranch).toHaveBeenCalledWith('cms/2019-11-11-post-title');
|
||||
@ -380,20 +426,55 @@ describe('github API', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('migrateToPullRequestLabels', () => {
|
||||
it('should migrate to pull request labels', async () => {
|
||||
const api = new API({ branch: 'master', repo: 'owner/repo' });
|
||||
|
||||
const pr = {
|
||||
head: { ref: 'cms/posts/2019-11-11-post-title', sha: 'pr_head' },
|
||||
title: 'pr title',
|
||||
number: 1,
|
||||
labels: [],
|
||||
};
|
||||
|
||||
api.setPullRequestStatus = jest.fn();
|
||||
api.deleteMetadata = jest.fn();
|
||||
|
||||
const metadata = {
|
||||
branch: pr.head.ref,
|
||||
type: 'PR',
|
||||
pr: { head: pr.head.sha },
|
||||
commitMessage: 'commitMessage',
|
||||
collection: 'posts',
|
||||
status: 'pending_review',
|
||||
};
|
||||
|
||||
await api.migrateToPullRequestLabels(pr, metadata);
|
||||
|
||||
expect(api.setPullRequestStatus).toHaveBeenCalledTimes(1);
|
||||
expect(api.setPullRequestStatus).toHaveBeenCalledWith(pr, 'pending_review');
|
||||
|
||||
expect(api.deleteMetadata).toHaveBeenCalledTimes(1);
|
||||
expect(api.deleteMetadata).toHaveBeenCalledWith('posts/2019-11-11-post-title');
|
||||
});
|
||||
});
|
||||
|
||||
describe('rebaseSingleCommit', () => {
|
||||
it('should create updated tree and commit', async () => {
|
||||
const api = new API({ branch: 'master', repo: 'owner/repo' });
|
||||
|
||||
api.getCommitsDiff = jest.fn().mockResolvedValueOnce([
|
||||
{ filename: 'removed.md', status: 'removed', sha: 'removed_sha' },
|
||||
{
|
||||
filename: 'renamed.md',
|
||||
status: 'renamed',
|
||||
previous_filename: 'previous_filename.md',
|
||||
sha: 'renamed_sha',
|
||||
},
|
||||
{ filename: 'added.md', status: 'added', sha: 'added_sha' },
|
||||
]);
|
||||
api.getDifferences = jest.fn().mockResolvedValueOnce({
|
||||
files: [
|
||||
{ filename: 'removed.md', status: 'removed', sha: 'removed_sha' },
|
||||
{
|
||||
filename: 'renamed.md',
|
||||
status: 'renamed',
|
||||
previous_filename: 'previous_filename.md',
|
||||
sha: 'renamed_sha',
|
||||
},
|
||||
{ filename: 'added.md', status: 'added', sha: 'added_sha' },
|
||||
],
|
||||
});
|
||||
|
||||
const newTree = { sha: 'new_tree_sha' };
|
||||
api.updateTree = jest.fn().mockResolvedValueOnce(newTree);
|
||||
@ -414,8 +495,8 @@ describe('github API', () => {
|
||||
|
||||
await expect(api.rebaseSingleCommit(baseCommit, commit)).resolves.toBe(newCommit);
|
||||
|
||||
expect(api.getCommitsDiff).toHaveBeenCalledTimes(1);
|
||||
expect(api.getCommitsDiff).toHaveBeenCalledWith('parent_sha', 'sha');
|
||||
expect(api.getDifferences).toHaveBeenCalledTimes(1);
|
||||
expect(api.getDifferences).toHaveBeenCalledWith('parent_sha', 'sha', '/repos/owner/repo');
|
||||
|
||||
expect(api.updateTree).toHaveBeenCalledTimes(1);
|
||||
expect(api.updateTree).toHaveBeenCalledWith('base_commit_sha', [
|
||||
@ -528,13 +609,18 @@ describe('github API', () => {
|
||||
];
|
||||
|
||||
api.request = jest.fn(() => Promise.resolve({ statuses }));
|
||||
|
||||
const sha = 'sha';
|
||||
await expect(api.getStatuses(sha)).resolves.toEqual([
|
||||
api.getBranchPullRequest = jest.fn(() => Promise.resolve({ head: { sha } }));
|
||||
|
||||
const collection = 'collection';
|
||||
const slug = 'slug';
|
||||
await expect(api.getStatuses(collection, slug)).resolves.toEqual([
|
||||
{ context: 'deploy', state: 'success', target_url: 'deploy-url' },
|
||||
{ context: 'build', state: 'other' },
|
||||
]);
|
||||
|
||||
expect(api.getBranchPullRequest).toHaveBeenCalledTimes(1);
|
||||
expect(api.getBranchPullRequest).toHaveBeenCalledWith('cms/collection/slug');
|
||||
expect(api.request).toHaveBeenCalledTimes(1);
|
||||
expect(api.request).toHaveBeenCalledWith(`/repos/repo/commits/${sha}/status`);
|
||||
});
|
||||
|
@ -9,7 +9,7 @@ describe('github GraphQL API', () => {
|
||||
|
||||
describe('editorialWorkflowGit', () => {
|
||||
it('should should flatten nested tree into a list of files', () => {
|
||||
const api = new GraphQLAPI({ branch: 'gh-pages', repo: 'my-repo' });
|
||||
const api = new GraphQLAPI({ branch: 'gh-pages', repo: 'owner/my-repo' });
|
||||
const entries = [
|
||||
{
|
||||
name: 'post-1.md',
|
||||
|
@ -185,7 +185,9 @@ describe('github backend implementation', () => {
|
||||
isModification: true,
|
||||
metaData: {
|
||||
branch: 'branch',
|
||||
objects: { entry: { path: 'entry-path' }, files: [{ path: 'image.png', sha: 'sha' }] },
|
||||
objects: {
|
||||
entry: { path: 'entry-path', mediaFiles: [{ path: 'image.png', id: 'sha' }] },
|
||||
},
|
||||
},
|
||||
};
|
||||
readUnpublishedBranchFile.mockResolvedValue(data);
|
||||
|
@ -41,6 +41,7 @@ export const pullRequest = gql`
|
||||
fragment PullRequestParts on PullRequest {
|
||||
id
|
||||
baseRefName
|
||||
baseRefOid
|
||||
body
|
||||
headRefName
|
||||
headRefOid
|
||||
@ -51,6 +52,11 @@ export const pullRequest = gql`
|
||||
repository {
|
||||
...RepositoryParts
|
||||
}
|
||||
labels(last: 100) {
|
||||
nodes {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
${repository}
|
||||
`;
|
||||
|
@ -25,6 +25,7 @@ import {
|
||||
UnpublishedEntryMediaFile,
|
||||
runWithLock,
|
||||
blobToFileObj,
|
||||
contentKeyFromBranch,
|
||||
} from 'netlify-cms-lib-util';
|
||||
import AuthenticationPage from './AuthenticationPage';
|
||||
import { Octokit } from '@octokit/rest';
|
||||
@ -299,7 +300,7 @@ export default class GitHub implements Implementation {
|
||||
const repoURL = this.useOpenAuthoring ? this.api!.originRepoURL : this.api!.repoURL;
|
||||
|
||||
const readFile = (path: string, id: string | null | undefined) =>
|
||||
this.api!.readFile(path, id, { repoURL }) as Promise<string>;
|
||||
this.api!.readFile(path, id, { repoURL }).catch(() => '') as Promise<string>;
|
||||
|
||||
return entriesByFiles(files, readFile, 'GitHub');
|
||||
}
|
||||
@ -307,10 +308,12 @@ export default class GitHub implements Implementation {
|
||||
// Fetches a single entry.
|
||||
getEntry(path: string) {
|
||||
const repoURL = this.api!.originRepoURL;
|
||||
return this.api!.readFile(path, null, { repoURL }).then(data => ({
|
||||
file: { path, id: null },
|
||||
data: data as string,
|
||||
}));
|
||||
return this.api!.readFile(path, null, { repoURL })
|
||||
.then(data => ({
|
||||
file: { path, id: null },
|
||||
data: data as string,
|
||||
}))
|
||||
.catch(() => ({ file: { path, id: null }, data: '' }));
|
||||
}
|
||||
|
||||
getMedia(mediaFolder = this.mediaFolder) {
|
||||
@ -412,7 +415,7 @@ export default class GitHub implements Implementation {
|
||||
unpublishedEntries() {
|
||||
const listEntriesKeys = () =>
|
||||
this.api!.listUnpublishedBranches().then(branches =>
|
||||
branches.map(({ ref }) => this.api!.contentKeyFromRef(ref)),
|
||||
branches.map(branch => contentKeyFromBranch(branch)),
|
||||
);
|
||||
|
||||
const readUnpublishedBranchFile = (contentKey: string) =>
|
||||
@ -431,10 +434,10 @@ export default class GitHub implements Implementation {
|
||||
) {
|
||||
const contentKey = this.api!.generateContentKey(collection, slug);
|
||||
const data = await this.api!.readUnpublishedBranchFile(contentKey);
|
||||
const files = data.metaData.objects.files || [];
|
||||
const files = data.metaData.objects.entry.mediaFiles || [];
|
||||
const mediaFiles = await loadEntryMediaFiles(
|
||||
data.metaData.branch,
|
||||
files.map(({ sha: id, path }) => ({ id, path })),
|
||||
files.map(({ id, path }) => ({ id, path })),
|
||||
);
|
||||
return {
|
||||
slug,
|
||||
@ -446,28 +449,18 @@ export default class GitHub implements Implementation {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses GitHub's Statuses API to retrieve statuses, infers which is for a
|
||||
* deploy preview via `getPreviewStatus`. Returns the url provided by the
|
||||
* status, as well as the status state, which should be one of 'success',
|
||||
* 'pending', and 'failure'.
|
||||
*/
|
||||
async getDeployPreview(collectionName: string, slug: string) {
|
||||
const contentKey = this.api!.generateContentKey(collectionName, slug);
|
||||
const data = await this.api!.retrieveMetadata(contentKey);
|
||||
async getDeployPreview(collection: string, slug: string) {
|
||||
try {
|
||||
const statuses = await this.api!.getStatuses(collection, slug);
|
||||
const deployStatus = getPreviewStatus(statuses, this.previewContext);
|
||||
|
||||
if (!data || !data.pr) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const headSHA = typeof data.pr.head === 'string' ? data.pr.head : data.pr.head.sha;
|
||||
const statuses = await this.api!.getStatuses(headSHA);
|
||||
const deployStatus = getPreviewStatus(statuses, this.previewContext);
|
||||
|
||||
if (deployStatus) {
|
||||
const { target_url: url, state } = deployStatus;
|
||||
return { url, status: state };
|
||||
} else {
|
||||
if (deployStatus) {
|
||||
const { target_url: url, state } = deployStatus;
|
||||
return { url, status: state };
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -38,37 +38,6 @@ export const blob = gql`
|
||||
${fragments.blobWithText}
|
||||
`;
|
||||
|
||||
export const unpublishedBranchFile = gql`
|
||||
query unpublishedBranchFile(
|
||||
$headOwner: String!
|
||||
$headRepoName: String!
|
||||
$headExpression: String!
|
||||
$baseOwner: String!
|
||||
$baseRepoName: String!
|
||||
$baseExpression: String!
|
||||
) {
|
||||
head: repository(owner: $headOwner, name: $headRepoName) {
|
||||
...RepositoryParts
|
||||
object(expression: $headExpression) {
|
||||
... on Blob {
|
||||
...BlobWithTextParts
|
||||
}
|
||||
}
|
||||
}
|
||||
base: repository(owner: $baseOwner, name: $baseRepoName) {
|
||||
...RepositoryParts
|
||||
object(expression: $baseExpression) {
|
||||
... on Blob {
|
||||
id
|
||||
oid
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${fragments.repository}
|
||||
${fragments.blobWithText}
|
||||
`;
|
||||
|
||||
export const statues = gql`
|
||||
query statues($owner: String!, $name: String!, $sha: GitObjectID!) {
|
||||
repository(owner: $owner, name: $name) {
|
||||
@ -140,30 +109,6 @@ export const files = (depth: number) => gql`
|
||||
${fragments.fileEntry}
|
||||
`;
|
||||
|
||||
export const unpublishedPrBranches = gql`
|
||||
query unpublishedPrBranches($owner: String!, $name: String!) {
|
||||
repository(owner: $owner, name: $name) {
|
||||
...RepositoryParts
|
||||
refs(refPrefix: "refs/heads/cms/", last: 50) {
|
||||
nodes {
|
||||
id
|
||||
associatedPullRequests(last: 50, states: OPEN) {
|
||||
nodes {
|
||||
id
|
||||
headRef {
|
||||
id
|
||||
name
|
||||
prefix
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${fragments.repository}
|
||||
`;
|
||||
|
||||
const branchQueryPart = `
|
||||
branch: ref(qualifiedName: $qualifiedName) {
|
||||
...BranchParts
|
||||
@ -181,6 +126,21 @@ export const branch = gql`
|
||||
${fragments.branch}
|
||||
`;
|
||||
|
||||
export const cmsBranches = gql`
|
||||
query cmsBranches($owner: String!, $name: String!) {
|
||||
repository(owner: $owner, name: $name) {
|
||||
...RepositoryParts
|
||||
refs(refPrefix: "refs/heads/cms/", last: 100) {
|
||||
nodes {
|
||||
...BranchParts
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${fragments.repository}
|
||||
${fragments.branch}
|
||||
`;
|
||||
|
||||
export const repository = gql`
|
||||
query repository($owner: String!, $name: String!) {
|
||||
repository(owner: $owner, name: $name) {
|
||||
@ -206,13 +166,27 @@ export const pullRequest = gql`
|
||||
${fragments.pullRequest}
|
||||
`;
|
||||
|
||||
export const pullRequests = gql`
|
||||
query pullRequests($owner: String!, $name: String!, $head: String, $states: [PullRequestState!]) {
|
||||
repository(owner: $owner, name: $name) {
|
||||
id
|
||||
pullRequests(last: 100, headRefName: $head, states: $states) {
|
||||
nodes {
|
||||
...PullRequestParts
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${fragments.pullRequest}
|
||||
`;
|
||||
|
||||
export const pullRequestAndBranch = gql`
|
||||
query pullRequestAndBranch($owner: String!, $name: String!, $origin_owner: String!, $origin_name: String!, $qualifiedName: String!, $number: Int!) {
|
||||
query pullRequestAndBranch($owner: String!, $name: String!, $originRepoOwner: String!, $originRepoName: String!, $qualifiedName: String!, $number: Int!) {
|
||||
repository(owner: $owner, name: $name) {
|
||||
...RepositoryParts
|
||||
${branchQueryPart}
|
||||
}
|
||||
origin: repository(owner: $origin_owner, name: $origin_name) {
|
||||
origin: repository(owner: $originRepoOwner, name: $originRepoName) {
|
||||
...RepositoryParts
|
||||
${pullRequestQueryPart}
|
||||
}
|
||||
@ -222,47 +196,6 @@ export const pullRequestAndBranch = gql`
|
||||
${fragments.pullRequest}
|
||||
`;
|
||||
|
||||
export const commitTree = gql`
|
||||
query commitTree($owner: String!, $name: String!, $sha: GitObjectID!) {
|
||||
repository(owner: $owner, name: $name) {
|
||||
...RepositoryParts
|
||||
commit: object(oid: $sha) {
|
||||
...ObjectParts
|
||||
... on Commit {
|
||||
tree {
|
||||
...ObjectParts
|
||||
entries {
|
||||
...TreeEntryParts
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${fragments.repository}
|
||||
${fragments.object}
|
||||
${fragments.treeEntry}
|
||||
`;
|
||||
|
||||
export const tree = gql`
|
||||
query tree($owner: String!, $name: String!, $sha: GitObjectID!) {
|
||||
repository(owner: $owner, name: $name) {
|
||||
...RepositoryParts
|
||||
tree: object(oid: $sha) {
|
||||
...ObjectParts
|
||||
... on Tree {
|
||||
entries {
|
||||
...TreeEntryParts
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${fragments.repository}
|
||||
${fragments.object}
|
||||
${fragments.treeEntry}
|
||||
`;
|
||||
|
||||
export const fileSha = gql`
|
||||
query fileSha($owner: String!, $name: String!, $expression: String!) {
|
||||
repository(owner: $owner, name: $name) {
|
||||
|
@ -21,6 +21,7 @@ import {
|
||||
responseParser,
|
||||
PreviewState,
|
||||
parseContentKey,
|
||||
branchFromContentKey,
|
||||
} from 'netlify-cms-lib-util';
|
||||
import { Base64 } from 'js-base64';
|
||||
import { Map, Set } from 'immutable';
|
||||
@ -457,18 +458,6 @@ export default class API {
|
||||
])(`${this.repoURL}/repository/files/${encodeURIComponent(path)}`);
|
||||
};
|
||||
|
||||
generateContentKey(collectionName: string, slug: string) {
|
||||
return generateContentKey(collectionName, slug);
|
||||
}
|
||||
|
||||
contentKeyFromBranch(branch: string) {
|
||||
return branch.substring(`${CMS_BRANCH_PREFIX}/`.length);
|
||||
}
|
||||
|
||||
branchFromContentKey(contentKey: string) {
|
||||
return `${CMS_BRANCH_PREFIX}/${contentKey}`;
|
||||
}
|
||||
|
||||
async getMergeRequests(sourceBranch?: string) {
|
||||
const mergeRequests: GitLabMergeRequest[] = await this.requestJSON({
|
||||
url: `${this.repoURL}/merge_requests`,
|
||||
@ -555,7 +544,7 @@ export default class API {
|
||||
|
||||
async retrieveMetadata(contentKey: string) {
|
||||
const { collection, slug } = parseContentKey(contentKey);
|
||||
const branch = this.branchFromContentKey(contentKey);
|
||||
const branch = branchFromContentKey(contentKey);
|
||||
const mergeRequest = await this.getBranchMergeRequest(branch);
|
||||
const diff = await this.getDifferences(mergeRequest.sha);
|
||||
const { old_path: path, new_file: newFile } = diff.find(d => !d.binary) as {
|
||||
@ -646,8 +635,8 @@ export default class API {
|
||||
}
|
||||
|
||||
async editorialWorkflowGit(files: (Entry | AssetProxy)[], entry: Entry, options: PersistOptions) {
|
||||
const contentKey = this.generateContentKey(options.collectionName as string, entry.slug);
|
||||
const branch = this.branchFromContentKey(contentKey);
|
||||
const contentKey = generateContentKey(options.collectionName as string, entry.slug);
|
||||
const branch = branchFromContentKey(contentKey);
|
||||
const unpublished = options.unpublished || false;
|
||||
if (!unpublished) {
|
||||
const items = await this.getCommitItems(files, this.branch);
|
||||
@ -694,8 +683,8 @@ export default class API {
|
||||
}
|
||||
|
||||
async updateUnpublishedEntryStatus(collection: string, slug: string, newStatus: string) {
|
||||
const contentKey = this.generateContentKey(collection, slug);
|
||||
const branch = this.branchFromContentKey(contentKey);
|
||||
const contentKey = generateContentKey(collection, slug);
|
||||
const branch = branchFromContentKey(contentKey);
|
||||
const mergeRequest = await this.getBranchMergeRequest(branch);
|
||||
|
||||
const labels = [
|
||||
@ -722,8 +711,8 @@ export default class API {
|
||||
}
|
||||
|
||||
async publishUnpublishedEntry(collectionName: string, slug: string) {
|
||||
const contentKey = this.generateContentKey(collectionName, slug);
|
||||
const branch = this.branchFromContentKey(contentKey);
|
||||
const contentKey = generateContentKey(collectionName, slug);
|
||||
const branch = branchFromContentKey(contentKey);
|
||||
const mergeRequest = await this.getBranchMergeRequest(branch);
|
||||
await this.mergeMergeRequest(mergeRequest);
|
||||
}
|
||||
@ -747,8 +736,8 @@ export default class API {
|
||||
}
|
||||
|
||||
async deleteUnpublishedEntry(collectionName: string, slug: string) {
|
||||
const contentKey = this.generateContentKey(collectionName, slug);
|
||||
const branch = this.branchFromContentKey(contentKey);
|
||||
const contentKey = generateContentKey(collectionName, slug);
|
||||
const branch = branchFromContentKey(contentKey);
|
||||
const mergeRequest = await this.getBranchMergeRequest(branch);
|
||||
await this.closeMergeRequest(mergeRequest);
|
||||
await this.deleteBranch(branch);
|
||||
@ -765,8 +754,8 @@ export default class API {
|
||||
}
|
||||
|
||||
async getStatuses(collectionName: string, slug: string) {
|
||||
const contentKey = this.generateContentKey(collectionName, slug);
|
||||
const branch = this.branchFromContentKey(contentKey);
|
||||
const contentKey = generateContentKey(collectionName, slug);
|
||||
const branch = branchFromContentKey(contentKey);
|
||||
const mergeRequest = await this.getBranchMergeRequest(branch);
|
||||
const statuses: GitLabCommitStatus[] = await this.getMergeRequestStatues(mergeRequest, branch);
|
||||
// eslint-disable-next-line @typescript-eslint/camelcase
|
||||
|
@ -27,6 +27,8 @@ import {
|
||||
runWithLock,
|
||||
getBlobSHA,
|
||||
blobToFileObj,
|
||||
contentKeyFromBranch,
|
||||
generateContentKey,
|
||||
} from 'netlify-cms-lib-util';
|
||||
import AuthenticationPage from './AuthenticationPage';
|
||||
import API, { API_NAME } from './API';
|
||||
@ -297,7 +299,7 @@ export default class GitLab implements Implementation {
|
||||
async unpublishedEntries() {
|
||||
const listEntriesKeys = () =>
|
||||
this.api!.listUnpublishedBranches().then(branches =>
|
||||
branches.map(branch => this.api!.contentKeyFromBranch(branch)),
|
||||
branches.map(branch => contentKeyFromBranch(branch)),
|
||||
);
|
||||
|
||||
const readUnpublishedBranchFile = (contentKey: string) =>
|
||||
@ -314,7 +316,7 @@ export default class GitLab implements Implementation {
|
||||
this.loadEntryMediaFiles(branch, files),
|
||||
} = {},
|
||||
) {
|
||||
const contentKey = this.api!.generateContentKey(collection, slug);
|
||||
const contentKey = generateContentKey(collection, slug);
|
||||
const data = await this.api!.readUnpublishedBranchFile(contentKey);
|
||||
const mediaFiles = await loadEntryMediaFiles(
|
||||
data.metaData.branch,
|
||||
|
@ -41,7 +41,7 @@ export const readFile = async (
|
||||
|
||||
const content = await fetchContent();
|
||||
if (key) {
|
||||
localForage.setItem(key, content);
|
||||
await localForage.setItem(key, content);
|
||||
}
|
||||
return content;
|
||||
};
|
||||
|
Reference in New Issue
Block a user