Skip to content

Commit 9764b29

Browse files
committed
utility to handle content sub requests (+ related typings)
1 parent 15e7589 commit 9764b29

File tree

6 files changed

+88
-37
lines changed

6 files changed

+88
-37
lines changed

apps/seven/app/root.tsx

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ import type { Route } from './+types/root';
66
import { flattenToAppURL } from '@plone/helpers';
77
import type PloneClient from '@plone/client';
88
import config from '@plone/registry';
9-
import type { Content, ListingBlockFormData } from '@plone/types';
9+
import type {
10+
LoaderDataUtilityMethod,
11+
ContentSubRequestUtilityMethod,
12+
} from '@plone/types';
1013
import {
1114
getAPIResourceWithAuth,
1215
installServerMiddleware,
@@ -33,35 +36,30 @@ export async function loader({ params, request }: Route.LoaderArgs) {
3336

3437
const path = `/${params['*'] || ''}`;
3538

36-
const rootLoaderDataUtilities = config.getUtilities({
39+
const rootLoaderDataUtilities = config.getUtilities<LoaderDataUtilityMethod>({
3740
type: 'rootLoaderData',
38-
}) as Array<{
39-
method: (args: {
40-
cli: PloneClient;
41-
content: Content;
42-
request: typeof request;
43-
path: string;
44-
params: typeof params;
45-
locale: string;
46-
}) => Promise<{ status: number; data: unknown }>;
47-
}>;
41+
});
42+
43+
const rootContentSubRequests =
44+
config.getUtilities<ContentSubRequestUtilityMethod>({
45+
type: 'rootContentSubRequest',
46+
});
4847

4948
try {
5049
const [content, site] = await Promise.all([
5150
cli.getContent({ path, expand }),
5251
cli.getSite(),
5352
]);
5453

55-
const listingBlocks: { id: string; block: ListingBlockFormData }[] =
56-
Object.entries(content.data.blocks)
57-
.filter(([, block]) => block['@type'] === 'listing')
58-
.map(([id, block]) => ({ id, block: block as ListingBlockFormData }));
59-
for (let i = 0; i < listingBlocks.length; i++) {
60-
const { id, block } = listingBlocks[i];
61-
if (block.querystring) {
62-
const results = await cli.querystringSearch(block.querystring);
63-
content.data.blocks[id].items = results.data.items;
64-
}
54+
for (const utility of rootContentSubRequests) {
55+
await utility.method({
56+
cli,
57+
content: content.data,
58+
request,
59+
path,
60+
params,
61+
locale,
62+
});
6563
}
6664

6765
const rootLoaderDataUtilitiesData = await Promise.all([

packages/blocks/index.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { ConfigType } from '@plone/registry';
2+
import type { ListingBlockFormData, LoaderUtilityArgs } from '@plone/types';
23

34
import TitleBlockInfo from './Title';
45
import TextBlockInfo from './Text';
@@ -19,5 +20,23 @@ export default function install(config: ConfigType) {
1920
config.blocks.blocksConfig.teaser = TeaserBlockInfo;
2021
config.blocks.blocksConfig.listing = ListingBlockInfo;
2122

23+
config.registerUtility({
24+
name: 'ListingBlocksContentLoading',
25+
type: 'rootContentSubRequest',
26+
method: async (args: LoaderUtilityArgs) => {
27+
const listingBlocks: { id: string; block: ListingBlockFormData }[] =
28+
Object.entries(args.content.blocks)
29+
.filter(([, block]) => block['@type'] === 'listing')
30+
.map(([id, block]) => ({ id, block: block as ListingBlockFormData }));
31+
for (let i = 0; i < listingBlocks.length; i++) {
32+
const { id, block } = listingBlocks[i];
33+
if (block.querystring) {
34+
const results = await args.cli.querystringSearch(block.querystring);
35+
args.content.blocks[id].items = results.data.items;
36+
}
37+
}
38+
},
39+
});
40+
2241
return config;
2342
}

packages/registry/src/index.ts

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import type {
1515
WidgetsConfig,
1616
ReactRouterRouteEntry,
1717
WidgetKey,
18+
UtilityMethod,
19+
DummyUtilityMethod,
1820
} from '@plone/types';
1921

2022
export type ConfigData = {
@@ -35,10 +37,6 @@ type GetComponentResult = {
3537
component: React.ComponentType<any>;
3638
};
3739

38-
type GetUtilityResult = {
39-
method: (...args: any[]) => any;
40-
};
41-
4240
export type ConfigType = InstanceType<typeof Config>;
4341
// This type is used to map widget keys to their definitions.
4442
type MappableWidgetKeys = {
@@ -459,11 +457,11 @@ class Config {
459457
}
460458
}
461459

462-
registerUtility(options: {
460+
registerUtility<T extends UtilityMethod = DummyUtilityMethod>(options: {
463461
name: string;
464462
type: string;
465463
dependencies?: Record<string, string>;
466-
method: (args: any) => any;
464+
method: T;
467465
}) {
468466
const { name, type, method, dependencies = {} } = options;
469467
let depsString: string = '';
@@ -485,11 +483,11 @@ class Config {
485483
utilityType[utilityName] = { method };
486484
}
487485

488-
getUtility(options: {
486+
getUtility<T extends UtilityMethod = DummyUtilityMethod>(options: {
489487
name: string;
490488
type: string;
491489
dependencies?: Record<string, string>;
492-
}): GetUtilityResult | Record<string, never> {
490+
}): { method: T } | Record<string, never> {
493491
const { name, type, dependencies = {} } = options;
494492

495493
if (!name || !type) return {};
@@ -501,13 +499,13 @@ class Config {
501499

502500
const utilityName = `${depsString ? `|${depsString}` : ''}${name}`;
503501

504-
return this._data.utilities[type]?.[utilityName] || {};
502+
return (this._data.utilities[type]?.[utilityName] as { method: T }) || {};
505503
}
506504

507-
getUtilities(options: {
505+
getUtilities<T extends UtilityMethod = DummyUtilityMethod>(options: {
508506
type: string;
509507
dependencies?: Record<string, string>;
510-
}): Array<GetUtilityResult> | [] {
508+
}): Array<{ method: T }> | [] {
511509
const { type, dependencies = {} } = options;
512510

513511
if (!type) return [];
@@ -525,7 +523,7 @@ class Config {
525523
(key) => this._data.utilities[type][key],
526524
);
527525

528-
return utilities;
526+
return utilities as Array<{ method: T }>;
529527
}
530528

531529
registerRoute(options: ReactRouterRouteEntry) {

packages/types/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,12 @@
4949
"devDependencies": {
5050
"@types/react": "catalog:",
5151
"@types/react-dom": "catalog:",
52+
"react-router": "catalog:",
5253
"release-it": "catalog:",
5354
"tsconfig": "workspace:*",
5455
"typescript": "catalog:"
56+
},
57+
"dependencies": {
58+
"@plone/client": "workspace:*"
5559
}
5660
}
Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,31 @@
1-
// export type Utility = Record<string, { method: (args: any) => any }>;
2-
export type Utility = Record<string, { method: (...args: any[]) => any }>;
1+
import { Content } from '../content';
2+
import type { Params } from 'react-router';
3+
import type PloneClient from '@plone/client';
4+
5+
export type Utility = Record<string, { method: UtilityMethod }>;
36

47
export type UtilitiesConfig = Record<string, Utility>;
8+
9+
export type UtilityMethod =
10+
| DummyUtilityMethod
11+
| LoaderDataUtilityMethod
12+
| ContentSubRequestUtilityMethod;
13+
14+
// TODO: remove when all utilities are properly typed
15+
export type DummyUtilityMethod = (...args: any[]) => any;
16+
17+
export type LoaderDataUtilityMethod = (
18+
args: LoaderUtilityArgs,
19+
) => Promise<{ status: number; data: unknown }>;
20+
export type ContentSubRequestUtilityMethod = (
21+
args: LoaderUtilityArgs,
22+
) => Promise<undefined>;
23+
24+
export interface LoaderUtilityArgs {
25+
cli: PloneClient;
26+
content: Content;
27+
request: Request;
28+
path: string;
29+
params: Params;
30+
locale: string;
31+
}

pnpm-lock.yaml

Lines changed: 6 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)