diff --git a/src/json-pipe.js b/src/json-pipe.js
index 554adbf3..91b7c09a 100644
--- a/src/json-pipe.js
+++ b/src/json-pipe.js
@@ -65,6 +65,13 @@ async function fetchJsonContent(state, req, res) {
if (state.content.sourceBus === 'content') {
keys.push(await computeSurrogateKey(`${contentBusId}${info.path}`));
keys.push(contentBusId);
+ const contentKeyPrefix = partition === 'preview' ? 'p_' : '';
+ if (partition === 'preview') {
+ // temporarily provide additional preview content keys
+ // TODO: eventually provide either (prefixed) preview or (unprefixed) live content keys
+ keys.push(`${contentKeyPrefix}${await computeSurrogateKey(`${contentBusId}${info.path}`)}`);
+ keys.push(`${contentKeyPrefix}${contentBusId}`);
+ }
} else {
keys.push(`${ref}--${repo}--${owner}_code`);
keys.push(await computeSurrogateKey(`${ref}--${repo}--${owner}${info.path}`));
@@ -104,6 +111,13 @@ async function computeSurrogateKeys(state) {
if (state.content.sourceBus.includes('content')) {
keys.push(await computeSurrogateKey(`${state.contentBusId}${state.info.path}`));
keys.push(state.contentBusId);
+ const contentKeyPrefix = state.partition === 'preview' ? 'p_' : '';
+ if (state.partition === 'preview') {
+ // temporarily provide additional preview content keys
+ // TODO: eventually provide either (prefixed) preview or (unprefixed) live content keys
+ keys.push(`${contentKeyPrefix}${await computeSurrogateKey(`${state.contentBusId}${state.info.path}`)}`);
+ keys.push(`${contentKeyPrefix}${state.contentBusId}`);
+ }
}
return keys;
diff --git a/src/steps/fetch-404.js b/src/steps/fetch-404.js
index 22c45fbf..5ed33e17 100644
--- a/src/steps/fetch-404.js
+++ b/src/steps/fetch-404.js
@@ -22,7 +22,7 @@ import { getPathKey } from './set-x-surrogate-key-header.js';
*/
export default async function fetch404(state, req, res) {
const {
- owner, repo, ref, contentBusId,
+ owner, repo, ref, contentBusId, partition,
} = state;
const ret = await state.s3Loader.getObject('helix-code-bus', `${owner}/${repo}/${ref}/404.html`);
if (ret.status === 200) {
@@ -46,12 +46,25 @@ export default async function fetch404(state, req, res) {
`${ref}--${repo}--${owner}_404`,
`${ref}--${repo}--${owner}_code`,
];
+ const contentKeyPrefix = partition === 'preview' ? 'p_' : '';
+ if (partition === 'preview') {
+ // temporarily provide additional preview content keys
+ // TODO: eventually provide either (prefixed) preview or (unprefixed) live content keys
+ keys.push(`${contentKeyPrefix}${pathKey}`);
+ keys.push(`${contentKeyPrefix}${contentBusId}`);
+ }
if (state.info.unmappedPath) {
- keys.push(await getPathKey({
+ const unmappedPathKey = await getPathKey({
contentBusId,
info: { path: state.info.unmappedPath },
- }));
+ });
+ keys.push(unmappedPathKey);
+ if (partition === 'preview') {
+ // temporarily provide additional preview content key
+ // TODO: eventually provide either (prefixed) preview or (unprefixed) live content key
+ keys.push(`${contentKeyPrefix}${unmappedPathKey}`);
+ }
}
res.headers.set('x-surrogate-key', keys.join(' '));
diff --git a/src/steps/fetch-content.js b/src/steps/fetch-content.js
index 080c7220..f017e25b 100644
--- a/src/steps/fetch-content.js
+++ b/src/steps/fetch-content.js
@@ -48,6 +48,13 @@ export default async function fetchContent(state, req, res) {
} else {
keys.push(await computeSurrogateKey(`${contentBusId}${info.path}`));
keys.push(contentBusId);
+ const contentKeyPrefix = partition === 'preview' ? 'p_' : '';
+ if (partition === 'preview') {
+ // temporarily provide additional preview content keys
+ // TODO: eventually provide either (prefixed) preview or (unprefixed) live content keys
+ keys.push(`${contentKeyPrefix}${await computeSurrogateKey(`${contentBusId}${info.path}`)}`);
+ keys.push(`${contentKeyPrefix}${contentBusId}`);
+ }
}
res.headers.set('x-surrogate-key', keys.join(' '));
res.error = 'moved';
diff --git a/src/steps/set-x-surrogate-key-header.js b/src/steps/set-x-surrogate-key-header.js
index 8a0f10bd..855ee346 100644
--- a/src/steps/set-x-surrogate-key-header.js
+++ b/src/steps/set-x-surrogate-key-header.js
@@ -42,11 +42,12 @@ export async function getPathKey(state) {
*/
export default async function setXSurrogateKeyHeader(state, req, res) {
const {
- contentBusId, owner, repo, ref,
+ contentBusId, owner, repo, ref, partition,
} = state;
const isCode = state.content.sourceBus === 'code';
+ const contentKeyPrefix = partition === 'preview' ? 'p_' : '';
const keys = [];
const hash = await getPathKey(state);
if (isCode) {
@@ -57,6 +58,13 @@ export default async function setXSurrogateKeyHeader(state, req, res) {
keys.push(`${contentBusId}_metadata`);
keys.push(`${ref}--${repo}--${owner}_head`);
keys.push(contentBusId);
+ if (partition === 'preview') {
+ // temporarily provide additional preview content keys
+ // TODO: eventually provide either (prefixed) preview or (unprefixed) live content keys
+ keys.push(`${contentKeyPrefix}${hash}`);
+ keys.push(`${contentKeyPrefix}${contentBusId}_metadata`);
+ keys.push(`${contentKeyPrefix}${contentBusId}`);
+ }
}
// for folder-mapped resources, we also need to include the surrogate key of the mapped metadata
if (state.mapped) {
@@ -67,6 +75,17 @@ export default async function setXSurrogateKeyHeader(state, req, res) {
info: { path: state.info.unmappedPath },
}));
}
+ if (partition === 'preview') {
+ // temporarily provide additional preview content keys
+ // TODO: eventually provide either (prefixed) preview or (unprefixed) live content keys
+ keys.push(`${contentKeyPrefix}${hash}_metadata`);
+ if (state.info.unmappedPath) {
+ keys.push(`${contentKeyPrefix}${await getPathKey({
+ contentBusId,
+ info: { path: state.info.unmappedPath },
+ })}`);
+ }
+ }
}
res.headers.set('x-surrogate-key', keys.join(' '));
}
diff --git a/test/json-pipe.test.js b/test/json-pipe.test.js
index 58224f02..a53d9743 100644
--- a/test/json-pipe.test.js
+++ b/test/json-pipe.test.js
@@ -108,6 +108,17 @@ describe('JSON Pipe Test', () => {
'last-modified': 'Wed, 12 Oct 2009 17:50:00 GMT',
},
}),
+ )
+ .reply(
+ 'helix-content-bus',
+ 'foobar/live/en/index.json',
+ new PipelineResponse(TEST_SINGLE_SHEET, {
+ headers: {
+ 'content-type': 'application/json',
+ 'x-amz-meta-x-source-location': 'foo-bar',
+ 'last-modified': 'Wed, 12 Oct 2009 17:50:00 GMT',
+ },
+ }),
),
timer: {
update: () => {},
@@ -126,7 +137,23 @@ describe('JSON Pipe Test', () => {
it('fetches correct content', async () => {
const state = createDefaultState();
- const resp = await jsonPipe(state, new PipelineRequest('https://json-filter.com/?limit=10&offset=5'));
+ let resp = await jsonPipe(state, new PipelineRequest('https://json-filter.com/?limit=10&offset=5'));
+ assert.strictEqual(resp.status, 200);
+ assert.deepStrictEqual(await resp.json(), {
+ ':type': 'sheet',
+ offset: 5,
+ limit: 10,
+ total: TEST_DATA.length,
+ data: TEST_DATA.slice(5, 15),
+ });
+ assert.deepStrictEqual(Object.fromEntries(resp.headers.entries()), {
+ 'content-type': 'application/json',
+ 'x-surrogate-key': 'Atrz_qDg26DmSe9a foobar p_Atrz_qDg26DmSe9a p_foobar',
+ 'last-modified': 'Wed, 12 Oct 2009 17:50:00 GMT',
+ });
+ // live (code coverage)
+ state.partition = 'live';
+ resp = await jsonPipe(state, new PipelineRequest('https://json-filter.com/?limit=10&offset=5'));
assert.strictEqual(resp.status, 200);
assert.deepStrictEqual(await resp.json(), {
':type': 'sheet',
@@ -148,7 +175,7 @@ describe('JSON Pipe Test', () => {
assert.strictEqual(resp.status, 404);
assert.deepStrictEqual(Object.fromEntries(resp.headers.entries()), {
'x-error': 'failed to load /config.json: 404',
- 'x-surrogate-key': 'U_NW4adJU7Qazf-I pzrU-nNKQOUYNTEf ref--repo--owner_code kz8SoCaNqfp4ohQo foobar',
+ 'x-surrogate-key': 'U_NW4adJU7Qazf-I pzrU-nNKQOUYNTEf ref--repo--owner_code kz8SoCaNqfp4ohQo foobar p_kz8SoCaNqfp4ohQo p_foobar',
});
});
@@ -165,7 +192,7 @@ describe('JSON Pipe Test', () => {
assert.strictEqual(resp.status, 200);
assert.deepStrictEqual(Object.fromEntries(resp.headers.entries()), {
'content-type': 'application/json',
- 'x-surrogate-key': 'U_NW4adJU7Qazf-I pzrU-nNKQOUYNTEf ref--repo--owner_code kz8SoCaNqfp4ohQo foobar',
+ 'x-surrogate-key': 'U_NW4adJU7Qazf-I pzrU-nNKQOUYNTEf ref--repo--owner_code kz8SoCaNqfp4ohQo foobar p_kz8SoCaNqfp4ohQo p_foobar',
});
assert.deepStrictEqual(await resp.json(), {
public: {
@@ -193,7 +220,7 @@ describe('JSON Pipe Test', () => {
});
assert.deepStrictEqual(Object.fromEntries(resp.headers.entries()), {
'content-type': 'application/json',
- 'x-surrogate-key': 'Atrz_qDg26DmSe9a foobar',
+ 'x-surrogate-key': 'Atrz_qDg26DmSe9a foobar p_Atrz_qDg26DmSe9a p_foobar',
'last-modified': 'Wed, 12 Oct 2009 17:50:00 GMT',
});
});
@@ -214,7 +241,7 @@ describe('JSON Pipe Test', () => {
'access-control-allow-origin': '*',
'content-security-policy': 'default-src \'self\'',
'last-modified': 'Wed, 12 Oct 2009 17:50:00 GMT',
- 'x-surrogate-key': 'Atrz_qDg26DmSe9a foobar',
+ 'x-surrogate-key': 'Atrz_qDg26DmSe9a foobar p_Atrz_qDg26DmSe9a p_foobar',
'content-type': 'application/json',
});
});
@@ -229,7 +256,7 @@ describe('JSON Pipe Test', () => {
'access-control-allow-origin': '*',
'content-security-policy': 'default-src \'self\'',
'x-error': 'failed to load /en/index.json: 404',
- 'x-surrogate-key': 'SIMSxecp2CJXqGYs ref--repo--owner_code Atrz_qDg26DmSe9a foobar',
+ 'x-surrogate-key': 'SIMSxecp2CJXqGYs ref--repo--owner_code Atrz_qDg26DmSe9a foobar p_Atrz_qDg26DmSe9a p_foobar',
});
});
@@ -244,8 +271,26 @@ describe('JSON Pipe Test', () => {
'x-amz-meta-redirect-location': '/de/index.json',
},
}),
- );
- const resp = await jsonPipe(state, new PipelineRequest('https://json-filter.com/?limit=10&offset=5'));
+ )
+ .reply(
+ 'helix-content-bus',
+ 'foobar/live/en/index.json',
+ new PipelineResponse(TEST_SINGLE_SHEET, {
+ headers: {
+ 'content-type': 'application/json',
+ 'x-amz-meta-redirect-location': '/de/index.json',
+ },
+ }),
+ );
+ let resp = await jsonPipe(state, new PipelineRequest('https://json-filter.com/?limit=10&offset=5'));
+ assert.strictEqual(resp.status, 301);
+ assert.deepStrictEqual(Object.fromEntries(resp.headers.entries()), {
+ 'location': '/de/index.json',
+ 'x-surrogate-key': 'Atrz_qDg26DmSe9a foobar p_Atrz_qDg26DmSe9a p_foobar',
+ });
+ // live (code coverage)
+ state.partition = 'live';
+ resp = await jsonPipe(state, new PipelineRequest('https://json-filter.com/?limit=10&offset=5'));
assert.strictEqual(resp.status, 301);
assert.deepStrictEqual(Object.fromEntries(resp.headers.entries()), {
'location': '/de/index.json',
@@ -301,7 +346,7 @@ describe('JSON Pipe Test', () => {
const headers = Object.fromEntries(resp.headers.entries());
assert.deepStrictEqual(headers, {
'last-modified': 'Wed, 12 Oct 2009 17:50:00 GMT',
- 'x-surrogate-key': 'Atrz_qDg26DmSe9a foobar',
+ 'x-surrogate-key': 'Atrz_qDg26DmSe9a foobar p_Atrz_qDg26DmSe9a p_foobar',
'content-type': 'application/json',
});
});
@@ -333,7 +378,7 @@ describe('JSON Pipe Test', () => {
const headers = Object.fromEntries(resp.headers.entries());
assert.deepStrictEqual(headers, {
'last-modified': 'Wed, 12 Oct 2009 15:50:00 GMT',
- 'x-surrogate-key': 'Atrz_qDg26DmSe9a foobar',
+ 'x-surrogate-key': 'Atrz_qDg26DmSe9a foobar p_Atrz_qDg26DmSe9a p_foobar',
'content-type': 'application/json',
});
});
diff --git a/test/rendering.test.js b/test/rendering.test.js
index 9c5b5141..ccf8dcbc 100644
--- a/test/rendering.test.js
+++ b/test/rendering.test.js
@@ -148,7 +148,7 @@ describe('Rendering', () => {
config = DEFAULT_CONFIG;
});
- async function render(url, selector = '', expectedStatus = 200) {
+ async function render(url, selector = '', expectedStatus = 200, partition = 'live') {
const req = new PipelineRequest(url, {
headers: new Map([['host', url.hostname]]),
body: '',
@@ -160,7 +160,7 @@ describe('Rendering', () => {
org: 'adobe',
site: 'helix-pages',
ref: 'super-test',
- partition: 'live',
+ partition,
config,
path: selector ? `${url.pathname}${selector}.html` : url.pathname,
timer: {
@@ -174,7 +174,7 @@ describe('Rendering', () => {
}
// eslint-disable-next-line default-param-last
- async function testRender(url, domSelector = 'main', expStatus) {
+ async function testRender(url, domSelector = 'main', expStatus, partition = 'live') {
if (!(url instanceof URL)) {
// eslint-disable-next-line no-param-reassign
url = new URL(`https://helix-pages.com/${url}`);
@@ -191,7 +191,7 @@ describe('Rendering', () => {
// eslint-disable-next-line no-param-reassign
expStatus = expHtml === null ? 404 : 200;
}
- const response = await render(url, '', expStatus);
+ const response = await render(url, '', expStatus, partition);
const actHtml = response.body;
console.log(actHtml);
if (expStatus === 200) {
@@ -516,6 +516,8 @@ describe('Rendering', () => {
it('renders 404 if content not found', async () => {
await testRender('not-found', 'html');
+ // preview (code coverage)
+ await testRender('not-found', 'html', 404, 'preview');
});
it('can render empty table row', async () => {
@@ -593,12 +595,27 @@ describe('Rendering', () => {
'x-surrogate-key': 'gPHXKWdMY_R8KV2Z foo-id super-test--helix-pages--adobe_404 super-test--helix-pages--adobe_code QJqsV4atnOA47sHc',
});
assert.strictEqual(body.trim(), '');
+ // preview (code coverage)
+ const resp = await testRender(new URL('https://helix-pipeline.com/broken/folder'), 'html', 404, 'preview');
+ assert.deepStrictEqual(Object.fromEntries(resp.headers.entries()), {
+ 'access-control-allow-origin': '*',
+ 'content-type': 'text/html; charset=utf-8',
+ link: '; rel=modulepreload; as=script; crossorigin=use-credentials',
+ 'x-error': 'failed to load /not-a-page.md from content-bus: 404',
+ 'x-surrogate-key': 'gPHXKWdMY_R8KV2Z foo-id super-test--helix-pages--adobe_404 super-test--helix-pages--adobe_code p_gPHXKWdMY_R8KV2Z p_foo-id QJqsV4atnOA47sHc p_QJqsV4atnOA47sHc',
+ });
+ assert.strictEqual(resp.body.trim(), '');
});
it('renders 301 for redirect file', async () => {
loader.headers('one-section.md', 'x-amz-meta-redirect-location', 'https://www.adobe.com');
- const ret = await render(new URL('https://localhost/one-section'), '', 301);
- assert.strictEqual(ret.headers.get('location'), 'https://www.adobe.com');
+ let resp = await render(new URL('https://localhost/one-section'), '', 301);
+ assert.strictEqual(resp.headers.get('location'), 'https://www.adobe.com');
+ assert.strictEqual(resp.headers.get('x-surrogate-key'), 'oHjg_WDu20CBS4rD foo-id');
+ // preview (code coverage)
+ resp = await render(new URL('https://localhost/one-section'), '', 301, 'preview');
+ assert.strictEqual(resp.headers.get('location'), 'https://www.adobe.com');
+ assert.strictEqual(resp.headers.get('x-surrogate-key'), 'oHjg_WDu20CBS4rD foo-id p_oHjg_WDu20CBS4rD p_foo-id');
});
it('appends .plain.html in redirects', async () => {
@@ -640,6 +657,12 @@ describe('Rendering', () => {
let resp = await render(new URL('https://helix-pipeline.com/products'), '', 200);
assert.match(resp.body, //);
+ // coverage
+ loader.status('products.md', 404);
+ loader.status('generic-product.md', 200);
+ resp = await render(new URL('https://helix-pipeline.com/products'), '', 200, 'preview');
+ assert.match(resp.body, //);
+
loader.status('product1.md', 404);
loader.rewrite('generic-product/metadata.json', 'metadata-product.json');
resp = await render(new URL('https://helix-pipeline.com/products/product1'), '', 200);
diff --git a/test/sitemap-pipe.test.js b/test/sitemap-pipe.test.js
index e81815b9..8d78e807 100644
--- a/test/sitemap-pipe.test.js
+++ b/test/sitemap-pipe.test.js
@@ -74,7 +74,7 @@ describe('Sitemap Pipe Test', () => {
assert.deepStrictEqual(Object.fromEntries(resp.headers.entries()), {
'content-type': 'text/plain; charset=utf-8',
'x-error': 'failed to load /sitemap.xml from content-bus: 404',
- 'x-surrogate-key': 'RXei-6EcTEMTEIqi foobar_metadata ref--repo--owner_head foobar',
+ 'x-surrogate-key': 'RXei-6EcTEMTEIqi foobar_metadata ref--repo--owner_head foobar p_RXei-6EcTEMTEIqi p_foobar_metadata p_foobar',
});
});
@@ -92,7 +92,7 @@ describe('Sitemap Pipe Test', () => {
assert.deepStrictEqual(Object.fromEntries(resp.headers.entries()), {
'content-type': 'text/plain; charset=utf-8',
'x-error': 'Failed to parse /sitemap.json: Unexpected token \'h\', "this is not JSON" is not valid JSON',
- 'x-surrogate-key': 'RXei-6EcTEMTEIqi foobar_metadata ref--repo--owner_head foobar',
+ 'x-surrogate-key': 'RXei-6EcTEMTEIqi foobar_metadata ref--repo--owner_head foobar p_RXei-6EcTEMTEIqi p_foobar_metadata p_foobar',
});
});
@@ -110,7 +110,7 @@ describe('Sitemap Pipe Test', () => {
assert.deepStrictEqual(Object.fromEntries(resp.headers.entries()), {
'content-type': 'text/plain; charset=utf-8',
'x-error': "Expected 'data' array not found in /sitemap.json",
- 'x-surrogate-key': 'RXei-6EcTEMTEIqi foobar_metadata ref--repo--owner_head foobar',
+ 'x-surrogate-key': 'RXei-6EcTEMTEIqi foobar_metadata ref--repo--owner_head foobar p_RXei-6EcTEMTEIqi p_foobar_metadata p_foobar',
});
});
@@ -128,7 +128,7 @@ describe('Sitemap Pipe Test', () => {
assert.deepStrictEqual(Object.fromEntries(resp.headers.entries()), {
'content-type': 'application/xml; charset=utf-8',
'last-modified': 'Fri, 30 Apr 2021 03:47:18 GMT',
- 'x-surrogate-key': 'RXei-6EcTEMTEIqi foobar_metadata ref--repo--owner_head foobar',
+ 'x-surrogate-key': 'RXei-6EcTEMTEIqi foobar_metadata ref--repo--owner_head foobar p_RXei-6EcTEMTEIqi p_foobar_metadata p_foobar',
});
assert.strictEqual(resp.body, `
@@ -154,7 +154,7 @@ describe('Sitemap Pipe Test', () => {
assert.deepStrictEqual(Object.fromEntries(resp.headers.entries()), {
'content-type': 'application/xml; charset=utf-8',
'last-modified': 'Fri, 30 Apr 2021 03:47:18 GMT',
- 'x-surrogate-key': 'RXei-6EcTEMTEIqi foobar_metadata ref--repo--owner_head foobar',
+ 'x-surrogate-key': 'RXei-6EcTEMTEIqi foobar_metadata ref--repo--owner_head foobar p_RXei-6EcTEMTEIqi p_foobar_metadata p_foobar',
});
assert.strictEqual(resp.body, `
@@ -190,7 +190,7 @@ describe('Sitemap Pipe Test', () => {
assert.deepStrictEqual(Object.fromEntries(resp.headers.entries()), {
'content-type': 'application/xml; charset=utf-8',
'last-modified': 'Fri, 30 Apr 2021 03:47:18 GMT',
- 'x-surrogate-key': 'RXei-6EcTEMTEIqi foobar_metadata ref--repo--owner_head foobar',
+ 'x-surrogate-key': 'RXei-6EcTEMTEIqi foobar_metadata ref--repo--owner_head foobar p_RXei-6EcTEMTEIqi p_foobar_metadata p_foobar',
});
assert.strictEqual(resp.body, `