revisium 2.3.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,6 +6,8 @@ Revisium CLI uses a special URL format to specify project connections.
6
6
 
7
7
  ```text
8
8
  revisium://[user:password@]host[:port]/organization/project/branch[:revision][?params]
9
+ revisium+http://[user:password@]host[:port]/organization/project/branch[:revision][?params]
10
+ revisium+https://[user:password@]host[:port]/organization/project/branch[:revision][?params]
9
11
  ```
10
12
 
11
13
  ## URL Parts
@@ -44,6 +46,29 @@ revisium://[user:password@]host[:port]/organization/project/branch[:revision][?p
44
46
  | `127.0.0.1` | http | 8080 |
45
47
  | Other hosts | https | 443 |
46
48
 
49
+ ### Explicit Protocol Override
50
+
51
+ Use `revisium+http://` or `revisium+https://` to force a specific protocol, bypassing the auto-detection logic. This is useful in Kubernetes or other environments where internal services use HTTP on non-localhost hostnames.
52
+
53
+ ```text
54
+ revisium+http://[user:password@]host[:port]/organization/project/branch[:revision][?params]
55
+ revisium+https://[user:password@]host[:port]/organization/project/branch[:revision][?params]
56
+ ```
57
+
58
+ | Prefix | Behavior |
59
+ |--------|----------|
60
+ | `revisium://` | Auto-detect (localhost → http, others → https) |
61
+ | `revisium+http://` | Force HTTP |
62
+ | `revisium+https://` | Force HTTPS |
63
+
64
+ ```bash
65
+ # Force HTTP for internal Kubernetes service
66
+ revisium+http://admin:pass@payment-config-svc:80/org/proj/master?token=xxx
67
+
68
+ # Force HTTPS explicitly
69
+ revisium+https://admin:pass@my-host/org/proj/master
70
+ ```
71
+
47
72
  ## Authentication Examples
48
73
 
49
74
  ### Token Authentication (Recommended)
@@ -156,4 +156,23 @@ describe('CLI Authentication', () => {
156
156
  expect(result.exitCode).toBe(0);
157
157
  });
158
158
  });
159
+
160
+ describe('Protocol override (revisium+http://)', () => {
161
+ it('connects via explicit HTTP protocol', async () => {
162
+ const token = process.env.E2E_ADMIN_TOKEN!;
163
+
164
+ const result = await runCli([
165
+ 'schema',
166
+ 'save',
167
+ '--url',
168
+ buildUrl(projectName, { token, protocol: 'http' }),
169
+ '--folder',
170
+ tempDir,
171
+ ]);
172
+
173
+ expect(result.exitCode).toBe(0);
174
+ expect(result.stdout).toContain('Authenticated as admin');
175
+ expect(result.stdout).toContain('Successfully saved');
176
+ });
177
+ });
159
178
  });
@@ -81,11 +81,19 @@ export function buildUrl(
81
81
  branch?: string;
82
82
  revision?: 'draft' | 'head';
83
83
  token?: string;
84
+ protocol?: 'http' | 'https';
84
85
  } = {},
85
86
  ): string {
86
- const { orgId = 'admin', branch = 'master', revision, token } = options;
87
-
88
- let url = `revisium://localhost:8082/${orgId}/${projectName}/${branch}`;
87
+ const {
88
+ orgId = 'admin',
89
+ branch = 'master',
90
+ revision,
91
+ token,
92
+ protocol,
93
+ } = options;
94
+
95
+ const scheme = protocol ? `revisium+${protocol}` : 'revisium';
96
+ let url = `${scheme}://localhost:8082/${orgId}/${projectName}/${branch}`;
89
97
 
90
98
  if (revision) {
91
99
  url += `:${revision}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "revisium",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "homepage": "https://revisium.io",
5
5
  "description": "A CLI tool for interacting with Revisium instances, providing migration management, schema export, and data export capabilities.",
6
6
  "author": "Anton Kashirov",
@@ -0,0 +1,162 @@
1
+ import { UrlParserService } from '../url-parser.service';
2
+
3
+ describe('UrlParserService', () => {
4
+ let service: UrlParserService;
5
+
6
+ beforeEach(() => {
7
+ service = new UrlParserService();
8
+ });
9
+
10
+ describe('parse - revisium+http://', () => {
11
+ it('forces HTTP for non-localhost host', () => {
12
+ const result = service.parse(
13
+ 'revisium+http://admin:pass@my-service:80/org/proj/branch',
14
+ );
15
+
16
+ expect(result).toEqual({
17
+ baseUrl: 'http://my-service:80',
18
+ username: 'admin',
19
+ password: 'pass',
20
+ token: undefined,
21
+ apikey: undefined,
22
+ organization: 'org',
23
+ project: 'proj',
24
+ branch: 'branch',
25
+ revision: undefined,
26
+ });
27
+ });
28
+
29
+ it('forces HTTP for non-localhost host with draft revision', () => {
30
+ const result = service.parse(
31
+ 'revisium+http://admin:pass@my-service:80/org/proj/branch:draft',
32
+ );
33
+
34
+ expect(result).toEqual({
35
+ baseUrl: 'http://my-service:80',
36
+ username: 'admin',
37
+ password: 'pass',
38
+ token: undefined,
39
+ apikey: undefined,
40
+ organization: 'org',
41
+ project: 'proj',
42
+ branch: 'branch',
43
+ revision: 'draft',
44
+ });
45
+ });
46
+
47
+ it('forces HTTP without port', () => {
48
+ const result = service.parse(
49
+ 'revisium+http://admin:pass@my-service/org/proj/branch',
50
+ );
51
+
52
+ expect(result).toEqual({
53
+ baseUrl: 'http://my-service',
54
+ username: 'admin',
55
+ password: 'pass',
56
+ token: undefined,
57
+ apikey: undefined,
58
+ organization: 'org',
59
+ project: 'proj',
60
+ branch: 'branch',
61
+ revision: undefined,
62
+ });
63
+ });
64
+
65
+ it('forces HTTP with token auth', () => {
66
+ const result = service.parse(
67
+ 'revisium+http://payment-svc:80/org/proj/master?token=abc123',
68
+ );
69
+
70
+ expect(result).toEqual({
71
+ baseUrl: 'http://payment-svc:80',
72
+ username: undefined,
73
+ password: undefined,
74
+ token: 'abc123',
75
+ apikey: undefined,
76
+ organization: 'org',
77
+ project: 'proj',
78
+ branch: 'master',
79
+ revision: undefined,
80
+ });
81
+ });
82
+ });
83
+
84
+ describe('parse - revisium+https://', () => {
85
+ it('forces HTTPS for any host', () => {
86
+ const result = service.parse(
87
+ 'revisium+https://admin:pass@my-service/org/proj/branch',
88
+ );
89
+
90
+ expect(result).toEqual({
91
+ baseUrl: 'https://my-service',
92
+ username: 'admin',
93
+ password: 'pass',
94
+ token: undefined,
95
+ apikey: undefined,
96
+ organization: 'org',
97
+ project: 'proj',
98
+ branch: 'branch',
99
+ revision: undefined,
100
+ });
101
+ });
102
+
103
+ it('forces HTTPS even for localhost', () => {
104
+ const result = service.parse(
105
+ 'revisium+https://admin:pass@localhost:8443/org/proj/main',
106
+ );
107
+
108
+ expect(result).toEqual({
109
+ baseUrl: 'https://localhost:8443',
110
+ username: 'admin',
111
+ password: 'pass',
112
+ token: undefined,
113
+ apikey: undefined,
114
+ organization: 'org',
115
+ project: 'proj',
116
+ branch: 'main',
117
+ revision: undefined,
118
+ });
119
+ });
120
+ });
121
+
122
+ describe('parse - revisium:// (unchanged behavior)', () => {
123
+ it('auto-detects http for localhost', () => {
124
+ const result = service.parse(
125
+ 'revisium://admin:pass@localhost:8888/org/proj/branch',
126
+ );
127
+
128
+ expect(result.baseUrl).toBe('http://localhost:8888');
129
+ });
130
+
131
+ it('auto-detects https for remote host', () => {
132
+ const result = service.parse(
133
+ 'revisium://admin:pass@cloud.revisium.io/org/proj/branch',
134
+ );
135
+
136
+ expect(result.baseUrl).toBe('https://cloud.revisium.io');
137
+ });
138
+ });
139
+
140
+ describe('buildBaseUrlFromHost - protocolOverride', () => {
141
+ it('uses override when provided', () => {
142
+ expect(service.buildBaseUrlFromHost('my-service:80', 'http')).toBe(
143
+ 'http://my-service:80',
144
+ );
145
+ });
146
+
147
+ it('override takes precedence over localhost auto-detect', () => {
148
+ expect(service.buildBaseUrlFromHost('localhost:8080', 'https')).toBe(
149
+ 'https://localhost:8080',
150
+ );
151
+ });
152
+
153
+ it('falls back to auto-detect when no override', () => {
154
+ expect(service.buildBaseUrlFromHost('localhost:8080')).toBe(
155
+ 'http://localhost:8080',
156
+ );
157
+ expect(service.buildBaseUrlFromHost('cloud.revisium.io')).toBe(
158
+ 'https://cloud.revisium.io',
159
+ );
160
+ });
161
+ });
162
+ });
@@ -27,6 +27,14 @@ function validatePort(port: number, context: string): void {
27
27
  @Injectable()
28
28
  export class UrlParserService {
29
29
  parse(input: string): RevisiumUrl {
30
+ if (input.startsWith('revisium+http://')) {
31
+ return this.parseRevisiumUrl(input, 'http');
32
+ }
33
+
34
+ if (input.startsWith('revisium+https://')) {
35
+ return this.parseRevisiumUrl(input, 'https');
36
+ }
37
+
30
38
  if (input.startsWith('revisium://')) {
31
39
  return this.parseRevisiumUrl(input);
32
40
  }
@@ -39,7 +47,10 @@ export class UrlParserService {
39
47
  return `${protocol}://${host}${portSuffix}`;
40
48
  }
41
49
 
42
- buildBaseUrlFromHost(hostWithPort: string): string {
50
+ buildBaseUrlFromHost(
51
+ hostWithPort: string,
52
+ protocolOverride?: 'http' | 'https',
53
+ ): string {
43
54
  const [host, portStr] = hostWithPort.split(':');
44
55
  const port = portStr ? Number.parseInt(portStr, 10) : undefined;
45
56
 
@@ -47,8 +58,9 @@ export class UrlParserService {
47
58
  validatePort(port, `in "${hostWithPort}"`);
48
59
  }
49
60
 
50
- const isLocalhost = LOCALHOST_HOSTS.has(host.toLowerCase());
51
- const protocol = isLocalhost ? 'http' : 'https';
61
+ const protocol =
62
+ protocolOverride ??
63
+ (LOCALHOST_HOSTS.has(host.toLowerCase()) ? 'http' : 'https');
52
64
 
53
65
  if (port) {
54
66
  return this.buildBaseUrl(protocol, host, port);
@@ -65,8 +77,11 @@ export class UrlParserService {
65
77
  validatePort(port, context);
66
78
  }
67
79
 
68
- private parseRevisiumUrl(url: string): RevisiumUrl {
69
- const withoutProtocol = url.replace('revisium://', '');
80
+ private parseRevisiumUrl(
81
+ url: string,
82
+ forceProtocol?: 'http' | 'https',
83
+ ): RevisiumUrl {
84
+ const withoutProtocol = url.replace(/^revisium(\+https?)?:\/\//, '');
70
85
 
71
86
  const [mainPart, queryString] = withoutProtocol.split('?');
72
87
  const queryParams = this.parseQueryParams(queryString);
@@ -104,7 +119,7 @@ export class UrlParserService {
104
119
  const pathParts = hostAndPath.split('/');
105
120
  const hostWithPort = pathParts[0];
106
121
 
107
- const baseUrl = this.buildBaseUrlFromHost(hostWithPort);
122
+ const baseUrl = this.buildBaseUrlFromHost(hostWithPort, forceProtocol);
108
123
 
109
124
  const branchWithRevision = pathParts[3] || undefined;
110
125
  let branch: string | undefined;