Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/nice-icons-like.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"nextjs-website": patch
---

Refactor guides API data layer
4 changes: 2 additions & 2 deletions apps/nextjs-website/src/app/sitemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import {
getSolutionsProps,
getWebinarsProps,
getReleaseNotesProps,
getGuidesProps,
} from '@/lib/cmsApi';
import { GuidesRepository } from '@/lib/guides';
import { baseUrl } from '@/config';
import {
getGuidesMetadata,
Expand Down Expand Up @@ -165,7 +165,7 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
// These are stored in S3 and retrieved via legacy helpers.
// We keep them ensuring no missing legacy content.
const guidesMetadata = await getGuidesMetadata(localeCode);
const guides = await getGuidesProps(localeCode);
const guides = await GuidesRepository.getAll(localeCode);
const guidesDirNames = Array.from(
new Set(
guides
Expand Down
17 changes: 8 additions & 9 deletions apps/nextjs-website/src/lib/api.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { ApiDataListPagesRepository } from '@/lib/apiDataListPages';
import { CaseHistoriesRepository } from '@/lib/caseHistories';
import { GuideListPagesRepository } from '@/lib/guideListPages';
import { GuidesRepository } from '@/lib/guides';
import { Product } from '@/lib/product/types';
import { Webinar } from '@/lib/types/webinar';
import {
getGuideListPagesProps,
getGuidePageProps,
getOverviewsProps,
getQuickStartGuidesProps,
getReleaseNoteProps,
Expand Down Expand Up @@ -58,14 +58,15 @@ export async function getGuidePage(
productSlug: string
) {
// Fetch data in parallel instead of sequential
const [products, guideProps] = await Promise.all([
const [products, guideResult] = await Promise.all([
getProducts(locale),
getGuidePageProps(
guidePaths.length > 0 ? guidePaths[0] : '',
GuidesRepository.getByProductAndSlug(
locale,
productSlug
productSlug,
guidePaths.length > 0 ? guidePaths[0] : ''
),
]);
const guideProps = manageUndefined(guideResult);

// Path construction
const guidePath = [
Expand Down Expand Up @@ -96,9 +97,7 @@ export async function getGuidePage(

export async function getGuideListPages(locale: string, productSlug?: string) {
const props = manageUndefined(
(await getGuideListPagesProps(locale)).find(
({ product }) => product.slug === productSlug
)
await GuideListPagesRepository.getByProductSlug(locale, productSlug || '')
);
return manageUndefinedAndAddProducts(locale, props);
}
Expand Down
46 changes: 7 additions & 39 deletions apps/nextjs-website/src/lib/cmsApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import { makeSolutionsProps } from './strapi/makeProps/makeSolutions';
import { makeSolutionListPageProps } from './strapi/makeProps/makeSolutionListPage';
import { fetchSolutionListPage } from './strapi/fetches/fetchSolutionListPage';

import { makeGuideListPagesProps } from './strapi/makeProps/makeGuideListPages';
import { makeGuidesProps } from './strapi/makeProps/makeGuides';
import { fetchOverviews } from '@/lib/strapi/fetches/fetchOverviews';
import { makeOverviewsProps } from '@/lib/strapi/makeProps/makeOverviews';
import { fetchTutorialListPages } from './strapi/fetches/fetchTutorialListPages';
Expand All @@ -33,7 +31,6 @@ import {
fetchResponseFromCDN,
JsonMetadata,
} from '@/helpers/s3Metadata.helpers';
import { StrapiGuides } from '@/lib/strapi/types/guide';
import { fetchUseCases } from '@/lib/strapi/fetches/fetchUseCases';
import { makeUseCasesProps } from '@/lib/strapi/makeProps/makeUseCases';
import { fetchUseCaseListPages } from '@/lib/strapi/fetches/fetchUseCaseListPages';
Expand All @@ -42,12 +39,11 @@ import { fetchTags } from '@/lib/strapi/fetches/fetchTags';
import { makeTagsProps } from '@/lib/strapi/makeProps/makeTags';
import { isMarkDownPart, MarkDownPart } from '@/lib/strapi/types/part';
import { getMarkdownContent } from '@/lib/api';
import { fetchGuideListPages } from './strapi/fetches/fetchGuideListPages';
import {
getSyncedGuidesResponseJsonFile,
getSyncedSolutionsResponseJsonFile,
getSyncedReleaseNotesResponseJsonFile,
} from 'gitbook-docs/syncedResponses';
import { GuidesRepository } from '@/lib/guides';
import { StrapiSolutions } from './strapi/types/solutions';
import { StrapiReleaseNotes } from './strapi/types/releaseNotes';

Expand Down Expand Up @@ -131,49 +127,21 @@ export const getOverviewsProps = async (locale: string) => {
return makeOverviewsProps(locale, strapiOverviews);
};

export const getGuideListPagesProps = async (locale: string) => {
const strapiGuideList = await fetchGuideListPages(locale, buildEnv);
return strapiGuideList
? makeGuideListPagesProps(locale, strapiGuideList)
: [];
};

export const getGuideProps = async (
guidePaths: ReadonlyArray<string>,
locale: string,
productSlug: string
) => {
const guide = await getGuidePageProps(guidePaths[0], locale, productSlug);
return await makeGuideS3({ guideDefinition: guide, locale, guidePaths });
};

export const getGuidesProps = async (locale: string) => {
const strapiGuides = (await fetchResponseFromCDN(
`${locale}/${getSyncedGuidesResponseJsonFile}`
)) as StrapiGuides | undefined;
return strapiGuides ? makeGuidesProps(locale, strapiGuides) : [];
};

export const getGuidePageProps = async (
guideSlug: string,
locale: string,
productSlug: string
) => {
const strapiGuides = (await fetchResponseFromCDN(
`${locale}/${getSyncedGuidesResponseJsonFile}`
)) as StrapiGuides | undefined;
// eslint-disable-next-line functional/no-expression-statements
const guides = strapiGuides ? makeGuidesProps(locale, strapiGuides) : [];
const guide = guides.filter(
(g) => g.guide.slug === guideSlug && g.product.slug === productSlug
)[0];

const guide = await GuidesRepository.getByProductAndSlug(
locale,
productSlug,
guidePaths[0]
);
if (!guide) {
// eslint-disable-next-line functional/no-throw-statements
throw new Error('Failed to fetch guide data');
}

return guide;
return await makeGuideS3({ guideDefinition: guide, locale, guidePaths });
};

export const getSolutionProps = async (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { makeGuideListPagesProps } from '@/lib/strapi/makeProps/makeGuideListPages';
import { mapGuideListPages } from '@/lib/guideListPages/mapper';
import {
guideListPagesProps,
strapiEmptyGuideListPagesData,
strapiGuideListPagesData,
} from '@/lib/strapi/__tests__/fixtures/guideListPages';
} from '@/lib/shared/fixtures/guideListPages';
import {
guideListPagesWithItemMissingProductSlug,
guideListPagesWithItemsMissingListItem,
guideListPagesWithItemsWithWrongDataType,
guideListPagesWithItemsMissingImages,
guideListPagesWithItemsMissingSlug,
} from '@/lib/strapi/__tests__/factories/guideListPages';
} from '@/lib/shared/factories/guideListPages';
import { spyOnConsoleError } from '@/lib/strapi/__tests__/spyOnConsole';

describe('makeGuideListPageProps', () => {
describe('mapGuideListPages', () => {
beforeEach(() => {
spyOnConsoleError.mockClear();
});
Expand All @@ -24,12 +24,12 @@ describe('makeGuideListPageProps', () => {
});

it('should return an empty array when no guides are provided', () => {
const result = makeGuideListPagesProps('it', strapiEmptyGuideListPagesData);
const result = mapGuideListPages('it', strapiEmptyGuideListPagesData);
expect(result).toEqual([]);
});

it('should return an array with a single element with the guides for the PagoPA product', () => {
const result = makeGuideListPagesProps('it', strapiGuideListPagesData);
const result = mapGuideListPages('it', strapiGuideListPagesData);
expect(result).toHaveLength(1);
expect(result).toEqual(guideListPagesProps);
});
Expand All @@ -46,7 +46,7 @@ describe('makeGuideListPageProps', () => {
},
},
};
const result = makeGuideListPagesProps('it', guideListWithMissingSlugsData);
const result = mapGuideListPages('it', guideListWithMissingSlugsData);
expect(result).toHaveLength(1);
expect(result[0]).toEqual({
...guideListPagesProps[0],
Expand All @@ -73,10 +73,7 @@ describe('makeGuideListPageProps', () => {
},
},
};
const result = makeGuideListPagesProps(
'it',
guideListWithMissingImagesData
);
const result = mapGuideListPages('it', guideListWithMissingImagesData);
expect(result).toHaveLength(1);
expect(result[0]).toEqual({
...guideListPagesProps[0],
Expand All @@ -93,7 +90,7 @@ describe('makeGuideListPageProps', () => {

it('should return a single element array of type GuideListPageProps with only one guide', () => {
const guideListWithInvalidData = guideListPagesWithItemsMissingListItem();
const result = makeGuideListPagesProps('it', guideListWithInvalidData);
const result = mapGuideListPages('it', guideListWithInvalidData);
expect(result).toHaveLength(1);
expect(result[0].guidesSections).toHaveLength(2);
expect(result[0].guidesSections?.[0].guides).toHaveLength(1);
Expand All @@ -104,7 +101,7 @@ describe('makeGuideListPageProps', () => {
const guideListWithNumbers =
guideListPagesWithItemsWithWrongDataType() as any;

const result = makeGuideListPagesProps('it', guideListWithNumbers);
const result = mapGuideListPages('it', guideListWithNumbers);
expect(result).toHaveLength(1);
expect(result[0].abstract).toEqual({
title: 12345,
Expand All @@ -113,7 +110,7 @@ describe('makeGuideListPageProps', () => {
});

it('should return an empty array if all guide list pages have missing product slugs', () => {
const result = makeGuideListPagesProps(
const result = mapGuideListPages(
'it',
guideListPagesWithItemMissingProductSlug()
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import * as qs from 'qs';
import { fetchFromStrapi } from '@/lib/strapi/fetchFromStrapi';
import { productRelationsPopulate } from '@/lib/product/fetcher';
import { StrapiGuideListPages } from '@/lib/strapi/types/guideListPage';
import { StrapiGuideListPages } from './types';
import { buildEnv } from '@/lib/buildEnv';

const makeStrapiGuideListPopulate = () =>
qs.stringify({
Expand All @@ -21,7 +22,8 @@ const makeStrapiGuideListPopulate = () =>
},
});

export const fetchGuideListPages = fetchFromStrapi<StrapiGuideListPages>(
'guide-list-pages',
makeStrapiGuideListPopulate()
);
export const fetchGuideListPages = (locale: string) =>
fetchFromStrapi<StrapiGuideListPages>(
'guide-list-pages',
makeStrapiGuideListPopulate()
)(locale, buildEnv);
25 changes: 25 additions & 0 deletions apps/nextjs-website/src/lib/guideListPages/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { GuideListPageProps } from '@/app/[locale]/[productSlug]/guides/page';
import { fetchGuideListPages } from './fetcher';
import { mapGuideListPages } from './mapper';

export const GuideListPagesRepository = {
getAll: async (
locale: string
): Promise<ReadonlyArray<GuideListPageProps>> => {
const rawData = await fetchGuideListPages(locale);
if (!rawData) {
return [];
}
return mapGuideListPages(locale, rawData);
},

getByProductSlug: async (
locale: string,
productSlug: string
): Promise<GuideListPageProps | undefined> => {
const all = await GuideListPagesRepository.getAll(locale);
return all.find(
(guideListPage) => guideListPage.product.slug === productSlug
);
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import { GuidesSectionProps } from '@/components/molecules/GuidesSection/GuidesS
import { makeBannerLinkProps } from '@/lib/strapi/makeProps/makeBannerLink';
import { makeBaseProductWithoutLogoProps } from '@/lib/product/mapper';
import { GuideCardProps } from '@/components/molecules/GuideCard/GuideCard';
import { StrapiBaseGuide } from '@/lib/strapi/types/guide';
import { StrapiBaseGuide } from '@/lib/guides/types';
import { compact } from 'lodash';
import { StrapiGuideListPages } from '@/lib/strapi/types/guideListPage';
import { StrapiGuideListPages } from './types';

export function makeGuideListPagesProps(
export function mapGuideListPages(
locale: string,
strapiGuideListPages: StrapiGuideListPages
): readonly GuideListPageProps[] {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { StrapiBaseGuide } from '@/lib/strapi/types/guide';
import { StrapiBaseGuide } from '@/lib/guides/types';
import { StrapiSeo } from '@/lib/strapi/types/seo';
import { StrapiBannerLink } from '@/lib/strapi/types/bannerLink';
import { StrapiBaseProductWithRelations } from '@/lib/product/types';
import { Paginated } from './paginated';
import { Paginated } from '../strapi/types/paginated';

export type StrapiGuideListPage = {
readonly id: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import {
guideProps,
strapiEmptyGuideData,
strapiGuideData,
} from '@/lib/strapi/__tests__/fixtures/guides';
import { makeGuidesProps } from '@/lib/strapi/makeProps/makeGuides';
} from '@/lib/shared/fixtures/guides';
import { mapGuides } from '@/lib/guides/mapper';
import {
guideListWithItemsWithEmptyProductSlug,
guideListWithMissingProductSlug,
} from '@/lib/strapi/__tests__/factories/guides';
} from '@/lib/shared/factories/guides';
import { spyOnConsoleError } from '@/lib/strapi/__tests__/spyOnConsole';

describe('makeGuidesProps', () => {
describe('mapGuides', () => {
beforeEach(() => {
spyOnConsoleError.mockClear();
});
Expand All @@ -20,29 +20,26 @@ describe('makeGuidesProps', () => {
});

it('should return an empty array when no guides are provided', () => {
const result = makeGuidesProps('it', strapiEmptyGuideData);
const result = mapGuides('it', strapiEmptyGuideData);
expect(result).toEqual([]);
});

it('should return an array containing only one object of type GuideDefinition', () => {
const result = makeGuidesProps('it', strapiGuideData);
const result = mapGuides('it', strapiGuideData);
expect(result).toHaveLength(1);
expect(result).toEqual(guideProps);
});

it('should return an empty array when the product slug is an empty string', () => {
const result = makeGuidesProps(
'it',
guideListWithItemsWithEmptyProductSlug()
);
const result = mapGuides('it', guideListWithItemsWithEmptyProductSlug());
expect(result).toEqual([]);
expect(spyOnConsoleError).toHaveBeenCalledWith(
'Error while processing Guide with name "SACI": missing the product slug. Skipping...'
);
});

it('should return an empty array when the product slug is undefined', () => {
const result = makeGuidesProps('it', guideListWithMissingProductSlug());
const result = mapGuides('it', guideListWithMissingProductSlug());
expect(result).toEqual([]);
expect(spyOnConsoleError).toHaveBeenCalledWith(
'Error while processing Guide with name "SACI": missing the product slug. Skipping...'
Expand Down
12 changes: 12 additions & 0 deletions apps/nextjs-website/src/lib/guides/fetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { fetchResponseFromCDN } from '@/helpers/s3Metadata.helpers';
import { getSyncedGuidesResponseJsonFile } from 'gitbook-docs/syncedResponses';
import { StrapiGuides } from './types';

export const fetchGuides = async (
locale: string
): Promise<StrapiGuides | undefined> => {
const response = await fetchResponseFromCDN(
`${locale}/${getSyncedGuidesResponseJsonFile}`
);
return response as StrapiGuides | undefined;
};
Loading
Loading