Skip to content

Let add-ons add expansions to the content request, like Volto's apiExpanders #226

Description

@sneridagh

Summary

Aurora fetches the current page's content once per request, in the fetchPloneContent middleware, with a fixed list of expansions:

const expand = ['navroot', 'breadcrumbs', 'navigation', 'actions'];
// ...
if (userId) expand.push('types');
// ...
cli.getContent({ path, expand }),

(apps/aurora/app/middleware.server.ts#L140-L175)

An add-on that publishes an expandable plone.restapi component cannot add it to that list. It has to make a request of its own, and the obvious place, a rootLoaderData utility, runs on every page. Volto solves this with config.settings.apiExpanders: add-ons declare, by path, which components ride along on the content request.

The use case

pas.plugins.identity, ported to Aurora in collective/pas-plugins-identity#132, has a profile gate: a signed-in user whose profile is missing required fields is held until it is complete.

To decide, it needs the user's @my-profile on every page. The backend offers my-profile as an expandable component for exactly this reason. In Volto, the add-on adds it to apiExpanders, so it costs nothing:

config.settings.apiExpanders = [
  ...config.settings.apiExpanders,
  { match: '', GET_CONTENT: ['my-profile'] },
];

In Aurora it is a rootLoaderData utility making one extra request per signed-in page view:

config.registerUtility({
  name: 'IdentityProfileGate',
  type: 'rootLoaderData',
  method: async ({ request }) => {
    const token = await getAuthFromRequest(request);
    if (!token) return { status: 200, data: {} };
    const answer = await callBackend(request, endpoints.myProfile(), { token });
    return { status: 200, data: { identityProfile: await answer.json() } };
  },
});

(source)

A rootContentSubRequest utility (apps/aurora/app/config/server.server.ts#L25) cannot help either: it runs after the content request and can only add requests of its own.

Proposal

A declarative way for add-ons to add expansions to the content request. Either a setting shaped like Volto's:

config.settings.apiExpanders = [
  ...(config.settings.apiExpanders ?? []),
  { match: '', GET_CONTENT: ['my-profile'], authenticated: true },
];

or a utility type, contentExpanders, whose method receives { request, path, userId } and returns the names to add.

fetchPloneContent then merges them into expand, and the result is available to loaders and slots as content['@components'][name], as it already is for navigation or breadcrumbs.

Details worth deciding:

  • Signed-in only. Volto's apiExpanders cannot say "signed-in users only": the anonymous request carries the parameter too, and the backend answers it with nothing. Since the middleware already knows userId (it uses it for types), an authenticated flag would keep anonymous URLs, and so their cache keys, unchanged.
  • Matched by path, like Volto's match, so an add-on can expand only below a section.
  • The 401 retry. The anonymous retry already filters out types. Signed-in-only expansions should be dropped there too.

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions