Skip to content
This repository was archived by the owner on Jan 19, 2026. It is now read-only.

Commit 877ecec

Browse files
committed
feat: update fetchLanguages to use management client and adjust test cases
- Refactored fetchLanguages to remove token parameter and utilize managementClient for API calls. - Updated related test cases to reflect changes in fetchLanguages function signature. - Added a new command to createManagementClient for better management of API interactions. - Adjusted launch configuration for debugging with a new space ID.
1 parent aa20e80 commit 877ecec

8 files changed

Lines changed: 90 additions & 28 deletions

File tree

.vscode/launch.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@
7474
"request": "launch",
7575
"name": "Debug Pull languages",
7676
"program": "${workspaceFolder}/dist/index.mjs",
77-
"args": ["languages", "pull", "--space", "2950182323", "--path", ".storyblok"],
77+
"args": ["languages", "pull", "--space", "295018", "--path", ".storyblok"],
7878
"cwd": "${workspaceFolder}",
7979
"console": "integratedTerminal",
8080
"sourceMaps": true,

src/commands/languages/actions.test.ts

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi }
55
import { fetchLanguages, saveLanguagesToFile } from './actions';
66
import { FetchError } from 'src/utils/fetch';
77
import { APIError } from 'src/utils';
8+
import { createManagementClient } from '../../api';
89

910
const handlers = [
1011
http.get('https://api.storyblok.com/v1/spaces/12345', async ({ request }) => {
1112
const token = request.headers.get('Authorization');
13+
console.log('msw token', token);
1214
if (token === 'valid-token') {
1315
return HttpResponse.json({
1416
space: {
@@ -30,6 +32,12 @@ const handlers = [
3032
}),
3133
];
3234

35+
// Mock the managementClient module
36+
vi.mock('src/api', () => ({
37+
managementClient: vi.fn(),
38+
createManagementClient: vi.fn(),
39+
}));
40+
3341
const server = setupServer(...handlers);
3442

3543
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
@@ -40,14 +48,19 @@ afterAll(() => server.close());
4048
vi.mock('node:fs');
4149
vi.mock('node:fs/promises');
4250

43-
describe('pull languages actions', () => {
51+
describe.only('pull languages actions', () => {
4452
beforeEach(() => {
4553
vi.clearAllMocks();
4654
vol.reset();
4755
});
4856

4957
describe('fetchLanguages', () => {
50-
it('should pull languages successfully with a valid token', async () => {
58+
it('should pull languages successfully', async () => {
59+
createManagementClient({
60+
accessToken: 'valid-token',
61+
region: 'eu',
62+
});
63+
5164
const mockResponse = {
5265
default_lang_name: 'en',
5366
languages: [
@@ -61,17 +74,26 @@ describe('pull languages actions', () => {
6174
},
6275
],
6376
};
64-
const result = await fetchLanguages('12345', 'valid-token', 'eu');
77+
const result = await fetchLanguages('12345');
6578
expect(result).toEqual(mockResponse);
6679
});
6780
});
68-
it.skip('should throw an masked error for invalid token', async () => {
69-
// TODO: Fix this test, is the only of it's kind that is not working, it's not clear why
70-
// Test return "Compared values have no visual difference." but fails anyway
71-
const error = new FetchError('Non-JSON response', { status: 401, statusText: 'Unauthorized', data: null });
72-
await expect(fetchLanguages('12345', 'invalid-token', 'eu')).rejects.toThrow(
73-
new APIError('unauthorized', 'pull_languages', error, `The user is not authorized to access the API`),
74-
);
81+
it('should throw an masked error for invalid token', async () => {
82+
createManagementClient({
83+
accessToken: 'invalid-token',
84+
region: 'eu',
85+
});
86+
87+
await expect(fetchLanguages('12345')).rejects.toMatchObject({
88+
name: 'API Error',
89+
errorId: 'unauthorized',
90+
cause: 'The user is not authorized to access the API',
91+
code: 401,
92+
messageStack: [
93+
'Failed to pull languages',
94+
'The user is not authorized to access the API',
95+
],
96+
});
7597
});
7698

7799
describe('saveLanguagesToFile', () => {

src/commands/languages/actions.ts

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,16 @@
11
import { join } from 'node:path';
22
import { handleAPIError, handleFileSystemError } from '../../utils';
3-
import { customFetch } from '../../utils/fetch';
43
import { resolvePath, saveToFile } from '../../utils/filesystem';
54
import type { PullLanguagesOptions } from './constants';
6-
import type { RegionCode } from '../../constants';
75
import type { SpaceInternationalization } from '../../types';
8-
import { getStoryblokUrl } from '../../utils/api-routes';
6+
import { managementClient } from '../../api';
97

10-
export const fetchLanguages = async (space: string, token: string, region: RegionCode): Promise<SpaceInternationalization | undefined> => {
8+
export const fetchLanguages = async (space: string): Promise<SpaceInternationalization | undefined> => {
119
try {
12-
const url = getStoryblokUrl(region);
13-
const response = await customFetch<{
10+
const { get } = managementClient();
11+
const response = await get<{
1412
space: SpaceInternationalization;
15-
}>(`${url}/spaces/${space}`, {
16-
headers: {
17-
Authorization: token,
18-
},
19-
});
13+
}>(`/spaces/${space}`);
2014

2115
return {
2216
default_lang_name: response.space.default_lang_name,

src/commands/languages/index.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ describe('languagesCommand', () => {
9191

9292
vi.mocked(fetchLanguages).mockResolvedValue(mockResponse);
9393
await languagesCommand.parseAsync(['node', 'test', 'pull', '--space', '12345']);
94-
expect(fetchLanguages).toHaveBeenCalledWith('12345', 'valid-token', 'eu');
94+
expect(fetchLanguages).toHaveBeenCalledWith('12345');
9595
expect(saveLanguagesToFile).toHaveBeenCalledWith('12345', mockResponse, {
9696
path: undefined,
9797
});
@@ -165,7 +165,7 @@ describe('languagesCommand', () => {
165165

166166
vi.mocked(fetchLanguages).mockResolvedValue(mockResponse);
167167
await languagesCommand.parseAsync(['node', 'test', 'pull', '--space', '12345', '--path', '/tmp']);
168-
expect(fetchLanguages).toHaveBeenCalledWith('12345', 'valid-token', 'eu');
168+
expect(fetchLanguages).toHaveBeenCalledWith('12345');
169169
expect(saveLanguagesToFile).toHaveBeenCalledWith('12345', mockResponse, {
170170
path: '/tmp',
171171
});
@@ -196,7 +196,7 @@ describe('languagesCommand', () => {
196196

197197
vi.mocked(fetchLanguages).mockResolvedValue(mockResponse);
198198
await languagesCommand.parseAsync(['node', 'test', 'pull', '--space', '12345', '--filename', 'custom-languages']);
199-
expect(fetchLanguages).toHaveBeenCalledWith('12345', 'valid-token', 'eu');
199+
expect(fetchLanguages).toHaveBeenCalledWith('12345');
200200
expect(saveLanguagesToFile).toHaveBeenCalledWith('12345', mockResponse, {
201201
filename: 'custom-languages',
202202
path: undefined,
@@ -228,7 +228,7 @@ describe('languagesCommand', () => {
228228

229229
vi.mocked(fetchLanguages).mockResolvedValue(mockResponse);
230230
await languagesCommand.parseAsync(['node', 'test', 'pull', '--space', '12345', '--suffix', 'custom-suffix']);
231-
expect(fetchLanguages).toHaveBeenCalledWith('12345', 'valid-token', 'eu');
231+
expect(fetchLanguages).toHaveBeenCalledWith('12345');
232232
expect(saveLanguagesToFile).toHaveBeenCalledWith('12345', mockResponse, {
233233
suffix: 'custom-suffix',
234234
path: undefined,

src/commands/languages/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { fetchLanguages, saveLanguagesToFile } from './actions';
66
import chalk from 'chalk';
77
import type { PullLanguagesOptions } from './constants';
88
import { Spinner } from '@topcli/spinner';
9+
import { createManagementClient } from '../../api';
910

1011
const program = getProgram(); // Get the shared singleton instance
1112

@@ -43,13 +44,18 @@ languagesCommand
4344
return;
4445
}
4546

47+
createManagementClient({
48+
accessToken: state.password,
49+
region: state.region,
50+
});
51+
4652
const spinner = new Spinner({
4753
verbose: !isVitest,
4854
});
4955
try {
5056
spinner.start(`Fetching ${chalk.hex(colorPalette.LANGUAGES)('languages')}`);
5157

52-
const internationalization = await fetchLanguages(space, state.password, state.region);
58+
const internationalization = await fetchLanguages(space);
5359

5460
if (!internationalization || internationalization.languages?.length === 0) {
5561
konsola.warn(`No languages found in the space ${space}`);

src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import './commands/types';
1313
import pkg from '../package.json';
1414

1515
import { colorPalette } from './constants';
16+
import { createManagementClient } from './api';
1617

1718
export * from './types/storyblok';
1819

@@ -33,6 +34,10 @@ program.on('command:*', () => {
3334
program.help();
3435
});
3536

37+
program.command('test').action(() => {
38+
const client = createManagementClient();
39+
});
40+
3641
try {
3742
program.parse(process.argv);
3843
}

src/types/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export interface Language {
1717
name: string;
1818
code: string;
1919
fallback_code?: string;
20-
ai_translation_code: string | null;
20+
ai_translation_code?: string | null;
2121
}
2222

2323
export interface SpaceInternationalization {

src/types/storyblok.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,38 @@
1+
export interface StoryblokBaseResponse<T> {
2+
cv: number;
3+
rels: string[];
4+
links: string[];
5+
stories: StoryblokStory<T>[];
6+
}
7+
8+
export interface StoryblokContentBase {
9+
_uid?: string;
10+
component: string;
11+
_editable?: string;
12+
}
13+
14+
export interface StoryblokStory<T> {
15+
id: number;
16+
uuid: string;
17+
name: string;
18+
slug: string;
19+
full_slug: string;
20+
path: string;
21+
content: StoryblokContentBase & T;
22+
position: number;
23+
tag_list: string[];
24+
is_startpage: boolean;
25+
group_id: string;
26+
parent_id: number;
27+
meta_title: string;
28+
meta_description: string;
29+
published: boolean;
30+
created_at: string;
31+
updated_at: string;
32+
published_at: string;
33+
alternates: string[];
34+
}
35+
136
export type StoryblokPropertyType = 'asset' | 'multiasset' | 'multilink' | 'table' | 'richtext';
237

338
export interface StoryblokAsset {

0 commit comments

Comments
 (0)