From 2a890a48fc80cba80c2cc9cade819de3b5c487a5 Mon Sep 17 00:00:00 2001 From: Carlos Saito Date: Mon, 27 Oct 2025 15:52:05 +0100 Subject: [PATCH 01/11] Split getLinksByPath into two functions --- .../optimizely-cms-sdk/src/graph/index.ts | 114 +++++++++++++++--- 1 file changed, 94 insertions(+), 20 deletions(-) diff --git a/packages/optimizely-cms-sdk/src/graph/index.ts b/packages/optimizely-cms-sdk/src/graph/index.ts index 1e03c515..4e8dd68d 100644 --- a/packages/optimizely-cms-sdk/src/graph/index.ts +++ b/packages/optimizely-cms-sdk/src/graph/index.ts @@ -53,6 +53,56 @@ query GetContentMetadata($where: _ContentWhereInput, $variation: VariationInput) } `; +const GET_PATH_QUERY = ` +query GetPath($where: _ContentWhereInput) { + _Content(where: $where) { + item { + _id + _link(type: PATH) { + _Page { + items { + _metadata { + sortOrder + displayName + locale + types + url { + hierarchical + default + } + } + } + } + } + } + } +}`; + +const GET_ITEMS_QUERY = ` +query GetPath($where: _ContentWhereInput) { + _Content(where: $where) { + item { + _id + _link(type: ITEMS) { + _Page { + items { + _metadata { + sortOrder + displayName + locale + types + url { + hierarchical + default + } + } + } + } + } + } + } +}`; + const GET_LINKS_QUERY = ` query GetLinks($where: _ContentWhereInput, $type: LinkTypes) { _Content(where: $where) { @@ -254,14 +304,17 @@ export class GraphClient { return response?._Content?.items; } - async getLinksByPath(path: string, options?: GraphGetLinksOptions) { - const input = { - ...pathFilter(path), - type: options?.type, - }; + /** + * 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?: GraphGetContentOptions) { const data = (await this.request( - GET_LINKS_QUERY, - input + GET_PATH_QUERY, + pathFilter(path, options?.host) )) as GetLinksResponse; // Check if the page itself exist. @@ -273,22 +326,43 @@ export class GraphClient { (i) => i._metadata ); - 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?.url?.hierarchical ?? ''; + const hb = b?.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 ancestors sorted + * from the top-most to the current + */ + async getItems(path: string, options?: GraphGetContentOptions) { + const data = (await this.request( + GET_ITEMS_QUERY, + pathFilter(path, options?.host) + )) 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 + ); + return links; } From 7d4beb3abdf15a4296ddd90f4d7943b8a6c33354 Mon Sep 17 00:00:00 2001 From: Carlos Saito Date: Mon, 27 Oct 2025 15:52:19 +0100 Subject: [PATCH 02/11] Update test page --- .../src/app/related/[...slug]/page.tsx | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/__test__/test-website/src/app/related/[...slug]/page.tsx b/__test__/test-website/src/app/related/[...slug]/page.tsx index 5016e79e..6c5618ed 100644 --- a/__test__/test-website/src/app/related/[...slug]/page.tsx +++ b/__test__/test-website/src/app/related/[...slug]/page.tsx @@ -17,21 +17,8 @@ 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 (
From 692d0994db9e681bd1cac9fbe8b7468b19cbc805 Mon Sep 17 00:00:00 2001 From: Carlos Saito Date: Mon, 27 Oct 2025 16:11:02 +0100 Subject: [PATCH 03/11] Make the function extensible --- packages/optimizely-cms-sdk/src/graph/index.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/optimizely-cms-sdk/src/graph/index.ts b/packages/optimizely-cms-sdk/src/graph/index.ts index 4e8dd68d..a7dbc5d1 100644 --- a/packages/optimizely-cms-sdk/src/graph/index.ts +++ b/packages/optimizely-cms-sdk/src/graph/index.ts @@ -322,14 +322,12 @@ export class GraphClient { return null; } - const links = data?._Content?.item._link._Page.items.map( - (i) => i._metadata - ); + const links = data?._Content?.item._link._Page.items; // Return sorted by "hierarchical" return links.toSorted((a, b) => { - const ha = a?.url?.hierarchical ?? ''; - const hb = b?.url?.hierarchical ?? ''; + const ha = a?._metadata?.url?.hierarchical ?? ''; + const hb = b?._metadata?.url?.hierarchical ?? ''; if (ha > hb) { return 1; @@ -359,9 +357,7 @@ export class GraphClient { return null; } - const links = data?._Content?.item._link._Page.items.map( - (i) => i._metadata - ); + const links = data?._Content?.item._link._Page.items; return links; } From fa8bbf37dfd20841f7df23bf64052f7fef9f20b0 Mon Sep 17 00:00:00 2001 From: Carlos Saito Date: Mon, 27 Oct 2025 16:12:13 +0100 Subject: [PATCH 04/11] Update test page --- __test__/test-website/src/app/related/[...slug]/page.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/__test__/test-website/src/app/related/[...slug]/page.tsx b/__test__/test-website/src/app/related/[...slug]/page.tsx index 6c5618ed..33692c52 100644 --- a/__test__/test-website/src/app/related/[...slug]/page.tsx +++ b/__test__/test-website/src/app/related/[...slug]/page.tsx @@ -1,5 +1,4 @@ import { GraphClient } from '@optimizely/cms-sdk'; -import { OptimizelyComponent } from '@optimizely/cms-sdk/react/server'; import React from 'react'; type Props = { @@ -27,7 +26,7 @@ export default async function Page({ params }: Props) {
    {children?.map((l) => (
  • - {l?.displayName} ({l?.url?.default}) + {l?._metadata?.displayName} ({l?._metadata?.url?.default})
  • ))}
@@ -35,7 +34,7 @@ export default async function Page({ params }: Props) {
    {ancestors?.map((l) => (
  1. - {l?.displayName} ({l?.url?.default}) + {l?._metadata?.displayName} ({l?._metadata?.url?.default})
  2. ))}
From 9fb9b1fa074723c72195384e932029574bd6d4a4 Mon Sep 17 00:00:00 2001 From: Carlos Saito Date: Mon, 27 Oct 2025 16:12:51 +0100 Subject: [PATCH 05/11] Remove unused variable --- .../optimizely-cms-sdk/src/graph/index.ts | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/packages/optimizely-cms-sdk/src/graph/index.ts b/packages/optimizely-cms-sdk/src/graph/index.ts index a7dbc5d1..fe16e56b 100644 --- a/packages/optimizely-cms-sdk/src/graph/index.ts +++ b/packages/optimizely-cms-sdk/src/graph/index.ts @@ -103,31 +103,6 @@ query GetPath($where: _ContentWhereInput) { } }`; -const GET_LINKS_QUERY = ` -query GetLinks($where: _ContentWhereInput, $type: LinkTypes) { - _Content(where: $where) { - item { - _id - _link(type: $type) { - _Page { - items { - _metadata { - sortOrder - displayName - locale - types - url { - hierarchical - default - } - } - } - } - } - } - } -}`; - type GetLinksResponse = { _Content: { item: { From 910939a25c6619e5d763ef43ef9735382d9bcd51 Mon Sep 17 00:00:00 2001 From: Carlos Saito Date: Mon, 27 Oct 2025 16:13:24 +0100 Subject: [PATCH 06/11] Remove unused type --- packages/optimizely-cms-sdk/src/graph/index.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/optimizely-cms-sdk/src/graph/index.ts b/packages/optimizely-cms-sdk/src/graph/index.ts index fe16e56b..b5db6edf 100644 --- a/packages/optimizely-cms-sdk/src/graph/index.ts +++ b/packages/optimizely-cms-sdk/src/graph/index.ts @@ -34,10 +34,6 @@ export type GraphGetContentOptions = { host?: string; }; -export type GraphGetLinksOptions = { - type?: 'DEFAULT' | 'ITEMS' | 'ASSETS' | 'PATH'; -}; - export { GraphVariationInput }; const GET_CONTENT_METADATA_QUERY = ` From 3c6eceafc2084073765f8512afaf0b8d495b3d67 Mon Sep 17 00:00:00 2001 From: Carlos Saito Date: Mon, 27 Oct 2025 16:15:35 +0100 Subject: [PATCH 07/11] Update input type --- packages/optimizely-cms-sdk/src/graph/index.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/optimizely-cms-sdk/src/graph/index.ts b/packages/optimizely-cms-sdk/src/graph/index.ts index b5db6edf..71964514 100644 --- a/packages/optimizely-cms-sdk/src/graph/index.ts +++ b/packages/optimizely-cms-sdk/src/graph/index.ts @@ -34,6 +34,10 @@ export type GraphGetContentOptions = { host?: string; }; +export type GraphGetLinksOptions = { + host?: string; +}; + export { GraphVariationInput }; const GET_CONTENT_METADATA_QUERY = ` @@ -282,7 +286,7 @@ export class GraphClient { * @returns A list with the metadata information of all ancestors sorted * from the top-most to the current */ - async getPath(path: string, options?: GraphGetContentOptions) { + async getPath(path: string, options?: GraphGetLinksOptions) { const data = (await this.request( GET_PATH_QUERY, pathFilter(path, options?.host) @@ -317,7 +321,7 @@ export class GraphClient { * @returns A list with the metadata information of all ancestors sorted * from the top-most to the current */ - async getItems(path: string, options?: GraphGetContentOptions) { + async getItems(path: string, options?: GraphGetLinksOptions) { const data = (await this.request( GET_ITEMS_QUERY, pathFilter(path, options?.host) From 3c1a3b528d31fac4fbfcc0933d0cd4a83d3761e7 Mon Sep 17 00:00:00 2001 From: Carlos Saito Date: Mon, 27 Oct 2025 16:17:09 +0100 Subject: [PATCH 08/11] Add key in _metadata --- __test__/test-website/src/app/related/[...slug]/page.tsx | 4 ++-- packages/optimizely-cms-sdk/src/graph/index.ts | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/__test__/test-website/src/app/related/[...slug]/page.tsx b/__test__/test-website/src/app/related/[...slug]/page.tsx index 33692c52..ef057737 100644 --- a/__test__/test-website/src/app/related/[...slug]/page.tsx +++ b/__test__/test-website/src/app/related/[...slug]/page.tsx @@ -25,7 +25,7 @@ export default async function Page({ params }: Props) {

Children

    {children?.map((l) => ( -
  • +
  • {l?._metadata?.displayName} ({l?._metadata?.url?.default})
  • ))} @@ -33,7 +33,7 @@ export default async function Page({ params }: Props) {

    Ancestors (breadcrumbs)

      {ancestors?.map((l) => ( -
    1. +
    2. {l?._metadata?.displayName} ({l?._metadata?.url?.default})
    3. ))} diff --git a/packages/optimizely-cms-sdk/src/graph/index.ts b/packages/optimizely-cms-sdk/src/graph/index.ts index 71964514..c977e389 100644 --- a/packages/optimizely-cms-sdk/src/graph/index.ts +++ b/packages/optimizely-cms-sdk/src/graph/index.ts @@ -62,6 +62,7 @@ query GetPath($where: _ContentWhereInput) { _Page { items { _metadata { + key sortOrder displayName locale @@ -87,6 +88,7 @@ query GetPath($where: _ContentWhereInput) { _Page { items { _metadata { + key sortOrder displayName locale @@ -111,6 +113,7 @@ type GetLinksResponse = { _Page: { items: Array<{ _metadata?: { + key: string; sortOrder?: number; displayName?: string; locale?: string; From 9acba2e95d7b823b4eb489c770ff89139863affa Mon Sep 17 00:00:00 2001 From: Carlos Saito Date: Mon, 27 Oct 2025 16:31:25 +0100 Subject: [PATCH 09/11] Add locale filter --- .../optimizely-cms-sdk/src/graph/filters.ts | 7 +++++ .../optimizely-cms-sdk/src/graph/index.ts | 26 ++++++++++--------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/packages/optimizely-cms-sdk/src/graph/filters.ts b/packages/optimizely-cms-sdk/src/graph/filters.ts index b0cd97dd..9a14cf24 100644 --- a/packages/optimizely-cms-sdk/src/graph/filters.ts +++ b/packages/optimizely-cms-sdk/src/graph/filters.ts @@ -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; }; diff --git a/packages/optimizely-cms-sdk/src/graph/index.ts b/packages/optimizely-cms-sdk/src/graph/index.ts index c977e389..a9063893 100644 --- a/packages/optimizely-cms-sdk/src/graph/index.ts +++ b/packages/optimizely-cms-sdk/src/graph/index.ts @@ -13,6 +13,7 @@ import { pathFilter, previewFilter, GraphVariationInput, + localeFilter, } from './filters.js'; /** Options for Graph */ @@ -36,6 +37,7 @@ export type GraphGetContentOptions = { export type GraphGetLinksOptions = { host?: string; + locales?: string[]; }; export { GraphVariationInput }; @@ -54,8 +56,8 @@ query GetContentMetadata($where: _ContentWhereInput, $variation: VariationInput) `; const GET_PATH_QUERY = ` -query GetPath($where: _ContentWhereInput) { - _Content(where: $where) { +query GetPath($where: _ContentWhereInput, $locale: [Locale]) { + _Content(where: $where, locale: $locale) { item { _id _link(type: PATH) { @@ -80,8 +82,8 @@ query GetPath($where: _ContentWhereInput) { }`; const GET_ITEMS_QUERY = ` -query GetPath($where: _ContentWhereInput) { - _Content(where: $where) { +query GetPath($where: _ContentWhereInput, $locale: [Locale]) { + _Content(where: $where, locale: $locale) { item { _id _link(type: ITEMS) { @@ -290,10 +292,10 @@ export class GraphClient { * 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) - )) as GetLinksResponse; + 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) { @@ -325,10 +327,10 @@ export class GraphClient { * from the top-most to the current */ async getItems(path: string, options?: GraphGetLinksOptions) { - const data = (await this.request( - GET_ITEMS_QUERY, - pathFilter(path, options?.host) - )) as GetLinksResponse; + 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) { From b4ff924aceaaed5d732659214db748a2f708e452 Mon Sep 17 00:00:00 2001 From: Carlos Saito Date: Mon, 27 Oct 2025 16:31:38 +0100 Subject: [PATCH 10/11] Publish GraphGetLinksOptions type --- packages/optimizely-cms-sdk/src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/optimizely-cms-sdk/src/index.ts b/packages/optimizely-cms-sdk/src/index.ts index fb98bd8f..43bd3001 100644 --- a/packages/optimizely-cms-sdk/src/index.ts +++ b/packages/optimizely-cms-sdk/src/index.ts @@ -11,6 +11,7 @@ export { export { GraphClient, GraphGetContentOptions, + GraphGetLinksOptions, GraphVariationInput, } from './graph/index.js'; export type { PreviewParams } from './graph/index.js'; From ebe1179102d34f2efa1ddb424720b67b4fa6692c Mon Sep 17 00:00:00 2001 From: Carlos Saito Date: Tue, 28 Oct 2025 08:52:21 +0100 Subject: [PATCH 11/11] Update packages/optimizely-cms-sdk/src/graph/index.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- packages/optimizely-cms-sdk/src/graph/index.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/optimizely-cms-sdk/src/graph/index.ts b/packages/optimizely-cms-sdk/src/graph/index.ts index a9063893..538aa60a 100644 --- a/packages/optimizely-cms-sdk/src/graph/index.ts +++ b/packages/optimizely-cms-sdk/src/graph/index.ts @@ -323,8 +323,7 @@ export class GraphClient { * 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 ancestors sorted - * from the top-most to the current + * @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, {