Skip to content

Commit

Permalink
fix(types): use correct contentful types
Browse files Browse the repository at this point in the history
fetch correct WITH_ALL_LOCALES and convert to localized entries & assets
  • Loading branch information
bezoerb committed Jan 17, 2024
1 parent 5348e40 commit 77b3b1a
Show file tree
Hide file tree
Showing 10 changed files with 168 additions and 84 deletions.
12 changes: 6 additions & 6 deletions packages/contentful-ssg/src/__test__/mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import { HookManager } from '../lib/hook-manager.js';
import type {
ContentType,
Locale,
Asset,
Entry,
Config,
RuntimeContext,
TransformContext,
AssetRaw,
EntryRaw,
} from '../types.js';

const cache = new Map();
Expand All @@ -39,8 +39,8 @@ export const readFixtureSync = (file) => {
};

export const getContent = async () => {
const assets = (await readFixture('assets.json')) as Asset[];
const entries = (await readFixture('entries.json')) as Entry[];
const assets = (await readFixture('assets.json')) as AssetRaw[];
const entries = (await readFixture('entries.json')) as EntryRaw[];
const locales = (await readFixture('locales.json')) as Locale[];
const contentTypes = (await readFixture('content_types.json')) as ContentType[];

Expand Down Expand Up @@ -86,8 +86,8 @@ export const getConfig = (fixture: Partial<Config> = {}): Config => ({
});

export const getRuntimeContext = (fixture: Partial<RuntimeContext> = {}): RuntimeContext => {
const assets = readFixtureSync('assets.json') as Asset[];
const entries = readFixtureSync('entries.json') as Entry[];
const assets = readFixtureSync('assets.json') as AssetRaw[];
const entries = readFixtureSync('entries.json') as EntryRaw[];
const locales = readFixtureSync('locales.json') as Locale[];
const contentTypes = readFixtureSync('content_types.json') as ContentType[];

Expand Down
12 changes: 6 additions & 6 deletions packages/contentful-ssg/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import chalk from 'chalk';
import { run, cleanupPrevData } from './index.js';
import { gracefulExit } from 'exit-hook';
import { write } from './tasks/write.js';
import { RunResult, RuntimeContext } from './types.js';
import { Asset, Entry, RunResult, RuntimeContext } from './types.js';
import { getContent } from './__test__/mock.js';

const mockedGracefulExit = <jest.Mock<typeof gracefulExit>>gracefulExit;
Expand Down Expand Up @@ -184,10 +184,10 @@ describe('Run', () => {
observables: {},
localized: {
[locale.code]: {
assets: mockData.assets,
entries: mockData.entries,
assetMap: mockData.assetMap,
entryMap: mockData.entryMap,
assets: mockData.assets as Asset[],
entries: mockData.entries as Entry[],
assetMap: mockData.assetMap as Map<string, Asset>,
entryMap: mockData.entryMap as Map<string, Entry>,
},
},
};
Expand All @@ -206,7 +206,7 @@ describe('Run', () => {

cleanupPrevData(context, prev);

for (let node of context.data.deletedEntries) {
for (let node of context?.data?.deletedEntries ?? []) {
expect(Object.keys(node)).toEqual(expect.arrayContaining(['sys', 'fields']));
expect(Object.keys(node.sys)).toEqual(expect.arrayContaining(['contentType']));
expect(node.sys.type).toEqual('DeletedEntry');
Expand Down
9 changes: 7 additions & 2 deletions packages/contentful-ssg/src/lib/cf-cache.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ReplaySubject } from 'rxjs';
import { Config, Locale, ObservableContext, RunResult } from '../types.js';
import { Asset, Config, Entry, Locale, ObservableContext, RunResult } from '../types.js';
import { getContent } from '../__test__/mock.js';
import { getObservableCount } from './observable.js';
import { getCacheDir, initializeCache } from './cf-cache.js';
Expand Down Expand Up @@ -57,7 +57,12 @@ test('syncState', async () => {

const state: RunResult = {
localized: {
en: { entries, assets, entryMap, assetMap },
en: {
entries: entries as Entry[],
assets: assets as Asset[],
entryMap: entryMap as Map<string, Entry>,
assetMap: assetMap as Map<string, Asset>,
},
},
observables: {
en: subject$,
Expand Down
26 changes: 26 additions & 0 deletions packages/contentful-ssg/src/lib/contentful.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,32 @@ jest.mock('contentful', () => {
deletedEntries: [{ sys: { id: 'entry-2' } }],
deletedAssets: [],
}),
withAllLocales: {
getLocales: jest.fn().mockResolvedValue({ items: Array(2) }),
getContentTypes: jest.fn().mockResolvedValue({ items: Array(3) }),
getAssets: jest.fn().mockResolvedValue({ items: Array(4) }),
getEntries: jest
.fn()
.mockResolvedValueOnce({ items: Array(1000), total: 2004 })
.mockResolvedValueOnce({ items: Array(1000), total: 2004 })
.mockResolvedValue({ items: Array(4), total: 2004 }),
sync: jest
.fn()
.mockResolvedValueOnce({
nextSyncToken: 'sync-token',
entries: Array(5),
assets: Array(3),
deletedEntries: [{ sys: { id: 'entry' } }],
deletedAssets: [{ sys: { id: 'asset' } }],
})
.mockResolvedValueOnce({
nextSyncToken: 'sync-token-2',
entries: [],
assets: [],
deletedEntries: [{ sys: { id: 'entry-2' } }],
deletedAssets: [],
}),
},
}),
};
});
Expand Down
57 changes: 29 additions & 28 deletions packages/contentful-ssg/src/lib/contentful.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type {
DeletedEntry,
EntryFields,
EntrySkeletonType,
SyncCollection,
SyncCollection as ContentfulSyncCollection,
} from 'contentful';
import contentful from 'contentful';
import type { ClientAPI as ContentfulManagementApi } from 'contentful-management';
Expand All @@ -15,10 +15,8 @@ import { createHash } from 'crypto';
import { hostname } from 'os';
import { v4 as uuidv4 } from 'uuid';
import type {
Asset,
ContentfulConfig,
ContentType,
Entry,
FieldSettings,
Locale,
Node,
Expand All @@ -27,10 +25,13 @@ import type {
CollectionResponse,
EntryCollection,
ContentfulCollection,
EntryRaw,
AssetRaw,
NodeRaw,
} from '../types.js';
import { initializeCache } from './cf-cache.js';

type ClientApi = ContentfulClientApi<undefined>;
type ClientApi = ContentfulClientApi<'WITH_ALL_LOCALES'>;

let client: ClientApi;
let managementClient: ContentfulManagementApi;
Expand All @@ -57,7 +58,7 @@ export const MAX_ALLOWED_LIMIT = 1000;
* @returns {String}
*/
export const getContentTypeId = <
T extends Node | EntryFields.EntryLink<EntrySkeletonType> | DeletedEntry
T extends Node | NodeRaw | EntryFields.EntryLink<EntrySkeletonType> | DeletedEntry
>(
node: T
): string => {
Expand All @@ -77,7 +78,7 @@ export const getContentTypeId = <
* @param {Object} node Contentful entry
* @returns {String}
*/
export const getEnvironmentId = <T extends Node>(node: T): string =>
export const getEnvironmentId = <T extends Node | NodeRaw>(node: T): string =>
node?.sys?.environment?.sys?.id ?? 'unknown';

/**
Expand All @@ -86,7 +87,7 @@ export const getEnvironmentId = <T extends Node>(node: T): string =>
* @returns {String}
*/
export const getContentId = <
T extends Node | ContentType | EntryFields.Link<EntrySkeletonType> | DeletedEntry
T extends Node | NodeRaw | ContentType | EntryFields.Link<EntrySkeletonType> | DeletedEntry
>(
node: T
): string => node?.sys?.id ?? 'unknown';
Expand All @@ -110,7 +111,7 @@ const getClient = (options: ContentfulConfig): ClientApi => {
accessToken: preview ? previewAccessToken : accessToken,
environment: environmentId,
};
return contentful.createClient(params);
return contentful.createClient(params).withAllLocales;
}

throw new Error('You need to login first. Run npx contentful login');
Expand Down Expand Up @@ -376,23 +377,22 @@ export const pagedGet = async <T, R extends CollectionResponse<T> = ContentfulCo
* @returns Promise for the collection resulting of a sync operation
*/

type SyncCollectionClean = Omit<SyncCollection<EntrySkeletonType>, 'entries' | 'assets'> & {
entries: Entry[];
assets: Asset[];
};
// type SyncCollectionClean = Omit<SyncCollection<EntrySkeletonType>, 'entries' | 'assets'> & {
// entries: EntryRaw[];
// assets: AssetRaw[];
// };

type SyncCollection = ContentfulSyncCollection<EntrySkeletonType, 'WITH_ALL_LOCALES'>;

const sync = async (
apiClient: ClientApi,
config: ContentfulConfig
): Promise<SyncCollectionClean> => {
const sync = async (apiClient: ClientApi, config: ContentfulConfig): Promise<SyncCollection> => {
const cache = initializeCache(config);
const options: SyncOptions = { initial: true };
if (cache.hasSyncToken()) {
options.nextSyncToken = await cache.getSyncToken();
delete options.initial;
}

const response: SyncCollectionClean = (await apiClient.sync(options)) as SyncCollectionClean;
const response: SyncCollection = await apiClient.sync(options);
if (response.nextSyncToken) {
await cache.setSyncToken(response.nextSyncToken);
}
Expand Down Expand Up @@ -429,12 +429,12 @@ export const getContent = async (options: ContentfulConfig) => {

// EntryCollections can have linked entries/assets included:
// https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/links
const { items: entries, includes } = await pagedGet<Entry, EntryCollection>(apiClient, {
const { items: entries, includes } = await pagedGet<EntryRaw, EntryCollection>(apiClient, {
method: 'getEntries',
query: options?.query ?? null,
});

const { items: assets } = await pagedGet<Asset>(apiClient, {
const { items: assets } = await pagedGet<AssetRaw>(apiClient, {
method: 'getAssets',
});

Expand All @@ -449,7 +449,7 @@ export const getContent = async (options: ContentfulConfig) => {
export const getEntriesLinkedToEntry = async (options: ContentfulConfig, id: string) => {
const apiClient = getClient(options);

const { items: entries } = await pagedGet<Entry>(apiClient, {
const { items: entries } = await pagedGet<EntryRaw>(apiClient, {
method: 'getEntries',
query: { links_to_entry: id },
});
Expand All @@ -460,7 +460,7 @@ export const getEntriesLinkedToEntry = async (options: ContentfulConfig, id: str
export const getEntriesLinkedToAsset = async (options: ContentfulConfig, id: string) => {
const apiClient = getClient(options);

const { items: entries } = await pagedGet<Entry>(apiClient, {
const { items: entries } = await pagedGet<EntryRaw>(apiClient, {
method: 'getEntries',
query: { links_to_asset: id },
});
Expand All @@ -473,43 +473,44 @@ export const getEntriesLinkedToAsset = async (options: ContentfulConfig, id: str
* @param {Object} entity Contentful entity
* @returns {Boolean}
*/
export const isContentfulObject = (obj) =>
export const isContentfulObject = (obj: any) =>
Object.prototype.toString.call(obj) === '[object Object]' && Object.keys(obj).includes('sys');

/**
* Check if the passed object is a contentful link object
* @param {Object} entity Contentful entity
* @returns {Boolean}
*/
export const isLink = (obj) => isContentfulObject(obj) && obj.sys.type === FIELD_TYPE_LINK;
export const isLink = (obj: any) => isContentfulObject(obj) && obj.sys.type === FIELD_TYPE_LINK;

/**
* Check if the passed object is a contentful asset link object
* @param {Object} entity Contentful entity
* @returns {Boolean}
*/
export const isAssetLink = (obj) => isLink(obj) && obj.sys.linkType === LINK_TYPE_ASSET;
export const isAssetLink = (obj: any) => isLink(obj) && obj.sys.linkType === LINK_TYPE_ASSET;

/**
* Check if the passed object is a contentful entry link object
* @param {Object} entity Contentful entity
* @returns {Boolean}
*/
export const isEntryLink = (obj) => isContentfulObject(obj) && obj.sys.linkType === LINK_TYPE_ENTRY;
export const isEntryLink = (obj: any) =>
isContentfulObject(obj) && obj.sys.linkType === LINK_TYPE_ENTRY;

/**
* Check if the passed object is a contentful asset object
* @param {Object} entity Contentful entity
* @returns {Boolean}
*/
export const isAsset = (obj) => isContentfulObject(obj) && obj.sys.type === LINK_TYPE_ASSET;
export const isAsset = (obj: any) => isContentfulObject(obj) && obj.sys.type === LINK_TYPE_ASSET;

/**
* Check if the passed object is a contentful entry object
* @param {Object} entity Contentful entity
* @returns {Boolean}
*/
export const isEntry = (obj) => isContentfulObject(obj) && obj.sys.type === LINK_TYPE_ENTRY;
export const isEntry = (obj: any) => isContentfulObject(obj) && obj.sys.type === LINK_TYPE_ENTRY;

/**
* Convert contenttype list to a map
Expand All @@ -530,5 +531,5 @@ export const getFieldSettings = (contentTypes: ContentType[]): FieldSettings =>
* Convert entries/assets array to map
* @param {Array} nodes Nodes array (entries/assets)
*/
export const convertToMap = <T extends Node>(nodes: T[] = []) =>
export const convertToMap = <T extends Node | NodeRaw>(nodes: T[] = []) =>
new Map(nodes.map((node) => [getContentId(node), node]));
16 changes: 8 additions & 8 deletions packages/contentful-ssg/src/tasks/localize.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-env jest */
import type { Locale } from '../types.js';
import type { EntryFieldRaw, Locale } from '../types.js';
import { getContent } from '../__test__/mock.js';
import { getContentId, getFieldSettings } from '../lib/contentful.js';

Expand Down Expand Up @@ -29,19 +29,19 @@ describe('Localize', () => {
});

test('localizeField', async () => {
const field = { b: 'b', c: 'c' };
const field: EntryFieldRaw = { b: { b: 'b' }, c: { c: 'c' } };

expect(localizeField(field, ...['a', 'b', 'c'])).toEqual('b');
expect(localizeField(field, ...['a', 'c'])).toEqual('c');
expect(localizeField(field, 'b')).toEqual('b');
expect(localizeField(field, 'c')).toEqual('c');
expect(localizeField(field, ...['a', 'b', 'c'])).toEqual({ b: 'b' });
expect(localizeField(field, ...['a', 'c'])).toEqual({ c: 'c' });
expect(localizeField(field, 'b')).toEqual({ b: 'b' });
expect(localizeField(field, 'c')).toEqual({ c: 'c' });
expect(localizeField(field, ...['a'])).toBeUndefined();
});

test('Localize entry', async () => {
const { entries, assets, contentTypes, locales } = await getContent();
const fieldSettings = getFieldSettings(contentTypes);
const entry = entries.find((entry) => getContentId(entry) === '34O95Y8gLXd3jPozdy7gmd');
const entry = entries.find((entry) => getContentId(entry) === '34O95Y8gLXd3jPozdy7gmd')!;

const { fields: fieldsDe } = localizeEntry(entry, 'de', { fieldSettings, locales });
expect(fieldsDe.shortText).toEqual('Short Text (DE)');
Expand All @@ -59,7 +59,7 @@ describe('Localize', () => {
test('Localize asset', async () => {
const { entries, assets, contentTypes, locales } = await getContent();
const fieldSettings = getFieldSettings(contentTypes);
const asset = assets.find((asset) => getContentId(asset) === '3t1t8PDynjpXbAzv6zOVQq');
const asset = assets.find((asset) => getContentId(asset) === '3t1t8PDynjpXbAzv6zOVQq')!;

const { fields } = localizeEntry(asset, 'en-US', { fieldSettings, locales });

Expand Down
Loading

0 comments on commit 77b3b1a

Please sign in to comment.