Skip to content

Commit a215194

Browse files
authored
Merge pull request #587 from acelaya-forks/feature/update-sdk
Update to Shlink JS SDK 2.0
2 parents 818456c + 9a68be7 commit a215194

30 files changed

+96
-153
lines changed

CHANGELOG.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,19 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org).
66

7-
## [Unreleased]
7+
## [0.13.0] - 2025-02-10
88
### Added
99
* *Nothing*
1010

1111
### Changed
1212
* Replace sass `@import`s with `@use`.
13+
* Update to `@shlinkio/shlink-js-sdk` v2.0.
1314

1415
### Deprecated
1516
* *Nothing*
1617

1718
### Removed
18-
* *Nothing*
19+
* Drop support for Shlink <3.5.0.
1920

2021
### Fixed
2122
* *Nothing*

dev/App.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ShlinkApiClient } from '@shlinkio/shlink-js-sdk';
2-
import { FetchHttpClient } from '@shlinkio/shlink-js-sdk/browser';
2+
import { FetchHttpClient } from '@shlinkio/shlink-js-sdk/fetch';
33
import type { FC } from 'react';
44
import { useCallback } from 'react';
55
import { useEffect, useMemo, useState } from 'react';

package-lock.json

+4-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
"@fortawesome/react-fontawesome": "^0.2.2",
4848
"@reduxjs/toolkit": "^2.5.0",
4949
"@shlinkio/shlink-frontend-kit": "^0.7.2",
50-
"@shlinkio/shlink-js-sdk": "^1.4.0",
50+
"@shlinkio/shlink-js-sdk": "^2.0.0",
5151
"react": "^18.3 || ^19.0",
5252
"react-dom": "^18.3 || ^19.0",
5353
"react-redux": "^9.2.0",

src/api-contract/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import type { ErrorType, ProblemDetailsError } from '@shlinkio/shlink-js-sdk/api
44
export * from '@shlinkio/shlink-js-sdk/api-contract';
55

66
export interface InvalidArgumentError extends ProblemDetailsError {
7-
type: ErrorType.INVALID_ARGUMENT;
7+
type: typeof ErrorType.INVALID_ARGUMENT;
88
invalidElements: string[];
99
}
1010

1111
export interface InvalidShortUrlDeletion extends ProblemDetailsError {
12-
type: ErrorType.INVALID_SHORT_URL_DELETION;
12+
type: typeof ErrorType.INVALID_SHORT_URL_DELETION;
1313
threshold: number;
1414
}

src/domains/reducers/domainsList.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export const domainsListReducerCreator = (
5858
`${REDUCER_PREFIX}/checkDomainHealth`,
5959
async (domain: string): Promise<ValidateDomain> => {
6060
try {
61-
const { status } = await apiClientFactory().health(domain);
61+
const { status } = await apiClientFactory().health({ domain });
6262
return { domain, status: status === 'pass' ? 'valid' : 'invalid' };
6363
} catch {
6464
return { domain, status: 'invalid' };

src/redirect-rules/reducers/setShortUrlRedirectRules.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export const setShortUrlRedirectRules = (apiClientFactory: () => ShlinkApiClient
2828
`${REDUCER_PREFIX}/setShortUrlRedirectRules`,
2929
({ shortUrl, data }: SetShortUrlRedirectRulesInfo) => {
3030
const { shortCode, domain } = shortUrl;
31-
return apiClientFactory().setShortUrlRedirectRules(shortCode, domain, data);
31+
return apiClientFactory().setShortUrlRedirectRules({ shortCode, domain }, data);
3232
},
3333
);
3434

src/redirect-rules/reducers/shortUrlRedirectRules.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const initialState: ShortUrlRedirectRules = {
1919

2020
export const getShortUrlRedirectRules = (apiClientFactory: () => ShlinkApiClient) => createAsyncThunk(
2121
`${REDUCER_PREFIX}/getShortUrlRedirectRules`,
22-
({ shortCode, domain }: ShortUrlIdentifier) => apiClientFactory().getShortUrlRedirectRules(shortCode, domain),
22+
({ shortCode, domain }: ShortUrlIdentifier) => apiClientFactory().getShortUrlRedirectRules({ shortCode, domain }),
2323
);
2424

2525
export const shortUrlRedirectRulesReducerCreator = (

src/short-urls/ShortUrlsList.tsx

+2-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import type { MercureBoundProps } from '../mercure/helpers/boundToMercureHub';
1212
import { boundToMercureHub } from '../mercure/helpers/boundToMercureHub';
1313
import { Topics } from '../mercure/helpers/Topics';
1414
import { useSettings } from '../settings';
15-
import { useFeature } from '../utils/features';
1615
import { TableOrderIcon } from '../utils/table/TableOrderIcon';
1716
import { VisitsComparisonCollector } from '../visits/visits-comparison/VisitsComparisonCollector';
1817
import {
@@ -69,7 +68,6 @@ const ShortUrlsList: FCWithDeps<ShortUrlsListProps, ShortUrlsListDeps> = boundTo
6968
() => excludeBots ?? settings.visits?.excludeBots,
7069
[excludeBots, settings.visits?.excludeBots],
7170
);
72-
const supportsExcludingBots = useFeature('excludeBotsOnShortUrls');
7371
const handleOrderBy = useCallback((field?: ShortUrlsOrderableFields, dir?: OrderDir) => {
7472
toFirstPage({ orderBy: { field, dir } });
7573
setActualOrderBy({ field, dir });
@@ -83,12 +81,12 @@ const ShortUrlsList: FCWithDeps<ShortUrlsListProps, ShortUrlsListDeps> = boundTo
8381
[tags, toFirstPage],
8482
);
8583
const parseOrderByForShlink = useCallback(({ field, dir }: ShortUrlsOrder): ShlinkShortUrlsOrder => {
86-
if (supportsExcludingBots && doExcludeBots && field === 'visits') {
84+
if (doExcludeBots && field === 'visits') {
8785
return { field: 'nonBotVisits', dir };
8886
}
8987

9088
return { field, dir };
91-
}, [doExcludeBots, supportsExcludingBots]);
89+
}, [doExcludeBots]);
9290
const visitsComparisonValue = useVisitsComparison();
9391

9492
useEffect(() => {

src/short-urls/data/index.ts

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
import type { Order } from '@shlinkio/shlink-frontend-kit';
2-
import type { ShlinkShortUrl } from '../../api-contract';
2+
import type { ShlinkShortUrl, ShlinkShortUrlIdentifier } from '@shlinkio/shlink-js-sdk/api-contract';
33

4-
export type ShortUrlIdentifier = {
5-
shortCode: string;
6-
domain?: string | null;
7-
};
4+
/** @deprecated Use ShlinkShortUrlIdentifier from the SDK's API contract definition */
5+
export type ShortUrlIdentifier = ShlinkShortUrlIdentifier;
86

97
export type ShortUrlModalProps = {
108
shortUrl: ShlinkShortUrl;

src/short-urls/helpers/ShortUrlsFilterDropdown.tsx

+8-13
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ interface ShortUrlsFilterDropdownProps {
2020
export const ShortUrlsFilterDropdown = (
2121
{ onChange, selected = {}, className, domains }: ShortUrlsFilterDropdownProps,
2222
) => {
23-
const supportsDisabledFiltering = useFeature('filterDisabledUrls');
2423
const supportsFilterByDomain = useFeature('filterShortUrlsByDomain');
2524
const { excludeBots = false, excludeMaxVisitsReached = false, excludePastValidUntil = false, domain } = selected;
2625

@@ -40,18 +39,14 @@ export const ShortUrlsFilterDropdown = (
4039
Ignore visits from bots
4140
</DropdownItem>
4241

43-
{supportsDisabledFiltering && (
44-
<>
45-
<DropdownItem divider tag="hr" />
46-
<DropdownItem header aria-hidden>Short URLs:</DropdownItem>
47-
<DropdownItem active={excludeMaxVisitsReached} onClick={() => toggleFilter('excludeMaxVisitsReached')}>
48-
Exclude with visits reached
49-
</DropdownItem>
50-
<DropdownItem active={excludePastValidUntil} onClick={() => toggleFilter('excludePastValidUntil')}>
51-
Exclude enabled in the past
52-
</DropdownItem>
53-
</>
54-
)}
42+
<DropdownItem divider tag="hr" />
43+
<DropdownItem header aria-hidden>Short URLs:</DropdownItem>
44+
<DropdownItem active={excludeMaxVisitsReached} onClick={() => toggleFilter('excludeMaxVisitsReached')}>
45+
Exclude with visits reached
46+
</DropdownItem>
47+
<DropdownItem active={excludePastValidUntil} onClick={() => toggleFilter('excludePastValidUntil')}>
48+
Exclude enabled in the past
49+
</DropdownItem>
5550

5651
{supportsFilterByDomain && (
5752
<>

src/short-urls/reducers/shortUrlDeletion.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const initialState: ShortUrlDeletion = {
2424
export const deleteShortUrl = (apiClientFactory: () => ShlinkApiClient) => createAsyncThunk(
2525
`${REDUCER_PREFIX}/deleteShortUrl`,
2626
async ({ shortCode, domain }: ShortUrlIdentifier): Promise<ShortUrlIdentifier> => {
27-
await apiClientFactory().deleteShortUrl(shortCode, domain);
27+
await apiClientFactory().deleteShortUrl({ shortCode, domain });
2828
return { shortCode, domain };
2929
},
3030
);

src/short-urls/reducers/shortUrlEdition.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const initialState: ShortUrlEdition = {
2727
export const editShortUrl = (apiClientFactory: () => ShlinkApiClient) => createAsyncThunk(
2828
`${REDUCER_PREFIX}/editShortUrl`,
2929
({ shortCode, domain, data }: EditShortUrl): Promise<ShlinkShortUrl> =>
30-
apiClientFactory().updateShortUrl(shortCode, domain, data as any) // TODO parse dates
30+
apiClientFactory().updateShortUrl({ shortCode, domain }, data as any) // TODO parse dates
3131
,
3232
);
3333

src/short-urls/reducers/shortUrlsDetails.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export const shortUrlsDetailsReducerCreator = (apiClientFactory: () => ShlinkApi
2929
const { shortCode, domain } = identifier;
3030
const alreadyLoaded = shortUrlsList?.shortUrls?.data.find((url) => shortUrlMatches(url, shortCode, domain));
3131

32-
return [identifier, alreadyLoaded ?? await apiClientFactory().getShortUrl(shortCode, domain)];
32+
return [identifier, alreadyLoaded ?? await apiClientFactory().getShortUrl({ shortCode, domain })];
3333
},
3434
));
3535

src/tags/reducers/tagEdit.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export const editTag = (
3535
) => createAsyncThunk(
3636
`${REDUCER_PREFIX}/editTag`,
3737
async ({ oldName, newName, color }: EditTag): Promise<EditTag> => {
38-
await apiClientFactory().editTag(oldName, newName);
38+
await apiClientFactory().editTag({ oldName, newName });
3939
colorGenerator.setColorForKey(newName, color);
4040

4141
return { oldName, newName, color };

src/utils/features.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@ import type { SemVerOrLatest, Versions } from './helpers/version';
33
import { versionMatch } from './helpers/version';
44

55
const supportedFeatures = {
6-
excludeBotsOnShortUrls: { minVersion: '3.4.0' },
7-
filterDisabledUrls: { minVersion: '3.4.0' },
8-
deviceLongUrls: { minVersion: '3.5.0', maxVersion: '3.*.*' },
96
shortUrlVisitsDeletion: { minVersion: '3.6.0' },
107
orphanVisitsDeletion: { minVersion: '3.7.0' },
8+
deviceLongUrls: { maxVersion: '3.*.*' },
119
shortUrlRedirectRules: { minVersion: '4.0.0' },
1210
qrCodeColors: { minVersion: '4.0.0' },
1311
urlValidation: { maxVersion: '3.*.*' },
@@ -26,8 +24,6 @@ const isFeatureEnabledForVersion = (feature: Feature, serverVersion: SemVerOrLat
2624
versionMatch(serverVersion === 'latest' ? '999.99.99' : serverVersion, supportedFeatures[feature]);
2725

2826
const getFeaturesForVersion = (serverVersion: SemVerOrLatest): Record<Feature, boolean> => ({
29-
excludeBotsOnShortUrls: isFeatureEnabledForVersion('excludeBotsOnShortUrls', serverVersion),
30-
filterDisabledUrls: isFeatureEnabledForVersion('filterDisabledUrls', serverVersion),
3127
deviceLongUrls: isFeatureEnabledForVersion('deviceLongUrls', serverVersion),
3228
shortUrlVisitsDeletion: isFeatureEnabledForVersion('shortUrlVisitsDeletion', serverVersion),
3329
orphanVisitsDeletion: isFeatureEnabledForVersion('orphanVisitsDeletion', serverVersion),

src/visits/reducers/shortUrlVisits.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ export const getShortUrlVisits = (apiClientFactory: () => ShlinkApiClient) => cr
2727
const apiClient = apiClientFactory();
2828
const { doIntervalFallback = false } = options;
2929

30-
const visitsLoader = (query: ShlinkVisitsParams) => apiClient.getShortUrlVisits(shortCode, { ...query, domain });
30+
const visitsLoader = (query: ShlinkVisitsParams) => apiClient.getShortUrlVisits({ shortCode, domain }, query);
3131
const lastVisitLoader = lastVisitLoaderForLoader(
3232
doIntervalFallback,
33-
(q) => apiClient.getShortUrlVisits(shortCode, { ...q, domain }),
33+
(q) => apiClient.getShortUrlVisits({ shortCode, domain }, q),
3434
);
3535

3636
return { visitsLoader, lastVisitLoader };

src/visits/reducers/shortUrlVisitsDeletion.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const initialState: ShortUrlVisitsDeletion = {
2121
export const deleteShortUrlVisits = (apiClientFactory: () => ShlinkApiClient) => createAsyncThunk(
2222
`${REDUCER_PREFIX}/deleteShortUrlVisits`,
2323
async ({ shortCode, domain }: ShortUrlIdentifier): Promise<DeleteVisitsResult> => {
24-
const result = await apiClientFactory().deleteShortUrlVisits(shortCode, domain);
24+
const result = await apiClientFactory().deleteShortUrlVisits({ shortCode, domain });
2525
return { ...result, shortCode, domain };
2626
},
2727
);

src/visits/visits-comparison/reducers/shortUrlVisitsComparison.ts

+1-4
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,7 @@ export const getShortUrlVisitsForComparison = (apiClientFactory: () => ShlinkApi
2626
const apiClient = apiClientFactory();
2727
const loaderEntries = shortUrls.map((identifier) => [
2828
shortUrlToQuery(identifier),
29-
(query: ShlinkVisitsParams) => apiClient.getShortUrlVisits(
30-
identifier.shortCode,
31-
{ ...query, domain: identifier.domain },
32-
),
29+
(query: ShlinkVisitsParams) => apiClient.getShortUrlVisits(identifier, query),
3330
]);
3431

3532
return Object.fromEntries(loaderEntries);

test/domains/reducers/domainsList.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ describe('domainsListReducer', () => {
147147
await checkDomainHealth(domain)(dispatch, getState, {});
148148

149149
expect(health).toHaveBeenCalledOnce();
150-
expect(health).toHaveBeenCalledWith(domain);
150+
expect(health).toHaveBeenCalledWith({ domain });
151151
expect(dispatch).toHaveBeenLastCalledWith(expect.objectContaining({
152152
payload: { domain, status: 'invalid' },
153153
}));
@@ -165,7 +165,7 @@ describe('domainsListReducer', () => {
165165
await checkDomainHealth(domain)(dispatch, getState, {});
166166

167167
expect(health).toHaveBeenCalledOnce();
168-
expect(health).toHaveBeenCalledWith(domain);
168+
expect(health).toHaveBeenCalledWith({ domain });
169169
expect(dispatch).toHaveBeenLastCalledWith(expect.objectContaining({
170170
payload: { domain, status: expectedStatus },
171171
}));

test/short-urls/ShortUrlsFilteringBar.test.tsx

+8-11
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { createMemoryHistory } from 'history';
77
import { Router } from 'react-router';
88
import { SettingsProvider } from '../../src/settings';
99
import { ShortUrlsFilteringBarFactory } from '../../src/short-urls/ShortUrlsFilteringBar';
10-
import { FeaturesProvider } from '../../src/utils/features';
1110
import { RoutesPrefixProvider } from '../../src/utils/routesPrefix';
1211
import { checkAccessibility } from '../__helpers__/accessibility';
1312
import { renderWithEvents } from '../__helpers__/setUpTest';
@@ -30,16 +29,14 @@ describe('<ShortUrlsFilteringBar />', () => {
3029
return renderWithEvents(
3130
<Router location={history.location} navigator={history}>
3231
<SettingsProvider value={fromPartial({ visits: {} })}>
33-
<FeaturesProvider value={fromPartial({ filterDisabledUrls: true })}>
34-
<RoutesPrefixProvider value={routesPrefix}>
35-
<ShortUrlsFilteringBar
36-
order={{}}
37-
handleOrderBy={handleOrderBy}
38-
tagsList={fromPartial({ tags: [] })}
39-
domainsList={fromPartial({})}
40-
/>
41-
</RoutesPrefixProvider>
42-
</FeaturesProvider>
32+
<RoutesPrefixProvider value={routesPrefix}>
33+
<ShortUrlsFilteringBar
34+
order={{}}
35+
handleOrderBy={handleOrderBy}
36+
tagsList={fromPartial({ tags: [] })}
37+
domainsList={fromPartial({})}
38+
/>
39+
</RoutesPrefixProvider>
4340
</SettingsProvider>
4441
</Router>,
4542
);

test/short-urls/ShortUrlsList.test.tsx

+10-25
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,11 @@ import type { ShortUrlsOrder } from '../../src/short-urls/data';
1010
import type { ShortUrlsList as ShortUrlsListModel } from '../../src/short-urls/reducers/shortUrlsList';
1111
import { ShortUrlsListFactory } from '../../src/short-urls/ShortUrlsList';
1212
import type { ShortUrlsTableType } from '../../src/short-urls/ShortUrlsTable';
13-
import { FeaturesProvider } from '../../src/utils/features';
1413
import { checkAccessibility } from '../__helpers__/accessibility';
1514
import { renderWithEvents } from '../__helpers__/setUpTest';
1615

1716
type SetUpOptions = {
1817
settings?: Partial<Settings>;
19-
excludeBotsOnShortUrls?: boolean;
2018
loading?: boolean;
2119
};
2220

@@ -43,20 +41,18 @@ describe('<ShortUrlsList />', () => {
4341
});
4442
let history: MemoryHistory;
4543
const ShortUrlsList = ShortUrlsListFactory(fromPartial({ ShortUrlsTable, ShortUrlsFilteringBar }));
46-
const setUp = ({ settings = {}, excludeBotsOnShortUrls = true, loading = false }: SetUpOptions = {}) => {
44+
const setUp = ({ settings = {}, loading = false }: SetUpOptions = {}) => {
4745
history = createMemoryHistory();
4846
history.push({ search: '?tags=test%20tag&search=example.com' });
4947

5048
return renderWithEvents(
5149
<Router location={history.location} navigator={history}>
5250
<SettingsProvider value={fromPartial(settings)}>
53-
<FeaturesProvider value={fromPartial({ excludeBotsOnShortUrls })}>
54-
<ShortUrlsList
55-
{...fromPartial<MercureBoundProps>({ mercureInfo: { loading: true } })}
56-
listShortUrls={listShortUrlsMock}
57-
shortUrlsList={{ ...shortUrlsList, loading }}
58-
/>
59-
</FeaturesProvider>
51+
<ShortUrlsList
52+
{...fromPartial<MercureBoundProps>({ mercureInfo: { loading: true } })}
53+
listShortUrls={listShortUrlsMock}
54+
shortUrlsList={{ ...shortUrlsList, loading }}
55+
/>
6056
</SettingsProvider>
6157
</Router>,
6258
);
@@ -119,26 +115,15 @@ describe('<ShortUrlsList />', () => {
119115
shortUrlsList: {
120116
defaultOrdering: { field: 'visits', dir: 'ASC' },
121117
},
122-
}), false, { field: 'visits', dir: 'ASC' }],
118+
}), { field: 'visits', dir: 'ASC' }],
123119
[fromPartial<Settings>({
124120
shortUrlsList: {
125121
defaultOrdering: { field: 'visits', dir: 'ASC' },
126122
},
127123
visits: { excludeBots: true },
128-
}), false, { field: 'visits', dir: 'ASC' }],
129-
[fromPartial<Settings>({
130-
shortUrlsList: {
131-
defaultOrdering: { field: 'visits', dir: 'ASC' },
132-
},
133-
}), true, { field: 'visits', dir: 'ASC' }],
134-
[fromPartial<Settings>({
135-
shortUrlsList: {
136-
defaultOrdering: { field: 'visits', dir: 'ASC' },
137-
},
138-
visits: { excludeBots: true },
139-
}), true, { field: 'nonBotVisits', dir: 'ASC' }],
140-
])('parses order by based on supported features version and config', (settings, excludeBotsOnShortUrls, expectedOrderBy) => {
141-
setUp({ settings, excludeBotsOnShortUrls });
124+
}), { field: 'nonBotVisits', dir: 'ASC' }],
125+
])('parses order by based on supported features version and config', (settings, expectedOrderBy) => {
126+
setUp({ settings });
142127
expect(listShortUrlsMock).toHaveBeenCalledWith(expect.objectContaining({ orderBy: expectedOrderBy }));
143128
});
144129
});

0 commit comments

Comments
 (0)