From ddf27c32fedf3c38dd66e7b5cec77bb76eb297cb Mon Sep 17 00:00:00 2001 From: Edoardo Dusi Date: Mon, 23 Dec 2024 13:31:34 +0100 Subject: [PATCH] fix: retrieve links with the right typings --- src/index.test.ts | 127 ++++++++++++++++++++++++++++++++++++++++++++++ src/index.ts | 17 ++++++- src/interfaces.ts | 20 ++++++++ 3 files changed, 162 insertions(+), 2 deletions(-) diff --git a/src/index.test.ts b/src/index.test.ts index c2e3af9a..2ae88d9f 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -3,6 +3,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import type { ResponseFn } from './sbFetch'; import SbFetch from './sbFetch'; import { SbHelpers } from './sbHelpers'; +import type { ISbLink } from './interfaces'; // Mocking external dependencies vi.mock('../src/sbFetch', () => { @@ -376,6 +377,132 @@ describe('storyblokClient', () => { // Verify the API was called only once (no relation resolution) expect(mockGet).toHaveBeenCalledTimes(1); }); + + describe('cdn/links endpoint', () => { + it('should fetch links with dates when include_dates is set to 1', async () => { + const mockLinksResponse = { + data: { + links: { + 'story-1': { + id: 1, + uuid: 'story-1-uuid', + slug: 'story-1', + name: 'Story 1', + is_folder: false, + parent_id: 0, + published: true, + position: 0, + // Date fields included because of include_dates: 1 + created_at: '2024-01-01T10:00:00.000Z', + published_at: '2024-01-01T11:00:00.000Z', + updated_at: '2024-01-02T10:00:00.000Z', + }, + 'story-2': { + id: 2, + uuid: 'story-2-uuid', + slug: 'story-2', + name: 'Story 2', + is_folder: false, + parent_id: 0, + published: true, + position: 1, + created_at: '2024-01-03T10:00:00.000Z', + published_at: '2024-01-03T11:00:00.000Z', + updated_at: '2024-01-04T10:00:00.000Z', + }, + }, + }, + headers: {}, + status: 200, + }; + + const mockGet = vi.fn().mockResolvedValue(mockLinksResponse); + + client.client = { + get: mockGet, + post: vi.fn(), + setFetchOptions: vi.fn(), + baseURL: 'https://api.storyblok.com/v2', + }; + + const response = await client.get('cdn/links', { + version: 'draft', + include_dates: 1, + }); + + // Verify the structure of the response + expect(response).toHaveProperty('data.links'); + + // Check if links are present and have the correct structure + expect(response.data.links['story-1']).toBeDefined(); + expect(response.data.links['story-2']).toBeDefined(); + + // Verify date fields are present in the response + const link: ISbLink = response.data.links['story-1']; + expect(link).toHaveProperty('created_at'); + expect(link).toHaveProperty('published_at'); + expect(link).toHaveProperty('updated_at'); + + // Verify the date formats + const DATETIME_FORMAT = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/; + expect(link.created_at).toMatch(DATETIME_FORMAT); + expect(link.published_at).toMatch(DATETIME_FORMAT); + expect(link.updated_at).toMatch(DATETIME_FORMAT); + + // Verify the API was called with correct parameters + expect(mockGet).toHaveBeenCalledWith('/cdn/links', { + cv: 0, + token: 'test-token', + version: 'draft', + include_dates: 1, + }); + expect(mockGet).toHaveBeenCalledTimes(1); + }); + + it('should handle links response without dates when include_dates is not set', async () => { + const mockResponse = { + data: { + links: { + 'story-1': { + id: 1, + uuid: 'story-1-uuid', + slug: 'story-1', + name: 'Story 1', + is_folder: false, + parent_id: 0, + published: true, + position: 0, + // No date fields + }, + }, + }, + headers: {}, + status: 200, + }; + + const mockGet = vi.fn().mockResolvedValue(mockResponse); + client.client.get = mockGet; + + const response = await client.get('cdn/links', { version: 'draft' }); + + expect(response.data.links['story-1']).not.toHaveProperty('created_at'); + expect(response.data.links['story-1']).not.toHaveProperty('published_at'); + expect(response.data.links['story-1']).not.toHaveProperty('updated_at'); + }); + + it('should handle errors gracefully', async () => { + const mockGet = vi.fn().mockRejectedValue({ + status: 404, + }); + client.client.get = mockGet; + + await expect(client.get('cdn/links', { + version: 'draft', + })).rejects.toMatchObject({ + status: 404, + }); + }); + }); }); describe('getAll', () => { diff --git a/src/index.ts b/src/index.ts index df14a646..bc174f2c 100755 --- a/src/index.ts +++ b/src/index.ts @@ -12,6 +12,8 @@ import type { ISbConfig, ISbContentMangmntAPI, ISbCustomFetch, + ISbLinksParams, + ISbLinksResult, ISbLinkURLObject, ISbNode, ISbResponse, @@ -226,11 +228,23 @@ class Storyblok { return this.cacheResponse(url, query, undefined, fetchOptions); } + public get( + slug: 'cdn/links', + params?: ISbLinksParams, + fetchOptions?: ISbCustomFetch + ): Promise; + public get( slug: string, params?: ISbStoriesParams, + fetchOptions?: ISbCustomFetch + ): Promise; + + public get( + slug: string, + params?: ISbStoriesParams | ISbLinksParams, fetchOptions?: ISbCustomFetch, - ): Promise { + ): Promise { if (!params) { params = {} as ISbStoriesParams; } @@ -451,7 +465,6 @@ class Storyblok { const fieldPath = jtree.component ? `${jtree.component}.${treeItem}` : treeItem; // Check if this exact pattern exists in the fields to resolve if (Array.isArray(fields) ? fields.includes(fieldPath) : fields === fieldPath) { - // this._resolveField(jtree, treeItem, resolveId); } } diff --git a/src/interfaces.ts b/src/interfaces.ts index a0e291c8..7fd34c55 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -238,6 +238,10 @@ export interface ISbResult { headers: Headers; } +export interface ISbLinksResult extends ISbResult { + data: ISbLinks; +} + export interface ISbResponse { data: any; status: number; @@ -333,6 +337,22 @@ export interface ISbLink { position?: number; uuid?: string; is_startpage?: boolean; + path?: string; + real_path?: string; + published_at?: string; + created_at?: string; + updated_at?: string; +} + +export interface ISbLinksParams { + starts_with?: string; + version?: 'published' | 'draft'; + paginated?: number; + per_page?: number; + page?: number; + sort_by?: string; + include_dates?: 0 | 1; + with_parent?: number; } export interface ISbLinks {