refactor: use fetch cache arg instead of cache busting (#3584)

This commit is contained in:
Erez Rokah
2020-04-21 17:46:06 +03:00
committed by GitHub
parent 9b79623bc8
commit 8f1b325809
147 changed files with 70994 additions and 51781 deletions

View File

@ -215,7 +215,13 @@ export default class API {
}
buildRequest = (req: ApiRequest) => {
return flow([unsentRequest.withRoot(this.apiRoot), unsentRequest.withTimestamp])(req);
const withRoot = unsentRequest.withRoot(this.apiRoot)(req);
if (withRoot.has('cache')) {
return withRoot;
} else {
const withNoCache = unsentRequest.withNoCache(withRoot);
return withNoCache;
}
};
request = (req: ApiRequest): Promise<Response> => {
@ -295,7 +301,7 @@ export default class API {
return content;
};
async readFileMetadata(path: string, sha: string) {
async readFileMetadata(path: string, sha: string | null | undefined) {
const fetchFileMetadata = async () => {
try {
const { values }: { values: BitBucketCommit[] } = await this.requestJSON({

View File

@ -5,7 +5,6 @@ describe('github API', () => {
beforeEach(() => {
const fetch = jest.fn();
global.fetch = fetch;
global.Date = jest.fn(() => ({ getTime: () => 1000 }));
});
afterEach(() => {
@ -27,15 +26,13 @@ describe('github API', () => {
const result = await api.request('/some-path');
expect(result).toEqual('some response');
expect(fetch).toHaveBeenCalledTimes(1);
expect(fetch).toHaveBeenCalledWith(
'https://site.netlify.com/.netlify/git/github/some-path?ts=1000',
{
headers: {
Authorization: 'Bearer token',
'Content-Type': 'application/json; charset=utf-8',
},
expect(fetch).toHaveBeenCalledWith('https://site.netlify.com/.netlify/git/github/some-path', {
cache: 'no-cache',
headers: {
Authorization: 'Bearer token',
'Content-Type': 'application/json; charset=utf-8',
},
);
});
});
it('should throw error on not ok response with message property', async () => {

View File

@ -11,7 +11,6 @@ import {
AssetProxy,
Entry as LibEntry,
PersistOptions,
readFile,
readFileMetadata,
CMS_BRANCH_PREFIX,
generateContentKey,
@ -249,8 +248,7 @@ export default class API {
}
urlFor(path: string, options: Options) {
const cacheBuster = new Date().getTime();
const params = [`ts=${cacheBuster}`];
const params = [];
if (options.params) {
for (const key in options.params) {
params.push(`${key}=${encodeURIComponent(options.params[key] as string)}`);
@ -289,6 +287,7 @@ export default class API {
options: Options = {},
parser = (response: Response) => this.parseResponse(response),
) {
options = { cache: 'no-cache', ...options };
const headers = await this.requestHeaders(options.headers || {});
const url = this.urlFor(path, options);
let responseStatus = 500;
@ -312,6 +311,7 @@ export default class API {
}
async requestAllPages<T>(url: string, options: Options = {}) {
options = { cache: 'no-cache', ...options };
const headers = await this.requestHeaders(options.headers || {});
const processedURL = this.urlFor(url, options);
const allResponses = await getAllResponses(
@ -344,9 +344,7 @@ export default class API {
}
checkMetadataRef() {
return this.request(`${this.repoURL}/git/refs/meta/_netlify_cms`, {
cache: 'no-store',
})
return this.request(`${this.repoURL}/git/refs/meta/_netlify_cms`)
.then(response => response.object)
.catch(() => {
// Meta ref doesn't exist
@ -432,7 +430,6 @@ export default class API {
const metadataRequestOptions = {
params: { ref: 'refs/meta/_netlify_cms' },
headers: { Accept: 'application/vnd.github.v3.raw' },
cache: 'no-store' as RequestCache,
};
const errorHandler = (err: Error) => {
@ -627,12 +624,11 @@ export default class API {
if (!sha) {
sha = await this.getFileSha(path, { repoURL, branch });
}
const fetchContent = () => this.fetchBlobContent({ sha: sha as string, repoURL, parseText });
const content = await readFile(sha, fetchContent, localForage, parseText);
const content = await this.fetchBlobContent({ sha: sha as string, repoURL, parseText });
return content;
}
async readFileMetadata(path: string, sha: string) {
async readFileMetadata(path: string, sha: string | null | undefined) {
const fetchFileMetadata = async () => {
try {
const result: Octokit.ReposListCommitsResponse = await this.request(
@ -655,7 +651,9 @@ export default class API {
}
async fetchBlobContent({ sha, repoURL, parseText }: BlobArgs) {
const result: Octokit.GitGetBlobResponse = await this.request(`${repoURL}/git/blobs/${sha}`);
const result: Octokit.GitGetBlobResponse = await this.request(`${repoURL}/git/blobs/${sha}`, {
cache: 'force-cache',
});
if (parseText) {
// treat content as a utf-8 string
@ -969,9 +967,7 @@ export default class API {
const fileDataPath = encodeURIComponent(directory);
const fileDataURL = `${repoURL}/git/trees/${branch}:${fileDataPath}`;
const result: Octokit.GitGetTreeResponse = await this.request(fileDataURL, {
cache: 'no-store',
});
const result: Octokit.GitGetTreeResponse = await this.request(fileDataURL);
const file = result.tree.find(file => file.path === filename);
if (file) {
return file.sha;

View File

@ -93,7 +93,6 @@ describe('github API', () => {
beforeEach(() => {
const fetch = jest.fn();
global.fetch = fetch;
global.Date = jest.fn(() => ({ getTime: () => 1000 }));
});
afterEach(() => {
@ -112,7 +111,8 @@ describe('github API', () => {
const result = await api.request('/some-path');
expect(result).toEqual('some response');
expect(fetch).toHaveBeenCalledTimes(1);
expect(fetch).toHaveBeenCalledWith('https://api.github.com/some-path?ts=1000', {
expect(fetch).toHaveBeenCalledWith('https://api.github.com/some-path', {
cache: 'no-cache',
headers: {
Authorization: 'token token',
'Content-Type': 'application/json; charset=utf-8',
@ -157,7 +157,8 @@ describe('github API', () => {
const result = await api.request('/some-path');
expect(result).toEqual('some response');
expect(fetch).toHaveBeenCalledTimes(1);
expect(fetch).toHaveBeenCalledWith('https://api.github.com/some-path?ts=1000', {
expect(fetch).toHaveBeenCalledWith('https://api.github.com/some-path', {
cache: 'no-cache',
headers: {
Authorization: 'promise-token',
'Content-Type': 'application/json; charset=utf-8',

View File

@ -197,7 +197,7 @@ export default class API {
}
withAuthorizationHeaders = (req: ApiRequest) => {
const withHeaders: ApiRequest = unsentRequest.withHeaders(
const withHeaders = unsentRequest.withHeaders(
this.token ? { Authorization: `Bearer ${this.token}` } : {},
req,
);
@ -206,9 +206,14 @@ export default class API {
buildRequest = async (req: ApiRequest) => {
const withRoot: ApiRequest = unsentRequest.withRoot(this.apiRoot)(req);
const withAuthorizationHeaders: ApiRequest = await this.withAuthorizationHeaders(withRoot);
const withTimestamp: ApiRequest = unsentRequest.withTimestamp(withAuthorizationHeaders);
return withTimestamp;
const withAuthorizationHeaders = await this.withAuthorizationHeaders(withRoot);
if (withAuthorizationHeaders.has('cache')) {
return withAuthorizationHeaders;
} else {
const withNoCache: ApiRequest = unsentRequest.withNoCache(withAuthorizationHeaders);
return withNoCache;
}
};
request = async (req: ApiRequest): Promise<Response> => {
@ -286,7 +291,7 @@ export default class API {
return content;
};
async readFileMetadata(path: string, sha: string) {
async readFileMetadata(path: string, sha: string | null | undefined) {
const fetchFileMetadata = async () => {
try {
const result: GitLabCommit[] = await this.requestJSON({
@ -520,7 +525,6 @@ export default class API {
method: 'HEAD',
url: `${this.repoURL}/repository/files/${encodeURIComponent(path)}`,
params: { ref: branch },
cache: 'no-store',
});
const blobId = request.headers.get('X-Gitlab-Blob-Id') as string;
@ -532,7 +536,6 @@ export default class API {
method: 'HEAD',
url: `${this.repoURL}/repository/files/${encodeURIComponent(path)}`,
params: { ref: branch },
cache: 'no-store',
})
.then(() => true)
.catch(error => {

View File

@ -119,19 +119,21 @@ export type FileMetadata = {
const getFileMetadataKey = (id: string) => `gh.${id}.meta`;
export const readFileMetadata = async (
id: string,
id: string | null | undefined,
fetchMetadata: () => Promise<FileMetadata>,
localForage: LocalForage,
) => {
const key = getFileMetadataKey(id);
const cached = await localForage.getItem<FileMetadata>(key);
const key = id ? getFileMetadataKey(id) : null;
const cached = key && (await localForage.getItem<FileMetadata>(key));
if (cached) {
return cached;
} else {
const metadata = await fetchMetadata();
await localForage.setItem<FileMetadata>(key, metadata);
return metadata;
}
const metadata = await fetchMetadata();
if (key) {
await localForage.setItem<FileMetadata>(key, metadata);
}
return metadata;
};
/**

View File

@ -162,7 +162,7 @@ type ReadFile = (
options: { parseText: boolean },
) => Promise<string | Blob>;
type ReadFileMetadata = (path: string, id: string) => Promise<FileMetadata>;
type ReadFileMetadata = (path: string, id: string | null | undefined) => Promise<FileMetadata>;
type ReadUnpublishedFile = (
key: string,
@ -183,9 +183,7 @@ const fetchFiles = async (
try {
const [data, fileMetadata] = await Promise.all([
readFile(file.path, file.id, { parseText: true }),
file.id
? readFileMetadata(file.path, file.id)
: Promise.resolve({ author: '', updatedOn: '' }),
readFileMetadata(file.path, file.id),
]);
resolve({ file: { ...file, ...fileMetadata }, data: data as string });
sem.leave();

View File

@ -49,26 +49,23 @@ const ensureRequestArg = func => req => func(maybeRequestArg(req));
const ensureRequestArg2 = func => (arg, req) => func(arg, maybeRequestArg(req));
// This actually performs the built request object
const performRequest = ensureRequestArg(req => fetch(...toFetchArguments(req)));
const performRequest = ensureRequestArg(req => {
const args = toFetchArguments(req);
return fetch(...args);
});
// Each of the following functions takes options and returns another
// function that performs the requested action on a request. They each
// default to containing an empty object, so you can simply call them
// without arguments to generate a request with only those properties.
// function that performs the requested action on a request.
const getCurriedRequestProcessor = flow([ensureRequestArg2, curry]);
const getPropSetFunctions = path => [
getCurriedRequestProcessor((val, req) => req.setIn(path, val)),
getCurriedRequestProcessor((val, req) => (req.getIn(path) ? req : req.setIn(path, val))),
];
const getPropMergeFunctions = path => [
getCurriedRequestProcessor((obj, req) => req.updateIn(path, (p = Map()) => p.merge(obj))),
getCurriedRequestProcessor((obj, req) => req.updateIn(path, (p = Map()) => Map(obj).merge(p))),
];
const getPropSetFunction = path => getCurriedRequestProcessor((val, req) => req.setIn(path, val));
const getPropMergeFunction = path =>
getCurriedRequestProcessor((obj, req) => req.updateIn(path, (p = Map()) => p.merge(obj)));
const [withMethod, withDefaultMethod] = getPropSetFunctions(['method']);
const [withBody, withDefaultBody] = getPropSetFunctions(['body']);
const [withParams, withDefaultParams] = getPropMergeFunctions(['params']);
const [withHeaders, withDefaultHeaders] = getPropMergeFunctions(['headers']);
const withMethod = getPropSetFunction(['method']);
const withBody = getPropSetFunction(['body']);
const withNoCache = getPropSetFunction(['cache'])('no-cache');
const withParams = getPropMergeFunction(['params']);
const withHeaders = getPropMergeFunction(['headers']);
// withRoot sets a root URL, unless the URL is already absolute
const absolutePath = new RegExp('^(?:[a-z]+:)?//', 'i');
@ -83,24 +80,15 @@ const withRoot = getCurriedRequestProcessor((root, req) =>
}),
);
// withTimestamp needs no argument and has to run as late as possible,
// so it calls `withParams` only when it's actually called with a
// request.
const withTimestamp = ensureRequestArg(req => withParams({ ts: new Date().getTime() }, req));
export default {
toURL,
fromURL,
fromFetchArguments,
performRequest,
withMethod,
withDefaultMethod,
withBody,
withDefaultBody,
withHeaders,
withDefaultHeaders,
withParams,
withDefaultParams,
withRoot,
withTimestamp,
withNoCache,
};