Skip to content

Commit 274f077

Browse files
Merge pull request #703 from adobe/scoped-content-keys
fix: scoped keys for prefix content (DON'T BACKPORT TO V5)
2 parents d5abe46 + 41ec5ec commit 274f077

File tree

7 files changed

+147
-26
lines changed

7 files changed

+147
-26
lines changed

src/json-pipe.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,13 @@ async function fetchJsonContent(state, req, res) {
6565
if (state.content.sourceBus === 'content') {
6666
keys.push(await computeSurrogateKey(`${contentBusId}${info.path}`));
6767
keys.push(contentBusId);
68+
const contentKeyPrefix = partition === 'preview' ? 'p_' : '';
69+
if (partition === 'preview') {
70+
// temporarily provide additional preview content keys
71+
// TODO: eventually provide either (prefixed) preview or (unprefixed) live content keys
72+
keys.push(`${contentKeyPrefix}${await computeSurrogateKey(`${contentBusId}${info.path}`)}`);
73+
keys.push(`${contentKeyPrefix}${contentBusId}`);
74+
}
6875
} else {
6976
keys.push(`${ref}--${repo}--${owner}_code`);
7077
keys.push(await computeSurrogateKey(`${ref}--${repo}--${owner}${info.path}`));
@@ -104,6 +111,13 @@ async function computeSurrogateKeys(state) {
104111
if (state.content.sourceBus.includes('content')) {
105112
keys.push(await computeSurrogateKey(`${state.contentBusId}${state.info.path}`));
106113
keys.push(state.contentBusId);
114+
const contentKeyPrefix = state.partition === 'preview' ? 'p_' : '';
115+
if (state.partition === 'preview') {
116+
// temporarily provide additional preview content keys
117+
// TODO: eventually provide either (prefixed) preview or (unprefixed) live content keys
118+
keys.push(`${contentKeyPrefix}${await computeSurrogateKey(`${state.contentBusId}${state.info.path}`)}`);
119+
keys.push(`${contentKeyPrefix}${state.contentBusId}`);
120+
}
107121
}
108122

109123
return keys;

src/steps/fetch-404.js

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { getPathKey } from './set-x-surrogate-key-header.js';
2222
*/
2323
export default async function fetch404(state, req, res) {
2424
const {
25-
owner, repo, ref, contentBusId,
25+
owner, repo, ref, contentBusId, partition,
2626
} = state;
2727
const ret = await state.s3Loader.getObject('helix-code-bus', `${owner}/${repo}/${ref}/404.html`);
2828
if (ret.status === 200) {
@@ -46,12 +46,25 @@ export default async function fetch404(state, req, res) {
4646
`${ref}--${repo}--${owner}_404`,
4747
`${ref}--${repo}--${owner}_code`,
4848
];
49+
const contentKeyPrefix = partition === 'preview' ? 'p_' : '';
50+
if (partition === 'preview') {
51+
// temporarily provide additional preview content keys
52+
// TODO: eventually provide either (prefixed) preview or (unprefixed) live content keys
53+
keys.push(`${contentKeyPrefix}${pathKey}`);
54+
keys.push(`${contentKeyPrefix}${contentBusId}`);
55+
}
4956

5057
if (state.info.unmappedPath) {
51-
keys.push(await getPathKey({
58+
const unmappedPathKey = await getPathKey({
5259
contentBusId,
5360
info: { path: state.info.unmappedPath },
54-
}));
61+
});
62+
keys.push(unmappedPathKey);
63+
if (partition === 'preview') {
64+
// temporarily provide additional preview content key
65+
// TODO: eventually provide either (prefixed) preview or (unprefixed) live content key
66+
keys.push(`${contentKeyPrefix}${unmappedPathKey}`);
67+
}
5568
}
5669

5770
res.headers.set('x-surrogate-key', keys.join(' '));

src/steps/fetch-content.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ export default async function fetchContent(state, req, res) {
4848
} else {
4949
keys.push(await computeSurrogateKey(`${contentBusId}${info.path}`));
5050
keys.push(contentBusId);
51+
const contentKeyPrefix = partition === 'preview' ? 'p_' : '';
52+
if (partition === 'preview') {
53+
// temporarily provide additional preview content keys
54+
// TODO: eventually provide either (prefixed) preview or (unprefixed) live content keys
55+
keys.push(`${contentKeyPrefix}${await computeSurrogateKey(`${contentBusId}${info.path}`)}`);
56+
keys.push(`${contentKeyPrefix}${contentBusId}`);
57+
}
5158
}
5259
res.headers.set('x-surrogate-key', keys.join(' '));
5360
res.error = 'moved';

src/steps/set-x-surrogate-key-header.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,12 @@ export async function getPathKey(state) {
4242
*/
4343
export default async function setXSurrogateKeyHeader(state, req, res) {
4444
const {
45-
contentBusId, owner, repo, ref,
45+
contentBusId, owner, repo, ref, partition,
4646
} = state;
4747

4848
const isCode = state.content.sourceBus === 'code';
4949

50+
const contentKeyPrefix = partition === 'preview' ? 'p_' : '';
5051
const keys = [];
5152
const hash = await getPathKey(state);
5253
if (isCode) {
@@ -57,6 +58,13 @@ export default async function setXSurrogateKeyHeader(state, req, res) {
5758
keys.push(`${contentBusId}_metadata`);
5859
keys.push(`${ref}--${repo}--${owner}_head`);
5960
keys.push(contentBusId);
61+
if (partition === 'preview') {
62+
// temporarily provide additional preview content keys
63+
// TODO: eventually provide either (prefixed) preview or (unprefixed) live content keys
64+
keys.push(`${contentKeyPrefix}${hash}`);
65+
keys.push(`${contentKeyPrefix}${contentBusId}_metadata`);
66+
keys.push(`${contentKeyPrefix}${contentBusId}`);
67+
}
6068
}
6169
// for folder-mapped resources, we also need to include the surrogate key of the mapped metadata
6270
if (state.mapped) {
@@ -67,6 +75,17 @@ export default async function setXSurrogateKeyHeader(state, req, res) {
6775
info: { path: state.info.unmappedPath },
6876
}));
6977
}
78+
if (partition === 'preview') {
79+
// temporarily provide additional preview content keys
80+
// TODO: eventually provide either (prefixed) preview or (unprefixed) live content keys
81+
keys.push(`${contentKeyPrefix}${hash}_metadata`);
82+
if (state.info.unmappedPath) {
83+
keys.push(`${contentKeyPrefix}${await getPathKey({
84+
contentBusId,
85+
info: { path: state.info.unmappedPath },
86+
})}`);
87+
}
88+
}
7089
}
7190
res.headers.set('x-surrogate-key', keys.join(' '));
7291
}

test/json-pipe.test.js

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,17 @@ describe('JSON Pipe Test', () => {
108108
'last-modified': 'Wed, 12 Oct 2009 17:50:00 GMT',
109109
},
110110
}),
111+
)
112+
.reply(
113+
'helix-content-bus',
114+
'foobar/live/en/index.json',
115+
new PipelineResponse(TEST_SINGLE_SHEET, {
116+
headers: {
117+
'content-type': 'application/json',
118+
'x-amz-meta-x-source-location': 'foo-bar',
119+
'last-modified': 'Wed, 12 Oct 2009 17:50:00 GMT',
120+
},
121+
}),
111122
),
112123
timer: {
113124
update: () => {},
@@ -126,7 +137,23 @@ describe('JSON Pipe Test', () => {
126137

127138
it('fetches correct content', async () => {
128139
const state = createDefaultState();
129-
const resp = await jsonPipe(state, new PipelineRequest('https://json-filter.com/?limit=10&offset=5'));
140+
let resp = await jsonPipe(state, new PipelineRequest('https://json-filter.com/?limit=10&offset=5'));
141+
assert.strictEqual(resp.status, 200);
142+
assert.deepStrictEqual(await resp.json(), {
143+
':type': 'sheet',
144+
offset: 5,
145+
limit: 10,
146+
total: TEST_DATA.length,
147+
data: TEST_DATA.slice(5, 15),
148+
});
149+
assert.deepStrictEqual(Object.fromEntries(resp.headers.entries()), {
150+
'content-type': 'application/json',
151+
'x-surrogate-key': 'Atrz_qDg26DmSe9a foobar p_Atrz_qDg26DmSe9a p_foobar',
152+
'last-modified': 'Wed, 12 Oct 2009 17:50:00 GMT',
153+
});
154+
// live (code coverage)
155+
state.partition = 'live';
156+
resp = await jsonPipe(state, new PipelineRequest('https://json-filter.com/?limit=10&offset=5'));
130157
assert.strictEqual(resp.status, 200);
131158
assert.deepStrictEqual(await resp.json(), {
132159
':type': 'sheet',
@@ -148,7 +175,7 @@ describe('JSON Pipe Test', () => {
148175
assert.strictEqual(resp.status, 404);
149176
assert.deepStrictEqual(Object.fromEntries(resp.headers.entries()), {
150177
'x-error': 'failed to load /config.json: 404',
151-
'x-surrogate-key': 'U_NW4adJU7Qazf-I pzrU-nNKQOUYNTEf ref--repo--owner_code kz8SoCaNqfp4ohQo foobar',
178+
'x-surrogate-key': 'U_NW4adJU7Qazf-I pzrU-nNKQOUYNTEf ref--repo--owner_code kz8SoCaNqfp4ohQo foobar p_kz8SoCaNqfp4ohQo p_foobar',
152179
});
153180
});
154181

@@ -165,7 +192,7 @@ describe('JSON Pipe Test', () => {
165192
assert.strictEqual(resp.status, 200);
166193
assert.deepStrictEqual(Object.fromEntries(resp.headers.entries()), {
167194
'content-type': 'application/json',
168-
'x-surrogate-key': 'U_NW4adJU7Qazf-I pzrU-nNKQOUYNTEf ref--repo--owner_code kz8SoCaNqfp4ohQo foobar',
195+
'x-surrogate-key': 'U_NW4adJU7Qazf-I pzrU-nNKQOUYNTEf ref--repo--owner_code kz8SoCaNqfp4ohQo foobar p_kz8SoCaNqfp4ohQo p_foobar',
169196
});
170197
assert.deepStrictEqual(await resp.json(), {
171198
public: {
@@ -193,7 +220,7 @@ describe('JSON Pipe Test', () => {
193220
});
194221
assert.deepStrictEqual(Object.fromEntries(resp.headers.entries()), {
195222
'content-type': 'application/json',
196-
'x-surrogate-key': 'Atrz_qDg26DmSe9a foobar',
223+
'x-surrogate-key': 'Atrz_qDg26DmSe9a foobar p_Atrz_qDg26DmSe9a p_foobar',
197224
'last-modified': 'Wed, 12 Oct 2009 17:50:00 GMT',
198225
});
199226
});
@@ -214,7 +241,7 @@ describe('JSON Pipe Test', () => {
214241
'access-control-allow-origin': '*',
215242
'content-security-policy': 'default-src \'self\'',
216243
'last-modified': 'Wed, 12 Oct 2009 17:50:00 GMT',
217-
'x-surrogate-key': 'Atrz_qDg26DmSe9a foobar',
244+
'x-surrogate-key': 'Atrz_qDg26DmSe9a foobar p_Atrz_qDg26DmSe9a p_foobar',
218245
'content-type': 'application/json',
219246
});
220247
});
@@ -229,7 +256,7 @@ describe('JSON Pipe Test', () => {
229256
'access-control-allow-origin': '*',
230257
'content-security-policy': 'default-src \'self\'',
231258
'x-error': 'failed to load /en/index.json: 404',
232-
'x-surrogate-key': 'SIMSxecp2CJXqGYs ref--repo--owner_code Atrz_qDg26DmSe9a foobar',
259+
'x-surrogate-key': 'SIMSxecp2CJXqGYs ref--repo--owner_code Atrz_qDg26DmSe9a foobar p_Atrz_qDg26DmSe9a p_foobar',
233260
});
234261
});
235262

@@ -244,8 +271,26 @@ describe('JSON Pipe Test', () => {
244271
'x-amz-meta-redirect-location': '/de/index.json',
245272
},
246273
}),
247-
);
248-
const resp = await jsonPipe(state, new PipelineRequest('https://json-filter.com/?limit=10&offset=5'));
274+
)
275+
.reply(
276+
'helix-content-bus',
277+
'foobar/live/en/index.json',
278+
new PipelineResponse(TEST_SINGLE_SHEET, {
279+
headers: {
280+
'content-type': 'application/json',
281+
'x-amz-meta-redirect-location': '/de/index.json',
282+
},
283+
}),
284+
);
285+
let resp = await jsonPipe(state, new PipelineRequest('https://json-filter.com/?limit=10&offset=5'));
286+
assert.strictEqual(resp.status, 301);
287+
assert.deepStrictEqual(Object.fromEntries(resp.headers.entries()), {
288+
'location': '/de/index.json',
289+
'x-surrogate-key': 'Atrz_qDg26DmSe9a foobar p_Atrz_qDg26DmSe9a p_foobar',
290+
});
291+
// live (code coverage)
292+
state.partition = 'live';
293+
resp = await jsonPipe(state, new PipelineRequest('https://json-filter.com/?limit=10&offset=5'));
249294
assert.strictEqual(resp.status, 301);
250295
assert.deepStrictEqual(Object.fromEntries(resp.headers.entries()), {
251296
'location': '/de/index.json',
@@ -301,7 +346,7 @@ describe('JSON Pipe Test', () => {
301346
const headers = Object.fromEntries(resp.headers.entries());
302347
assert.deepStrictEqual(headers, {
303348
'last-modified': 'Wed, 12 Oct 2009 17:50:00 GMT',
304-
'x-surrogate-key': 'Atrz_qDg26DmSe9a foobar',
349+
'x-surrogate-key': 'Atrz_qDg26DmSe9a foobar p_Atrz_qDg26DmSe9a p_foobar',
305350
'content-type': 'application/json',
306351
});
307352
});
@@ -333,7 +378,7 @@ describe('JSON Pipe Test', () => {
333378
const headers = Object.fromEntries(resp.headers.entries());
334379
assert.deepStrictEqual(headers, {
335380
'last-modified': 'Wed, 12 Oct 2009 15:50:00 GMT',
336-
'x-surrogate-key': 'Atrz_qDg26DmSe9a foobar',
381+
'x-surrogate-key': 'Atrz_qDg26DmSe9a foobar p_Atrz_qDg26DmSe9a p_foobar',
337382
'content-type': 'application/json',
338383
});
339384
});

test/rendering.test.js

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ describe('Rendering', () => {
148148
config = DEFAULT_CONFIG;
149149
});
150150

151-
async function render(url, selector = '', expectedStatus = 200) {
151+
async function render(url, selector = '', expectedStatus = 200, partition = 'live') {
152152
const req = new PipelineRequest(url, {
153153
headers: new Map([['host', url.hostname]]),
154154
body: '',
@@ -160,7 +160,7 @@ describe('Rendering', () => {
160160
org: 'adobe',
161161
site: 'helix-pages',
162162
ref: 'super-test',
163-
partition: 'live',
163+
partition,
164164
config,
165165
path: selector ? `${url.pathname}${selector}.html` : url.pathname,
166166
timer: {
@@ -174,7 +174,7 @@ describe('Rendering', () => {
174174
}
175175

176176
// eslint-disable-next-line default-param-last
177-
async function testRender(url, domSelector = 'main', expStatus) {
177+
async function testRender(url, domSelector = 'main', expStatus, partition = 'live') {
178178
if (!(url instanceof URL)) {
179179
// eslint-disable-next-line no-param-reassign
180180
url = new URL(`https://helix-pages.com/${url}`);
@@ -191,7 +191,7 @@ describe('Rendering', () => {
191191
// eslint-disable-next-line no-param-reassign
192192
expStatus = expHtml === null ? 404 : 200;
193193
}
194-
const response = await render(url, '', expStatus);
194+
const response = await render(url, '', expStatus, partition);
195195
const actHtml = response.body;
196196
console.log(actHtml);
197197
if (expStatus === 200) {
@@ -516,6 +516,8 @@ describe('Rendering', () => {
516516

517517
it('renders 404 if content not found', async () => {
518518
await testRender('not-found', 'html');
519+
// preview (code coverage)
520+
await testRender('not-found', 'html', 404, 'preview');
519521
});
520522

521523
it('can render empty table row', async () => {
@@ -593,12 +595,27 @@ describe('Rendering', () => {
593595
'x-surrogate-key': 'gPHXKWdMY_R8KV2Z foo-id super-test--helix-pages--adobe_404 super-test--helix-pages--adobe_code QJqsV4atnOA47sHc',
594596
});
595597
assert.strictEqual(body.trim(), '');
598+
// preview (code coverage)
599+
const resp = await testRender(new URL('https://helix-pipeline.com/broken/folder'), 'html', 404, 'preview');
600+
assert.deepStrictEqual(Object.fromEntries(resp.headers.entries()), {
601+
'access-control-allow-origin': '*',
602+
'content-type': 'text/html; charset=utf-8',
603+
link: '</scripts/scripts.js>; rel=modulepreload; as=script; crossorigin=use-credentials',
604+
'x-error': 'failed to load /not-a-page.md from content-bus: 404',
605+
'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',
606+
});
607+
assert.strictEqual(resp.body.trim(), '');
596608
});
597609

598610
it('renders 301 for redirect file', async () => {
599611
loader.headers('one-section.md', 'x-amz-meta-redirect-location', 'https://www.adobe.com');
600-
const ret = await render(new URL('https://localhost/one-section'), '', 301);
601-
assert.strictEqual(ret.headers.get('location'), 'https://www.adobe.com');
612+
let resp = await render(new URL('https://localhost/one-section'), '', 301);
613+
assert.strictEqual(resp.headers.get('location'), 'https://www.adobe.com');
614+
assert.strictEqual(resp.headers.get('x-surrogate-key'), 'oHjg_WDu20CBS4rD foo-id');
615+
// preview (code coverage)
616+
resp = await render(new URL('https://localhost/one-section'), '', 301, 'preview');
617+
assert.strictEqual(resp.headers.get('location'), 'https://www.adobe.com');
618+
assert.strictEqual(resp.headers.get('x-surrogate-key'), 'oHjg_WDu20CBS4rD foo-id p_oHjg_WDu20CBS4rD p_foo-id');
602619
});
603620

604621
it('appends .plain.html in redirects', async () => {
@@ -640,6 +657,12 @@ describe('Rendering', () => {
640657
let resp = await render(new URL('https://helix-pipeline.com/products'), '', 200);
641658
assert.match(resp.body, /<meta property="og:url" content="https:\/\/www.adobe.com\/products">/);
642659

660+
// coverage
661+
loader.status('products.md', 404);
662+
loader.status('generic-product.md', 200);
663+
resp = await render(new URL('https://helix-pipeline.com/products'), '', 200, 'preview');
664+
assert.match(resp.body, /<meta property="og:url" content="https:\/\/www.adobe.com\/products">/);
665+
643666
loader.status('product1.md', 404);
644667
loader.rewrite('generic-product/metadata.json', 'metadata-product.json');
645668
resp = await render(new URL('https://helix-pipeline.com/products/product1'), '', 200);

0 commit comments

Comments
 (0)