Skip to content

Commit

Permalink
Adding renderType flag to prerendered search endpoint (#2107)
Browse files Browse the repository at this point in the history
  • Loading branch information
FadhlanR authored Jan 31, 2025
1 parent 1f3544b commit 005b910
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -920,9 +920,6 @@ module('Integration | operator-mode', function (hooks) {
assert.dom(`[data-test-stack-card-index="0"]`).exists();
assert.dom(`[data-test-cards-grid-item]`).exists();

assert
.dom(`[data-test-cards-grid-item="${testRealmURL}BlogPost/1"]`)
.includesText('Blog Post');
assert
.dom(`[data-test-cards-grid-item="${testRealmURL}BlogPost/1"] `)
.includesText('Outer Space Journey');
Expand Down
16 changes: 8 additions & 8 deletions packages/realm-server/tests/realm-endpoints-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2452,35 +2452,35 @@ module(basename(__filename), function () {
assert.true(
json.data[0].attributes.html
.replace(/\s+/g, ' ')
.includes('Person Aaron'),
'embedded html looks correct (CardDef template)',
.includes('Embedded Card Person: Aaron'),
'embedded html looks correct (Person template)',
);

// 2nd card: Person Craig
assert.strictEqual(json.data[1].type, 'prerendered-card');
assert.true(
json.data[1].attributes.html
.replace(/\s+/g, ' ')
.includes('Person Craig'),
'embedded html for Craig looks correct (CardDef template)',
.includes('Embedded Card Person: Craig'),
'embedded html for Craig looks correct (Person template)',
);

// 3rd card: FancyPerson Jane
assert.strictEqual(json.data[2].type, 'prerendered-card');
assert.true(
json.data[2].attributes.html
.replace(/\s+/g, ' ')
.includes('FancyPerson Jane'),
'embedded html for Jane looks correct (CardDef template)',
.includes('Embedded Card FancyPerson: Jane'),
'embedded html for Jane looks correct (FancyPerson template)',
);

// 4th card: FancyPerson Jimmy
assert.strictEqual(json.data[3].type, 'prerendered-card');
assert.true(
json.data[3].attributes.html
.replace(/\s+/g, ' ')
.includes('FancyPerson Jimmy'),
'embedded html for Jimmy looks correct (CardDef template)',
.includes('Embedded Card FancyPerson: Jimmy'),
'embedded html for Jimmy looks correct (FancyPerson template)',
);

assertScopedCssUrlsContain(
Expand Down
1 change: 1 addition & 0 deletions packages/runtime-common/card-document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ export function transformResultsToPrerenderedCardsDoc(results: {
id: card.url,
attributes: {
html: card.html,
usedRenderType: card.usedRenderType,
...(card.isError ? { isError: true as const } : {}),
},
}));
Expand Down
148 changes: 118 additions & 30 deletions packages/runtime-common/index-query-engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ import {
fieldArity,
tableValuedFunctionsPlaceholder,
query,
dbExpression,
isDbExpression,
DBSpecificExpression,
Param,
} from './expression';
import {
type Query,
Expand Down Expand Up @@ -120,6 +123,7 @@ export type QueryOptions = WIPOptions & PrerenderedCardOptions;

interface PrerenderedCardOptions {
htmlFormat?: 'embedded' | 'fitted' | 'atom';
renderType?: ResolvedCodeRef;
includeErrors?: true;
cardUrls?: string[];
}
Expand All @@ -131,6 +135,7 @@ interface WIPOptions {
export interface PrerenderedCard {
url: string;
html: string | null;
usedRenderType: ResolvedCodeRef;
isError?: true;
}

Expand Down Expand Up @@ -461,47 +466,35 @@ export class IndexQueryEngine {
);
}

let ref: ResolvedCodeRef;
let filterOnValue = filter && 'type' in filter ? filter.type : filter?.on;
if (filterOnValue) {
ref = filterOnValue as ResolvedCodeRef;
} else {
ref = baseCardRef;
}

let htmlColumnExpression;
switch (opts.htmlFormat) {
case 'embedded':
htmlColumnExpression = [
'embedded_html ->> ',
param(internalKeyFor(ref, undefined)),
];
break;
case 'fitted':
htmlColumnExpression = [
'fitted_html ->> ',
param(internalKeyFor(ref, undefined)),
];
break;
case 'atom':
default:
htmlColumnExpression = ['atom_html'];
break;
}
let htmlColumnExpression = this.buildHtmlColumnExpression({
htmlFormat: opts.htmlFormat,
renderType: opts.renderType,
});
let usedRenderTypeColumnExpression =
this.buildUsedRenderTypeColumnExpression({
htmlFormat: opts.htmlFormat,
renderType: opts.renderType,
});

let { results, meta } = (await this._search(
realmURL,
{ filter, sort, page },
loader,
opts,
[
'SELECT url, ANY_VALUE(i.type) as type, ANY_VALUE(file_alias) as file_alias, ANY_VALUE(',
'SELECT url, ANY_VALUE(i.type) as type, ANY_VALUE(file_alias) as file_alias, ',
...htmlColumnExpression,
') as html, ANY_VALUE(deps) as deps',
' as html,',
...usedRenderTypeColumnExpression,
' as used_render_type,',
'ANY_VALUE(deps) as deps',
],
)) as {
meta: QueryResultsMeta;
results: (Partial<BoxelIndexTable> & { html: string | null })[];
results: (Partial<BoxelIndexTable> & {
html: string | null;
used_render_type: string;
})[];
};

// We need a way to get scoped css urls even from cards linked from foreign realms.These are saved in the deps column of instances and modules.
Expand All @@ -518,16 +511,111 @@ export class IndexQueryEngine {
}
});

let moduleNameSeparatorIndex = card.used_render_type.lastIndexOf('/');
return {
url: card.url!,
html: card.html,
usedRenderType: {
module: card.used_render_type.substring(0, moduleNameSeparatorIndex),
name: card.used_render_type.substring(moduleNameSeparatorIndex + 1),
},
...(card.type === 'error' ? { isError: true as const } : {}),
};
});

return { prerenderedCards, scopedCssUrls: [...scopedCssUrls], meta };
}

private buildHtmlColumnExpression({
htmlFormat,
renderType,
}: {
htmlFormat: 'embedded' | 'fitted' | 'atom' | undefined;
renderType?: ResolvedCodeRef;
}): (string | Param | DBSpecificExpression)[] {
let fieldName = htmlFormat ? `${htmlFormat}_html` : `atom_html`;
if (!htmlFormat || htmlFormat === 'atom') {
return [`ANY_VALUE(${fieldName})`];
}

let htmlColumnExpression = [];
htmlColumnExpression.push('COALESCE(');
if (renderType) {
htmlColumnExpression.push(`ANY_VALUE(${fieldName}) ->> `);
htmlColumnExpression.push(param(internalKeyFor(renderType, undefined)));
htmlColumnExpression.push(',');
}

htmlColumnExpression.push(
...[
`(
CASE WHEN ANY_VALUE(${fieldName}) IS NOT NULL AND `,
dbExpression({
pg: `jsonb_typeof(ANY_VALUE(${fieldName})) = 'object'`,
sqlite: `json_type(ANY_VALUE(${fieldName})) = 'object'`,
}),
` THEN ( SELECT value FROM `,
dbExpression({
pg: `jsonb_each_text(ANY_VALUE(${fieldName}))`,
sqlite: `json_each(ANY_VALUE(${fieldName}))`,
}),
` WHERE key = (SELECT replace(ANY_VALUE( `,
dbExpression({
pg: `types[0]::text`,
sqlite: `json_extract(types, '$[0]')`,
}),
`), '"', ''))) ELSE NULL END), NULL)`,
],
);

return htmlColumnExpression;
}

private buildUsedRenderTypeColumnExpression({
htmlFormat,
renderType,
}: {
htmlFormat: 'embedded' | 'fitted' | 'atom' | undefined;
renderType?: ResolvedCodeRef;
}): (string | Param | DBSpecificExpression)[] {
let usedRenderTypeColumnExpression = [];
if (htmlFormat && htmlFormat !== 'atom' && renderType) {
usedRenderTypeColumnExpression.push(`CASE`);
usedRenderTypeColumnExpression.push(
`WHEN ANY_VALUE(${htmlFormat}_html) ->> `,
);
usedRenderTypeColumnExpression.push(
param(internalKeyFor(renderType, undefined)),
);
usedRenderTypeColumnExpression.push(
`IS NOT NULL THEN '${internalKeyFor(renderType, undefined)}'`,
);
usedRenderTypeColumnExpression.push(
...[
`ELSE replace(ANY_VALUE(`,
dbExpression({
pg: `types[0]::text`,
sqlite: `json_extract(types, '$[0]')`,
}),
`), '"', '') END`,
],
);
} else {
usedRenderTypeColumnExpression.push(
...[
`replace(ANY_VALUE(`,
dbExpression({
pg: `types[0]::text`,
sqlite: `json_extract(types, '$[0]')`,
}),
`), '"', '')`,
],
);
}

return usedRenderTypeColumnExpression;
}

async fetchCardTypeSummary(realmURL: URL): Promise<CardTypeSummary[]> {
let results = (await this.#query([
`SELECT value
Expand Down
4 changes: 4 additions & 0 deletions packages/runtime-common/realm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
type QueuePublisher,
type FileMeta,
type DirectoryMeta,
type ResolvedCodeRef,
} from './index';
import merge from 'lodash/merge';
import mergeWith from 'lodash/mergeWith';
Expand Down Expand Up @@ -1645,6 +1646,7 @@ export class Realm {
let parsedQueryString = parseQuery(href);
let htmlFormat = parsedQueryString.prerenderedHtmlFormat as string;
let cardUrls = parsedQueryString.cardUrls as string[];
let renderType = parsedQueryString.renderType as ResolvedCodeRef;

if (!isValidPrerenderedHtmlFormat(htmlFormat)) {
return badRequest(
Expand All @@ -1659,6 +1661,7 @@ export class Realm {
// prerenderedHtmlFormat and cardUrls are special parameters only for this endpoint so don't include it in our Query for standard card search
delete parsedQueryString.prerenderedHtmlFormat;
delete parsedQueryString.cardUrls;
delete parsedQueryString.renderType;

let cardsQuery = parsedQueryString;
assertQuery(parsedQueryString);
Expand All @@ -1669,6 +1672,7 @@ export class Realm {
useWorkInProgressIndex,
htmlFormat,
cardUrls,
renderType,
includeErrors: true,
},
);
Expand Down
Loading

0 comments on commit 005b910

Please sign in to comment.