Skip to content

Commit

Permalink
feat: relative urls and support react 19 (#3407)
Browse files Browse the repository at this point in the history
Closing #3405
  • Loading branch information
stepan662 authored Dec 13, 2024
1 parent 9c6b1c6 commit c018ef2
Show file tree
Hide file tree
Showing 19 changed files with 923 additions and 413 deletions.
6 changes: 3 additions & 3 deletions packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"src/**/*"
],
"peerDependencies": {
"react": "^16.14.0 || ^17.0.1 || ^18.1.0"
"react": "^16.14.0 || ^17.0.1 || ^18.1.0 || ^19"
},
"dependencies": {
"@tolgee/web": "5.32.0"
Expand All @@ -50,8 +50,8 @@
"@tolgee/testing": "5.32.0",
"@types/jest": "^27.0.2",
"@types/node": "^17.0.8",
"@types/react": "^18.2.42",
"@types/react-dom": "^18.2.17",
"@types/react": "^18.3.16",
"@types/react-dom": "^18.3.5",
"@types/testing-library__jest-dom": "^5.14.5",
"concurrently": "7.2.2",
"jest": "~27.5.1",
Expand Down
6 changes: 3 additions & 3 deletions packages/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,12 @@
"@testing-library/jest-dom": "^6.4.2",
"@tginternal/editor": "^1.15.2",
"@types/jest": "^29.5.12",
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
"@types/react": "^18.3.16",
"@types/react-dom": "^18.3.5",
"@typescript-eslint/eslint-plugin": "^7.2.0",
"@typescript-eslint/parser": "^7.2.0",
"@vitejs/plugin-react": "^4.2.1",
"codemirror": "^6.0.1",
"codemirror": "6.0.1",
"eslint": "^8.57.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.6",
Expand Down
8 changes: 4 additions & 4 deletions packages/web/src/package/DevBackend.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { BackendDevMiddleware, TolgeePlugin } from '@tolgee/core';
import { getApiKeyType, getProjectIdFromApiKey } from './tools/decodeApiKey';
import { createUrl } from './tools/url';

function createDevBackend(): BackendDevMiddleware {
return {
Expand All @@ -13,12 +14,11 @@ function createDevBackend(): BackendDevMiddleware {
fetch,
}) {
const pId = getProjectIdFromApiKey(apiKey) ?? projectId;
const url = new URL(apiUrl);

let url: URL;
if (pId !== undefined) {
url.pathname = `/v2/projects/${pId}/translations/${language}`;
url = createUrl(apiUrl, `/v2/projects/${pId}/translations/${language}`);
} else {
url.pathname = `/v2/projects/translations/${language}`;
url = createUrl(apiUrl, `/v2/projects/translations/${language}`);
}

if (namespace) {
Expand Down
53 changes: 53 additions & 0 deletions packages/web/src/package/__test__/fetch.apiUrl.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { TolgeeCore } from '@tolgee/core';
import { createFetchingUtility } from './fetchingUtillity';
import { DevBackend } from '../DevBackend';

describe('can handle relative urls in apiUrl', () => {
let f: ReturnType<typeof createFetchingUtility>;

beforeEach(() => {
f = createFetchingUtility();
});

it('dev backend can resolve relative urls', async () => {
const fetchMock = f.fetchWithResponse({});
const tolgee = TolgeeCore()
.use(DevBackend())
.init({
language: 'en',
availableLanguages: ['en'],
fetch: fetchMock,
apiUrl: '/test',
apiKey: 'test',
});
await expect(tolgee.loadRecord({ language: 'en' })).resolves.toEqual(
new Map()
);
expect(fetchMock).toHaveBeenCalledTimes(1);
const args = fetchMock.mock.calls[0] as any;
expect(args[0]).toEqual(
'http://localhost/test/v2/projects/translations/en'
);
});

it('dev backend can resolve apiUrl with included path', async () => {
const fetchMock = f.fetchWithResponse({});
const tolgee = TolgeeCore()
.use(DevBackend())
.init({
language: 'en',
availableLanguages: ['en'],
fetch: fetchMock,
apiUrl: 'https://test.com/abcd',
apiKey: 'test',
});
await expect(tolgee.loadRecord({ language: 'en' })).resolves.toEqual(
new Map()
);
expect(fetchMock).toHaveBeenCalledTimes(1);
const args = fetchMock.mock.calls[0] as any;
expect(args[0]).toEqual(
'https://test.com/abcd/v2/projects/translations/en'
);
});
});
18 changes: 15 additions & 3 deletions packages/web/src/package/__test__/fetchingUtillity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ export const createFetchingUtility = () => {
signalHandler,
fetchMock: jest.fn(() =>
Promise.resolve({
json: () => ({
status: 'ok',
}),
json: () =>
Promise.resolve({
status: 'ok',
}),
ok: true,
} as unknown as Response)
),
Expand All @@ -20,5 +21,16 @@ export const createFetchingUtility = () => {
})
),
failingFetch: jest.fn(() => Promise.reject(new Error('Fetch failed'))),
fetchWithResponse: (
response: any = {
status: 'ok',
}
) =>
jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve(response),
ok: true,
} as unknown as Response)
),
};
};
15 changes: 15 additions & 0 deletions packages/web/src/package/tools/url.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { joinUrls } from './url';

describe('url module', () => {
it('joins urls without double slash', () => {
expect(joinUrls('a/b/', '/c')).toEqual('a/b/c');
});

it("doesn't remove last slash", () => {
expect(joinUrls('a/b/', '/c/')).toEqual('a/b/c/');
});

it("doesn't touch the protocol", () => {
expect(joinUrls('https://test.com/', '/c/')).toEqual('https://test.com/c/');
});
});
21 changes: 21 additions & 0 deletions packages/web/src/package/tools/url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
function composeUrl(base: string, path: string): string {
// Ensure the base URL does not end with a slash
base = base.replace(/\/+$/, '');
// Ensure the path does not start with a slash
path = path.replace(/^\/+/, '');
// Combine the two parts with a single slash in between
return `${base}/${path}`;
}

export function joinUrls(...parts: string[]): string {
let result = parts[0];
parts.slice(1).forEach((part) => {
result = composeUrl(result, part);
});
return result;
}

export function createUrl(...parts: string[]): URL {
const base = typeof window === undefined ? undefined : window.location.origin;
return new URL(joinUrls(...parts), base);
}
3 changes: 2 additions & 1 deletion packages/web/src/package/ui/KeyDialog/ErrorAlert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Alert, AlertTitle } from '@mui/material';
import { HttpError } from '../client/HttpError';
import { useDialogContext } from './dialogContext';
import { NewTabLink } from './Link';
import { createUrl } from '../../tools/url';

type Props = {
error: HttpError | Error;
Expand All @@ -14,7 +15,7 @@ export const ErrorAlert = ({ error, severity = 'error' }: Props) => {
return (
<Alert sx={{ mt: 2 }} severity={severity}>
{error instanceof HttpError
? getErrorContent(error, apiUrl)
? getErrorContent(error, createUrl(apiUrl).toString())
: error.message}
</Alert>
);
Expand Down
4 changes: 3 additions & 1 deletion packages/web/src/package/ui/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { GlobalOptions } from './QueryProvider';
import { RequestParamsType, ResponseContent } from './types';
import { HttpError } from './HttpError';
import { isUrlValid } from '../tools/validateUrl';
import { createUrl } from '../../tools/url';

const errorFromResponse = (status: number, body: any) => {
if (body?.code) {
Expand Down Expand Up @@ -82,7 +83,8 @@ async function customFetch(
'X-API-Key': options.apiKey,
};

return fetchFn(options.apiUrl + input, init).then(async (r) => {
const url = createUrl(options.apiUrl, input.toString()).toString();
return fetchFn(url, init).then(async (r) => {
if (!r.ok) {
const data = await getResObject(r);
throw errorFromResponse(r.status, data);
Expand Down
4 changes: 3 additions & 1 deletion packages/web/src/package/ui/tools/validateUrl.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { createUrl } from '../../tools/url';

export function isUrlValid(url: string) {
try {
const result = new URL(url);
const result = createUrl(url);
return result instanceof URL;
} catch (e) {
return false;
Expand Down
Loading

0 comments on commit c018ef2

Please sign in to comment.