Skip to content
This repository has been archived by the owner on May 15, 2024. It is now read-only.

Commit

Permalink
feat(index): proxy all modules in a remote module map (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
anescobar1991 authored Feb 28, 2020
1 parent aed8a8a commit c5fb1e7
Show file tree
Hide file tree
Showing 6 changed files with 522 additions and 88 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Want to get paid for your contributions to `one-app-dev-cdn`?

* Loads and serves local module map.
* Loads, serves, and merges remote module map with local module map.
* Acts as a proxy to modules defined in the remote module map.

## 🤹‍ Usage

Expand Down Expand Up @@ -90,6 +91,13 @@ Whether to use modules from `localDevPublicPath`. Passed as `true` or `false`, d
the remote module map will be merged with modules from `localDevPublicPath` with the local modules
taking precedence.

### Proxy Support

`one-app-dev-cdn` respects the `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` environment variables.

Make use of these environment variables if the remote module map or modules you want to use are
inaccessible without the use of a proxy server.

## 🏆 Contributing

We welcome Your interest in the American Express Open Source Community on Github.
Expand Down
82 changes: 81 additions & 1 deletion __tests__/__snapshots__/index.spec.js.snap
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`one-app-dev-cdn module-map.json extends the remote map with the local map 1`] = `"{\\"key\\":\\"not-used-in-development\\",\\"modules\\":{\\"module-b\\":{\\"node\\":{\\"url\\":\\"https://example.com/cdn/module-b/1.0.0/module-b.node.js\\",\\"integrity\\":\\"123\\"},\\"browser\\":{\\"url\\":\\"https://example.com/cdn/module-b/1.0.0/module-b.browser.js\\",\\"integrity\\":\\"234\\"},\\"legacyBrowser\\":{\\"url\\":\\"https://example.com/cdn/module-b/1.0.0/module-b.legacy.browser.js\\",\\"integrity\\":\\"345\\"}},\\"module-a\\":{\\"node\\":{\\"url\\":\\"https://example.com/cdn/module-a/1.0.0/module-a.node.js\\",\\"integrity\\":\\"123\\"},\\"browser\\":{\\"url\\":\\"https://example.com/cdn/module-a/1.0.0/module-a.browser.js\\",\\"integrity\\":\\"234\\"},\\"legacyBrowser\\":{\\"url\\":\\"https://example.com/cdn/module-a/1.0.0/module-a.legacy.browser.js\\",\\"integrity\\":\\"345\\"}}}}"`;
exports[`one-app-dev-cdn module-map.json extends the remote map with the local map 1`] = `"{\\"key\\":\\"not-used-in-development\\",\\"modules\\":{\\"module-b\\":{\\"node\\":{\\"url\\":\\"http://0.0.0.0:3001/static/cdn/module-b/1.0.0/module-b.node.js\\",\\"integrity\\":\\"123\\"},\\"browser\\":{\\"url\\":\\"http://0.0.0.0:3001/static/cdn/module-b/1.0.0/module-b.browser.js\\",\\"integrity\\":\\"234\\"},\\"legacyBrowser\\":{\\"url\\":\\"http://0.0.0.0:3001/static/cdn/module-b/1.0.0/module-b.legacy.browser.js\\",\\"integrity\\":\\"345\\"}},\\"module-a\\":{\\"node\\":{\\"url\\":\\"https://example.com/cdn/module-a/1.0.0/module-a.node.js\\",\\"integrity\\":\\"123\\"},\\"browser\\":{\\"url\\":\\"https://example.com/cdn/module-a/1.0.0/module-a.browser.js\\",\\"integrity\\":\\"234\\"},\\"legacyBrowser\\":{\\"url\\":\\"https://example.com/cdn/module-a/1.0.0/module-a.legacy.browser.js\\",\\"integrity\\":\\"345\\"}}}}"`;

exports[`one-app-dev-cdn module-map.json uses the local map overriding the cdn url placeholder with the one-app-dev-cdn url 1`] = `
Object {
Expand All @@ -24,6 +24,86 @@ Object {
}
`;

exports[`one-app-dev-cdn modules gets remote modules: module map response 1`] = `"{\\"key\\":\\"not-used-in-development\\",\\"modules\\":{\\"module-b\\":{\\"node\\":{\\"url\\":\\"http://0.0.0.0:3001/static/cdn/module-b/1.0.0/module-b.node.js\\",\\"integrity\\":\\"123\\"},\\"browser\\":{\\"url\\":\\"http://0.0.0.0:3001/static/cdn/module-b/1.0.0/module-b.browser.js\\",\\"integrity\\":\\"234\\"},\\"legacyBrowser\\":{\\"url\\":\\"http://0.0.0.0:3001/static/cdn/module-b/1.0.0/module-b.legacy.browser.js\\",\\"integrity\\":\\"345\\"}}}}"`;

exports[`one-app-dev-cdn modules gets remote modules: module response 1`] = `
Array [
Array [
"https://example.com/module-map.json",
Object {
"agent": ProxyAgent {
"_events": Object {},
"_eventsCount": 0,
"_maxListeners": undefined,
"_promisifiedCallback": false,
"callback": [Function],
"options": undefined,
"timeout": null,
},
},
],
Array [
"https://example.com//cdn/module-b/1.0.0/module-b.node.js",
Object {
"agent": ProxyAgent {
"_events": Object {},
"_eventsCount": 0,
"_maxListeners": undefined,
"_promisifiedCallback": false,
"callback": [Function],
"options": undefined,
"timeout": null,
},
"headers": Object {
"connection": "keep-alive",
},
},
],
]
`;

exports[`one-app-dev-cdn modules returns a 404 if a request for something not known as a module from the module map comes in: module map response 1`] = `"{\\"key\\":\\"not-used-in-development\\",\\"modules\\":{\\"module-b\\":{\\"node\\":{\\"url\\":\\"http://0.0.0.0:3001/static/cdn/module-b/1.0.0/module-b.node.js\\",\\"integrity\\":\\"123\\"},\\"browser\\":{\\"url\\":\\"http://0.0.0.0:3001/static/cdn/module-b/1.0.0/module-b.browser.js\\",\\"integrity\\":\\"234\\"},\\"legacyBrowser\\":{\\"url\\":\\"http://0.0.0.0:3001/static/cdn/module-b/1.0.0/module-b.legacy.browser.js\\",\\"integrity\\":\\"345\\"}}}}"`;

exports[`one-app-dev-cdn modules returns a 404 if a request for something not known as a module from the module map comes in: remote calls from got 1`] = `
Array [
Array [
"https://example.com/module-map.json",
Object {
"agent": ProxyAgent {
"_events": Object {},
"_eventsCount": 0,
"_maxListeners": undefined,
"_promisifiedCallback": false,
"callback": [Function],
"options": undefined,
"timeout": null,
},
},
],
]
`;

exports[`one-app-dev-cdn modules returns a 500 if a request to proxy a module from the module map fails: module map response 1`] = `"{\\"key\\":\\"not-used-in-development\\",\\"modules\\":{\\"module-b\\":{\\"node\\":{\\"url\\":\\"http://0.0.0.0:3001/static/cdn/module-b/1.0.0/module-b.node.js\\",\\"integrity\\":\\"123\\"},\\"browser\\":{\\"url\\":\\"http://0.0.0.0:3001/static/cdn/module-b/1.0.0/module-b.browser.js\\",\\"integrity\\":\\"234\\"},\\"legacyBrowser\\":{\\"url\\":\\"http://0.0.0.0:3001/static/cdn/module-b/1.0.0/module-b.legacy.browser.js\\",\\"integrity\\":\\"345\\"}}}}"`;

exports[`one-app-dev-cdn modules returns a 500 if a request to proxy a module from the module map fails: remote calls from got 1`] = `
Array [
Array [
"https://example.com/module-map.json",
Object {
"agent": ProxyAgent {
"_events": Object {},
"_eventsCount": 0,
"_maxListeners": undefined,
"_promisifiedCallback": false,
"callback": [Function],
"options": undefined,
"timeout": null,
},
},
],
]
`;

exports[`one-app-dev-cdn usage should throw if appPort is not given 1`] = `"appPort is a required param"`;

exports[`one-app-dev-cdn usage should throw if localDevPublicPath is not given 1`] = `"localDevPublicPath is a required param"`;
Expand Down
144 changes: 125 additions & 19 deletions __tests__/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ describe('one-app-dev-cdn', () => {
appPort,
} = {}) => {
process.env.NODE_ENV = nodeEnv;
console.log('remoteModuleMapUrl', remoteModuleMapUrl);

createPublicDir(publicDirContentsSetting);
return oneAppDevCdn({
localDevPublicPath: mockLocalDevPublicPath,
Expand All @@ -120,6 +120,11 @@ describe('one-app-dev-cdn', () => {
});
};

const sanitizeModuleMapForSnapshot = moduleMapString => moduleMapString.replace(
/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,8}/g,
'0.0.0.0:3001'
);

beforeEach(() => {
jest
.resetAllMocks()
Expand Down Expand Up @@ -234,10 +239,8 @@ describe('one-app-dev-cdn', () => {
});
});

// it('overrides the existing')

it('does not use the local map when useLocalModules is false', () => {
expect.assertions(4);
expect.assertions(3);

const fcdn = setupTest({
useLocalModules: false,
Expand All @@ -252,26 +255,45 @@ describe('one-app-dev-cdn', () => {
expect(response.status).toBe(200);
expect(response.header['content-type']).toMatch(/^application\/json/);
const responseBody = response.text;
expect(responseBody).toBe(
JSON.stringify({ ...defaultRemoteMap, key: 'not-used-in-development' })
);

expect(responseBody.modules).not.toEqual(defaultLocalMap.modules);
});
});

it('mirrors the remote map but modifies the key property', () => {
it('mirrors the remote map but modifies the key property and the module URLs', () => {
expect.assertions(4);
const remoteModuleMapUrl = 'https://my-domain.com/map/module-map.json';
const fcdn = setupTest({ appPort: 3000, useLocalModules: false, remoteModuleMapUrl });
got.mockReturnJsonOnce(defaultRemoteMap);

const modifiedRemoteMap = {
key: 'not-used-in-development',
modules: {
'module-b': {
node: {
url: 'http://0.0.0.0:3001/static/cdn/module-b/1.0.0/module-b.node.js',
integrity: '123',
},
browser: {
url: 'http://0.0.0.0:3001/static/cdn/module-b/1.0.0/module-b.browser.js',
integrity: '234',
},
legacyBrowser: {
url: 'http://0.0.0.0:3001/static/cdn/module-b/1.0.0/module-b.legacy.browser.js',
integrity: '345',
},
},
},
};
return supertest(fcdn)
.get('/module-map.json')
.then((response) => {
expect(response.status).toBe(200);
expect(response.header['content-type']).toMatch(/^application\/json/);
expect(response.text).toEqual(JSON.stringify({ ...defaultRemoteMap, key: 'not-used-in-development' }));
expect(got.mock.calls).toEqual([[remoteModuleMapUrl]]);
expect(
sanitizeModuleMapForSnapshot(response.text)
).toEqual(JSON.stringify(modifiedRemoteMap));
expect(got.mock.calls[0]).toContain(remoteModuleMapUrl);
});
});

Expand All @@ -287,8 +309,10 @@ describe('one-app-dev-cdn', () => {
.then((response) => {
expect(response.status).toBe(200);
expect(response.header['content-type']).toMatch(/^application\/json/);
expect(response.text).toMatchSnapshot();
expect(got.mock.calls).toEqual([[remoteModuleMapUrl]]);
expect(
sanitizeModuleMapForSnapshot(response.text)
).toMatchSnapshot();
expect(got.mock.calls[0]).toContain(remoteModuleMapUrl);
});
});

Expand All @@ -303,11 +327,16 @@ describe('one-app-dev-cdn', () => {
.then((response) => {
expect(response.status).toBe(200);
expect(response.header['content-type']).toMatch(/^application\/json/);
const responseBody = response.text;
expect(responseBody).toEqual(
JSON.stringify({ ...defaultRemoteMap, key: 'not-used-in-development' })

expect(
sanitizeModuleMapForSnapshot(response.text)
).toEqual(
JSON.stringify({
...defaultRemoteMap,
key: 'not-used-in-development',
}).replace(/https:\/\/example.com\//g, 'http://0.0.0.0:3001/static/')
);
expect(got.mock.calls).toEqual([[remoteModuleMapUrl]]);
expect(got.mock.calls[0]).toContain(remoteModuleMapUrl);
});
});

Expand Down Expand Up @@ -343,7 +372,7 @@ describe('one-app-dev-cdn', () => {
expect(response.status).toBe(200);
expect(response.header['content-type']).toMatch(/^application\/json/);
expect(response.text).toEqual(JSON.stringify(defaultLocalMap));
expect(got.mock.calls).toEqual([[remoteModuleMapUrl]]);
expect(got.mock.calls[0]).toContain(remoteModuleMapUrl);
});
});

Expand All @@ -359,7 +388,7 @@ describe('one-app-dev-cdn', () => {
expect(response.status).toBe(200);
expect(response.header['content-type']).toMatch(/^application\/json/);
expect(response.text).toEqual(JSON.stringify(defaultLocalMap));
expect(got.mock.calls).toEqual([[remoteModuleMapUrl]]);
expect(got.mock.calls[0]).toContain(remoteModuleMapUrl);
expect(console.warn).toHaveBeenCalledTimes(1);
expect(console.warn).toHaveBeenCalledWith(
'one-app-dev-cdn error loading module map from https://my-domain.com/map/module-map.json: Error: simulated timeout or some other network error!'
Expand All @@ -374,7 +403,6 @@ describe('one-app-dev-cdn', () => {
appPort: 3000,
useLocalModules: true,
});
got.mockReturnJsonOnce({ 'module-a': '1.2.0' });

return supertest(fcdn)
.get('/module-map.json')
Expand All @@ -401,6 +429,84 @@ describe('one-app-dev-cdn', () => {
expect(response.text).toBe('console.log("a");');
});
});

it('gets remote modules', async () => {
expect.assertions(7);

const fcdn = setupTest({
useLocalModules: false,
appPort: 3000,
remoteModuleMapUrl: 'https://example.com/module-map.json',
});
got.mockReturnJsonOnce(defaultRemoteMap);
got.mockReturnFileOnce('console.log("a");');

const moduleMapResponse = await supertest(fcdn)
.get('/module-map.json');

expect(moduleMapResponse.status).toBe(200);
expect(moduleMapResponse.header['content-type']).toMatch(/^application\/json/);
expect(
sanitizeModuleMapForSnapshot(moduleMapResponse.text)
).toMatchSnapshot('module map response');

const moduleResponse = await supertest(fcdn)
.get('/cdn/module-b/1.0.0/module-b.node.js?key="123');
expect(moduleResponse.status).toBe(200);
expect(moduleResponse.header['content-type']).toMatch(/^application\/javascript/);
expect(moduleResponse.text).toBe('console.log("a");');
expect(got.mock.calls).toMatchSnapshot('module response');
});

it('returns a 404 if a request for something not known as a module from the module map comes in', async () => {
expect.assertions(5);

const fcdn = setupTest({
useLocalModules: false,
appPort: 3000,
remoteModuleMapUrl: 'https://example.com/module-map.json',
});
got.mockReturnJsonOnce(defaultRemoteMap);

const moduleMapResponse = await supertest(fcdn)
.get('/module-map.json');

expect(moduleMapResponse.status).toBe(200);
expect(moduleMapResponse.header['content-type']).toMatch(/^application\/json/);
expect(
sanitizeModuleMapForSnapshot(moduleMapResponse.text)
).toMatchSnapshot('module map response');
expect(got.mock.calls).toMatchSnapshot('remote calls from got');

const moduleResponse = await supertest(fcdn)
.get('/cdn/not-a-module/1.0.0/module-d.node.js?key="123');
expect(moduleResponse.status).toBe(404);
});

it('returns a 500 if a request to proxy a module from the module map fails', async () => {
expect.assertions(5);

const fcdn = setupTest({
useLocalModules: false,
appPort: 3000,
remoteModuleMapUrl: 'https://example.com/module-map.json',
});
got.mockReturnJsonOnce(defaultRemoteMap);
got.mockReturnJsonOnce(new Error('Network error!'));
const moduleMapResponse = await supertest(fcdn)
.get('/module-map.json');

expect(moduleMapResponse.status).toBe(200);
expect(moduleMapResponse.header['content-type']).toMatch(/^application\/json/);
expect(
sanitizeModuleMapForSnapshot(moduleMapResponse.text)
).toMatchSnapshot('module map response');
expect(got.mock.calls).toMatchSnapshot('remote calls from got');

const moduleResponse = await supertest(fcdn)
.get('/cdn/module-b/1.0.0/module-b.node.js?key="123');
expect(moduleResponse.status).toBe(500);
});
});

describe('production', () => {
Expand Down
Loading

0 comments on commit c5fb1e7

Please sign in to comment.