Skip to content

Commit

Permalink
Merge pull request #78 from acelaya-forks/feature/redirect-rules
Browse files Browse the repository at this point in the history
Feature/redirect rules
  • Loading branch information
acelaya authored Mar 5, 2024
2 parents c4a595b + b1b09f2 commit 4920164
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 13 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ All notable changes to this project will be documented in this file.

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).

## [Unreleased]
## [1.0.0] - 2024-03-05
### Added
* [#72](https://github.com/shlinkio/shlink-js-sdk/issues/72) Add support for Shlink 4.0.0

* Add support to list and set short URL redirect conditions.
* Add `type` param to `ShlinkApiClient.getOrphanVisits`.

### Changed
Expand Down
2 changes: 1 addition & 1 deletion dev/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { NodeHttpClient } from '../dist/node.js';
baseUrl,
apiKey,
});
console.log('Success:', await apiClient.deleteShortUrlVisits('nieRB'));
console.log('Success:', await apiClient.getShortUrlRedirectRules('sNvEk'));
} catch (e) {
console.error('Error:', e);
}
Expand Down
12 changes: 12 additions & 0 deletions src/api-contract/ShlinkApiClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import type {
ShlinkHealth,
ShlinkMercureInfo,
ShlinkOrphanVisitsParams,
ShlinkRedirectRulesList,
ShlinkSetRedirectRulesData,
ShlinkShortUrl,
ShlinkShortUrlsList,
ShlinkShortUrlsListParams,
Expand Down Expand Up @@ -36,6 +38,16 @@ export type ShlinkApiClient = {
data: ShlinkEditShortUrlData,
): Promise<ShlinkShortUrl>;

// Short URL redirect rules

getShortUrlRedirectRules(shortCode: string, domain?: string | null): Promise<ShlinkRedirectRulesList>;

setShortUrlRedirectRules(
shortCode: string,
domain: string | null | undefined,
data: ShlinkSetRedirectRulesData,
): Promise<ShlinkRedirectRulesList>;

// Visits

getVisitsOverview(): Promise<ShlinkVisitsOverview>;
Expand Down
31 changes: 25 additions & 6 deletions src/api-contract/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,9 +225,28 @@ export type ShlinkShortUrlsListParams = {
excludePastValidUntil?: boolean;
};

export type ShlinkShortUrlsListNormalizedParams =
Omit<ShlinkShortUrlsListParams, 'orderBy' | 'excludeMaxVisitsReached' | 'excludePastValidUntil'> & {
orderBy?: string;
excludeMaxVisitsReached?: 'true';
excludePastValidUntil?: 'true';
};
export type ShlinkRedirectConditionType = 'device' | 'language' | 'query-param';

export type ShlinkRedirectCondition = {
type: ShlinkRedirectConditionType;
matchKey: string | null;
matchValue: string;
};

export type ShlinkRedirectRuleData = {
longUrl: string;
conditions: ShlinkRedirectCondition[];
};

export type ShlinkRedirectRule = ShlinkRedirectRuleData & {
priority: number;
};

export type ShlinkRedirectRulesList = {
defaultLongUrl: string;
redirectRules: ShlinkRedirectRule[];
};

export type ShlinkSetRedirectRulesData = {
redirectRules: ShlinkRedirectRuleData[];
};
20 changes: 20 additions & 0 deletions src/api/ShlinkApiClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import type {
ShlinkHealth,
ShlinkMercureInfo,
ShlinkOrphanVisitsParams,
ShlinkRedirectRulesList,
ShlinkSetRedirectRulesData,
ShlinkShortUrl,
ShlinkShortUrlsList,
ShlinkShortUrlsListParams,
Expand Down Expand Up @@ -88,6 +90,24 @@ export class ShlinkApiClient implements BaseShlinkApiClient {
);
}

// Short URL redirect rules

public async getShortUrlRedirectRules(shortCode: string, domain?: string | null): Promise<ShlinkRedirectRulesList> {
return this.performRequest<ShlinkRedirectRulesList>(
{ url: `/short-urls/${shortCode}/redirect-rules`, method: 'GET', query: { domain } },
);
}

public async setShortUrlRedirectRules(
shortCode: string,
domain: string | null | undefined,
data: ShlinkSetRedirectRulesData,
): Promise<ShlinkRedirectRulesList> {
return this.performRequest<ShlinkRedirectRulesList>(
{ url: `/short-urls/${shortCode}/redirect-rules`, method: 'POST', query: { domain }, body: data },
);
}

// Visits

public async getVisitsOverview(): Promise<ShlinkVisitsOverview> {
Expand Down
13 changes: 8 additions & 5 deletions src/api/utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import type {
ShlinkShortUrlsListNormalizedParams,
ShlinkShortUrlsListParams,
ShlinkShortUrlsOrder,
} from '../api-contract';
import type { ShlinkShortUrlsListParams, ShlinkShortUrlsOrder } from '../api-contract';

export type ApiVersion = 3;

Expand All @@ -12,6 +8,13 @@ const shortUrlsOrderToString = (order: ShlinkShortUrlsOrder): string | undefined
order.dir ? `${order.field}-${order.dir}` : undefined
);

type ShlinkShortUrlsListNormalizedParams =
Omit<ShlinkShortUrlsListParams, 'orderBy' | 'excludeMaxVisitsReached' | 'excludePastValidUntil'> & {
orderBy?: string;
excludeMaxVisitsReached?: 'true';
excludePastValidUntil?: 'true';
};

export const normalizeListParams = (
{ orderBy = {}, excludeMaxVisitsReached, excludePastValidUntil, ...rest }: ShlinkShortUrlsListParams,
): ShlinkShortUrlsListNormalizedParams => ({
Expand Down
49 changes: 49 additions & 0 deletions test/api/ShlinkApiClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import type { HttpClient } from '../../src';
import { ShlinkApiClient } from '../../src';
import type {
ShlinkDomain,
ShlinkRedirectRulesList,
ShlinkSetRedirectRulesData,
ShlinkShortUrl,
ShlinkShortUrlsOrder,
ShlinkVisitsList,
Expand Down Expand Up @@ -399,4 +401,51 @@ describe('ShlinkApiClient', () => {
expect(result).toEqual(resp);
});
});

describe('getShortUrlRedirectRules', () => {
it.each([
['the_domain', '?domain=the_domain'],
[null, ''],
[undefined, ''],
])('returns the redirect rules', async (domain, expectedQuery) => {
const resp: ShlinkRedirectRulesList = {
defaultLongUrl: 'foo',
redirectRules: [],
};
jsonRequest.mockResolvedValue(resp);

const result = await apiClient.getShortUrlRedirectRules('foo', domain);

expect(jsonRequest).toHaveBeenCalledWith(
expect.stringContaining(`/short-urls/foo/redirect-rules${expectedQuery}`),
expect.objectContaining({ method: 'GET' }),
);
expect(result).toEqual(resp);
});
});

describe('setShortUrlRedirectRules', () => {
it.each([
['the_domain', '?domain=the_domain'],
[null, ''],
[undefined, ''],
])('sets redirect rules', async (domain, expectedQuery) => {
const resp: ShlinkRedirectRulesList = {
defaultLongUrl: 'foo',
redirectRules: [],
};
const data: ShlinkSetRedirectRulesData = {
redirectRules: [],
};
jsonRequest.mockResolvedValue(resp);

const result = await apiClient.setShortUrlRedirectRules('foo', domain, data);

expect(jsonRequest).toHaveBeenCalledWith(
expect.stringContaining(`/short-urls/foo/redirect-rules${expectedQuery}`),
expect.objectContaining({ method: 'POST', body: JSON.stringify(data) }),
);
expect(result).toEqual(resp);
});
});
});

0 comments on commit 4920164

Please sign in to comment.