Skip to content

Commit

Permalink
Ensure generating server IDs work even if server URLs are invalid
Browse files Browse the repository at this point in the history
  • Loading branch information
acelaya committed Nov 1, 2024
1 parent 645abea commit 5e95a7a
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 3 deletions.
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,27 @@ 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]
### Added
* * [#1360](https://github.com/shlinkio/shlink-web-client/issues/1360) Added ability for server IDs to be generated based on the server name and URL, instead of generating a random UUID.

This can improve sharing a predefined set of servers cia servers.json, env vars, or simply export and import your servers in some other device, and then be able to share server URLs which continue working.

All existing servers will keep their generated IDs in existing devices for backwards compatibility, but newly created servers will use the new approach.

### Changed
* *Nothing*

### Deprecated
* *Nothing*

### Removed
* *Nothing*

### Fixed
* *Nothing*


## [4.2.2] - 2024-10-19
### Added
* *Nothing*
Expand Down
20 changes: 17 additions & 3 deletions src/servers/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,23 @@ import type { ServerData, ServersMap, ServerWithId } from '../data';
* in lowercase and replacing invalid URL characters with hyphens.
*/
function idForServer(server: ServerData): string {
// TODO Handle invalid URLs. If not valid url, use the value as is
const url = new URL(server.url);
return `${server.name} ${url.host}`.toLowerCase().replace(/[^a-zA-Z0-9-_.~]/g, '-');
let urlSegment = server.url;
try {
const { host, pathname } = new URL(urlSegment);
urlSegment = host;

// Remove leading slash from pathname
const normalizedPathname = pathname.substring(1);

// Include pathname in the ID, if not empty
if (normalizedPathname.length > 0) {
urlSegment = `${urlSegment} ${normalizedPathname}`;
}
} catch {
// If the server URL is not valid, use the value as is
}

return `${server.name} ${urlSegment}`.toLowerCase().replace(/[^a-zA-Z0-9-_.~]/g, '-');
}

export function serversListToMap(servers: ServerWithId[]): ServersMap {
Expand Down
26 changes: 26 additions & 0 deletions test/servers/helpers/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,31 @@ describe('index', () => {
expect.objectContaining({ id: 'baz-s.test' }),
]);
});

it('includes server paths when not empty', () => {
const result = ensureUniqueIds({}, [
fromPartial({ name: 'Foo', url: 'https://example.com' }),
fromPartial({ name: 'Bar', url: 'https://s.test/some/path' }),
fromPartial({ name: 'Baz', url: 'https://s.test/some/other-path-here/123' }),
]);

expect(result).toEqual([
expect.objectContaining({ id: 'foo-example.com' }),
expect.objectContaining({ id: 'bar-s.test-some-path' }),
expect.objectContaining({ id: 'baz-s.test-some-other-path-here-123' }),
]);
});

it('uses server URL verbatim when it is not a valid URL', () => {
const result = ensureUniqueIds({}, [
fromPartial({ name: 'Foo', url: 'invalid' }),
fromPartial({ name: 'Bar', url: 'this is not a URL' }),
]);

expect(result).toEqual([
expect.objectContaining({ id: 'foo-invalid' }),
expect.objectContaining({ id: 'bar-this-is-not-a-url' }),
]);
});
});
});

0 comments on commit 5e95a7a

Please sign in to comment.