feat(proxy-server): add local fs middleware and make it the default (#3217)

This commit is contained in:
Erez Rokah
2020-02-10 18:07:52 +02:00
committed by GitHub
parent 97bc0c8dc4
commit 31dbd72273
14 changed files with 523 additions and 131 deletions

View File

@ -1,5 +1,5 @@
import { fromJS } from 'immutable';
import { applyDefaults, detectProxyServer } from '../config';
import { applyDefaults, detectProxyServer, handleLocalBackend } from '../config';
jest.spyOn(console, 'log').mockImplementation(() => {});
@ -185,38 +185,46 @@ describe('config', () => {
delete window.location;
});
it('should return undefined when not on localhost', async () => {
it('should return empty object when not on localhost', async () => {
window.location = { hostname: 'www.netlify.com' };
global.fetch = jest.fn();
await expect(detectProxyServer()).resolves.toBeUndefined();
await expect(detectProxyServer()).resolves.toEqual({});
expect(global.fetch).toHaveBeenCalledTimes(0);
});
it('should return undefined when fetch returns an error', async () => {
it('should return empty object when fetch returns an error', async () => {
window.location = { hostname: 'localhost' };
global.fetch = jest.fn().mockRejectedValue(new Error());
await expect(detectProxyServer(true)).resolves.toBeUndefined();
await expect(detectProxyServer(true)).resolves.toEqual({});
assetFetchCalled();
});
it('should return undefined when fetch returns an invalid response', async () => {
it('should return empty object when fetch returns an invalid response', async () => {
window.location = { hostname: 'localhost' };
global.fetch = jest
.fn()
.mockResolvedValue({ json: jest.fn().mockResolvedValue({ repo: [] }) });
await expect(detectProxyServer(true)).resolves.toBeUndefined();
await expect(detectProxyServer(true)).resolves.toEqual({});
assetFetchCalled();
});
it('should return proxyUrl when fetch returns a valid response', async () => {
it('should return result object when fetch returns a valid response', async () => {
window.location = { hostname: 'localhost' };
global.fetch = jest
.fn()
.mockResolvedValue({ json: jest.fn().mockResolvedValue({ repo: 'test-repo' }) });
await expect(detectProxyServer(true)).resolves.toBe('http://localhost:8081/api/v1');
global.fetch = jest.fn().mockResolvedValue({
json: jest.fn().mockResolvedValue({
repo: 'test-repo',
publish_modes: ['simple', 'editorial_workflow'],
type: 'local_git',
}),
});
await expect(detectProxyServer(true)).resolves.toEqual({
proxyUrl: 'http://localhost:8081/api/v1',
publish_modes: ['simple', 'editorial_workflow'],
type: 'local_git',
});
assetFetchCalled();
});
@ -224,12 +232,83 @@ describe('config', () => {
it('should use local_backend url', async () => {
const url = 'http://localhost:8082/api/v1';
window.location = { hostname: 'localhost' };
global.fetch = jest
.fn()
.mockResolvedValue({ json: jest.fn().mockResolvedValue({ repo: 'test-repo' }) });
await expect(detectProxyServer({ url })).resolves.toBe(url);
global.fetch = jest.fn().mockResolvedValue({
json: jest.fn().mockResolvedValue({
repo: 'test-repo',
publish_modes: ['simple', 'editorial_workflow'],
type: 'local_git',
}),
});
await expect(detectProxyServer({ url })).resolves.toEqual({
proxyUrl: url,
publish_modes: ['simple', 'editorial_workflow'],
type: 'local_git',
});
assetFetchCalled(url);
});
});
describe('handleLocalBackend', () => {
beforeEach(() => {
delete window.location;
});
it('should not replace backend config when proxy is not detected', async () => {
window.location = { hostname: 'localhost' };
global.fetch = jest.fn().mockRejectedValue(new Error());
const config = fromJS({ local_backend: true, backend: { name: 'github' } });
const actual = await handleLocalBackend(config);
expect(actual).toEqual(config);
});
it('should replace backend config when proxy is detected', async () => {
window.location = { hostname: 'localhost' };
global.fetch = jest.fn().mockResolvedValue({
json: jest.fn().mockResolvedValue({
repo: 'test-repo',
publish_modes: ['simple', 'editorial_workflow'],
type: 'local_git',
}),
});
const config = fromJS({ local_backend: true, backend: { name: 'github' } });
const actual = await handleLocalBackend(config);
expect(actual).toEqual(
fromJS({
local_backend: true,
backend: { name: 'proxy', proxy_url: 'http://localhost:8081/api/v1' },
}),
);
});
it('should replace publish mode when not supported by proxy', async () => {
window.location = { hostname: 'localhost' };
global.fetch = jest.fn().mockResolvedValue({
json: jest.fn().mockResolvedValue({
repo: 'test-repo',
publish_modes: ['simple'],
type: 'local_fs',
}),
});
const config = fromJS({
local_backend: true,
publish_mode: 'editorial_workflow',
backend: { name: 'github' },
});
const actual = await handleLocalBackend(config);
expect(actual).toEqual(
fromJS({
local_backend: true,
publish_mode: 'simple',
backend: { name: 'proxy', proxy_url: 'http://localhost:8081/api/v1' },
}),
);
});
});
});

View File

@ -155,19 +155,48 @@ export async function detectProxyServer(localBackend) {
}
try {
console.log(`Looking for Netlify CMS Proxy Server at '${proxyUrl}'`);
const { repo } = await fetch(`${proxyUrl}`, {
const { repo, publish_modes, type } = await fetch(`${proxyUrl}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'info' }),
}).then(res => res.json());
if (typeof repo === 'string') {
if (typeof repo === 'string' && Array.isArray(publish_modes) && typeof type === 'string') {
console.log(`Detected Netlify CMS Proxy Server at '${proxyUrl}' with repo: '${repo}'`);
return proxyUrl;
return { proxyUrl, publish_modes, type };
}
} catch {
console.log(`Netlify CMS Proxy Server not detected at '${proxyUrl}'`);
}
}
return {};
}
export async function handleLocalBackend(mergedConfig) {
if (mergedConfig.has('local_backend')) {
const { proxyUrl, publish_modes, type } = await detectProxyServer(
mergedConfig.toJS().local_backend,
);
if (proxyUrl) {
mergedConfig = mergePreloadedConfig(mergedConfig, {
backend: { name: 'proxy', proxy_url: proxyUrl },
});
if (
mergedConfig.has('publish_mode') &&
!publish_modes.includes(mergedConfig.get('publish_mode'))
) {
const newPublishMode = publish_modes[0];
console.log(
`'${mergedConfig.get(
'publish_mode',
)}' is not supported by '${type}' backend, switching to '${newPublishMode}'`,
);
mergedConfig = mergePreloadedConfig(mergedConfig, {
publish_mode: newPublishMode,
});
}
}
}
return mergedConfig;
}
export function loadConfig() {
@ -193,15 +222,7 @@ export function loadConfig() {
validateConfig(mergedConfig.toJS());
// detect running Netlify CMS proxy
if (mergedConfig.has('local_backend')) {
const proxyUrl = await detectProxyServer(mergedConfig.toJS().local_backend);
if (proxyUrl) {
mergedConfig = mergePreloadedConfig(mergedConfig, {
backend: { name: 'proxy', proxy_url: proxyUrl },
});
}
}
mergedConfig = await handleLocalBackend(mergedConfig);
const config = applyDefaults(mergedConfig);