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.
- package/dist/e2e/utils/cli-runner.d.ts +1 -0
- package/dist/e2e/utils/cli-runner.js +3 -2
- package/dist/e2e/utils/cli-runner.js.map +1 -1
- package/dist/package.json +1 -1
- package/dist/src/services/url/url-parser.service.d.ts +1 -1
- package/dist/src/services/url/url-parser.service.js +12 -6
- package/dist/src/services/url/url-parser.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/docs/url-format.md +25 -0
- package/e2e/tests/01-auth.e2e-spec.ts +19 -0
- package/e2e/utils/cli-runner.ts +11 -3
- package/package.json +1 -1
- package/src/services/url/__tests__/url-parser.service.spec.ts +162 -0
- package/src/services/url/url-parser.service.ts +21 -6
package/docs/url-format.md
CHANGED
|
@@ -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
|
});
|
package/e2e/utils/cli-runner.ts
CHANGED
|
@@ -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 {
|
|
87
|
-
|
|
88
|
-
|
|
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
|
+
"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(
|
|
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
|
|
51
|
-
|
|
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(
|
|
69
|
-
|
|
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;
|