Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 6 additions & 20 deletions __test__/test-website/src/app/related/[...slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { GraphClient } from '@optimizely/cms-sdk';
import { OptimizelyComponent } from '@optimizely/cms-sdk/react/server';
import React from 'react';

type Props = {
Expand All @@ -17,38 +16,25 @@ export default async function Page({ params }: Props) {
graphUrl: process.env.OPTIMIZELY_GRAPH_URL,
});

const children = [
...((await client.getLinksByPath(`/${slug.join('/')}/`)) ?? []),
// NOTE: if you are using "simple address", you should fetch without trailing slash:
...((await client.getLinksByPath(`/${slug.join('/')}`)) ?? []),
];
const ancestors = [
...((await client.getLinksByPath(`/${slug.join('/')}/`, {
type: 'PATH',
})) ?? []),
// Same here:
...((await client.getLinksByPath(`/${slug.join('/')}`, {
type: 'PATH',
})) ?? []),
];
// .catch(handleGraphErrors);
const children = (await client.getItems(`/${slug.join('/')}`)) ?? [];
const ancestors = (await client.getPath(`/${slug.join('/')}`)) ?? [];

return (
<div>
<h1>Links from this page</h1>
<h2>Children</h2>
<ul>
{children?.map((l) => (
<li>
{l?.displayName} ({l?.url?.default})
<li key={l._metadata?.key}>
{l?._metadata?.displayName} ({l?._metadata?.url?.default})
</li>
))}
</ul>
<h2>Ancestors (breadcrumbs)</h2>
<ol>
{ancestors?.map((l) => (
<li>
{l?.displayName} ({l?.url?.default})
<li key={l._metadata?.key}>
{l?._metadata?.displayName} ({l?._metadata?.url?.default})
</li>
))}
</ol>
Expand Down
7 changes: 7 additions & 0 deletions packages/optimizely-cms-sdk/src/graph/filters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,17 @@ export function variationFilter(value: string): ContentInput {
};
}

export function localeFilter(locale?: string[]): ContentInput {
return {
locale,
};
}

/**
* Arguments for querying content via the Graph API.
*/
export type ContentInput = {
locale?: string[];
variation?: GraphVariationInput;
where?: ContentWhereInput;
};
Expand Down
109 changes: 79 additions & 30 deletions packages/optimizely-cms-sdk/src/graph/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
pathFilter,
previewFilter,
GraphVariationInput,
localeFilter,
} from './filters.js';

/** Options for Graph */
Expand All @@ -35,7 +36,8 @@ export type GraphGetContentOptions = {
};

export type GraphGetLinksOptions = {
type?: 'DEFAULT' | 'ITEMS' | 'ASSETS' | 'PATH';
host?: string;
locales?: string[];
};

export { GraphVariationInput };
Expand All @@ -53,15 +55,42 @@ query GetContentMetadata($where: _ContentWhereInput, $variation: VariationInput)
}
`;

const GET_LINKS_QUERY = `
query GetLinks($where: _ContentWhereInput, $type: LinkTypes) {
_Content(where: $where) {
const GET_PATH_QUERY = `
query GetPath($where: _ContentWhereInput, $locale: [Locale]) {
_Content(where: $where, locale: $locale) {
item {
_id
_link(type: $type) {
_link(type: PATH) {
_Page {
items {
_metadata {
key
sortOrder
displayName
locale
types
url {
hierarchical
default
}
}
}
}
}
}
}
}`;

const GET_ITEMS_QUERY = `
query GetPath($where: _ContentWhereInput, $locale: [Locale]) {
_Content(where: $where, locale: $locale) {
item {
_id
_link(type: ITEMS) {
_Page {
items {
_metadata {
key
sortOrder
displayName
locale
Expand All @@ -86,6 +115,7 @@ type GetLinksResponse = {
_Page: {
items: Array<{
_metadata?: {
key: string;
sortOrder?: number;
displayName?: string;
locale?: string;
Expand Down Expand Up @@ -254,41 +284,60 @@ export class GraphClient {
return response?._Content?.items;
}

async getLinksByPath(path: string, options?: GraphGetLinksOptions) {
const input = {
...pathFilter(path),
type: options?.type,
};
const data = (await this.request(
GET_LINKS_QUERY,
input
)) as GetLinksResponse;
/**
* Given the path of a page, return its "path" (i.e. a list of ancestor pages).
*
* @param path The URL of the current page
* @returns A list with the metadata information of all ancestors sorted
* from the top-most to the current
*/
async getPath(path: string, options?: GraphGetLinksOptions) {
const data = (await this.request(GET_PATH_QUERY, {
...pathFilter(path, options?.host),
...localeFilter(options?.locales),
})) as GetLinksResponse;

// Check if the page itself exist.
if (!data._Content.item._id) {
return null;
}

const links = data?._Content?.item._link._Page.items.map(
(i) => i._metadata
);
const links = data?._Content?.item._link._Page.items;

if (options?.type === 'PATH') {
// Return sorted by "hierarchical"
return links.toSorted((a, b) => {
const ha = a?.url?.hierarchical ?? '';
const hb = b?.url?.hierarchical ?? '';
// Return sorted by "hierarchical"
return links.toSorted((a, b) => {
const ha = a?._metadata?.url?.hierarchical ?? '';
const hb = b?._metadata?.url?.hierarchical ?? '';

if (ha > hb) {
return 1;
}
if (ha < hb) {
return -1;
}
return 0;
});
if (ha > hb) {
return 1;
}
if (ha < hb) {
return -1;
}
return 0;
});
}

/**
* Given the path of a page, get its "items" (i.e. the children pages)
*
* @param path The URL of the current page
* @returns A list with the metadata information of all child/descendant pages
*/
async getItems(path: string, options?: GraphGetLinksOptions) {
const data = (await this.request(GET_ITEMS_QUERY, {
...pathFilter(path, options?.host),
...localeFilter(options?.locales),
})) as GetLinksResponse;

// Check if the page itself exist.
if (!data._Content.item._id) {
return null;
}

const links = data?._Content?.item._link._Page.items;

return links;
}

Expand Down
1 change: 1 addition & 0 deletions packages/optimizely-cms-sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export {
export {
GraphClient,
GraphGetContentOptions,
GraphGetLinksOptions,
GraphVariationInput,
} from './graph/index.js';
export type { PreviewParams } from './graph/index.js';
Expand Down