Skip to content

Commit

Permalink
replacing segments with path
Browse files Browse the repository at this point in the history
  • Loading branch information
vitaly-t committed Jul 19, 2018
1 parent 6a23743 commit c48f84c
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 49 deletions.
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Advanced URL Connection String Parser, with fully optional syntax.
Takes a URL connection string (with every element being optional):

```
protocol://user:password@host1:123,[abcd::]:456/seg1/seg2?p1=val1&msg=hello+world!
protocol://user:password@host1:123,[abcd::]:456/one/two2?p1=val1&msg=hello+world!
```

and converts it into an object that contains only what's specified:
Expand All @@ -23,7 +23,7 @@ and converts it into an object that contains only what's specified:
{name: 'host1', port: 123, isIPv6: false},
{name: 'abcd::', port: 456, isIPv6: true}
],
segments: ['seg1', 'seg2'],
path: ['one', 'two'],
params: {
p1: 'val1',
msg: 'hello world!'
Expand All @@ -50,11 +50,11 @@ Unlike the default URL parser, this one supports the following:
* `localhost` => `{hosts: [{name: 'localhost', isIPv6: false}]`
* `localhost:12345` => `{hosts: [{name: 'localhost', port: 12345, isIPv6: false}]`
* `[12ab:34cd]:123` => `{hosts: [{name: '12ab:34cd', port: 123, isIPv6: true}]`
* `abc:///seg1?p1=val` => `{protocol: 'abc', segments: ['seg1'], params: {p1: 'val'}}`
* `abc:///one?p1=val` => `{protocol: 'abc', path: ['one'], params: {p1: 'val'}}`
* `:12345` => `{hosts: [{port: 12345}]`
* `username@` => `{user: 'username'}`
* `:pass123@` => `{password: 'pass123'}`
* `/seg1/seg2` => `{segments: ['seg1', 'seg2']}`
* `/one/two` => `{path: ['seg1', 'seg2']}`
* `?p1=1&p2=a+b` => `{params: {p1: '1', p2: 'a b'}}`

For more short-syntax examples see [Optional Format].
Expand Down Expand Up @@ -121,9 +121,9 @@ combined with the defaults, if those were specified, plus methods as documented

The method takes an object with default values, and safely combines it with what's missing in the current object.

Please note that while missing defaults for `hosts` and `params` are merged with the existing sets, `segments` are not,
since their order is often important, so the defaults for `segments` are only used when there are no segments
in the current object, though you can override segments manually, just like everything else in the object.
Please note that while missing defaults for `hosts` and `params` are merged with the existing sets, for the `path` they are not,
since their order is often important, so the defaults for `path` are only used when there are no path segments
in the current object, though you can override the path manually, just like everything else in the object.

You can call this method either directly or when parsing/constructing the connection string, as the second parameter.

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "connection-string",
"version": "0.9.1",
"version": "1.0.0",
"description": "Advanced URL Connection String Parser.",
"main": "src/index.js",
"typings": "src/index.d.ts",
Expand Down
4 changes: 2 additions & 2 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ interface IConnectionDefaults {
hosts?: Array<IHost>
user?: string
password?: string
segments?: string[]
path?: string[]
params?: { [name: string]: string }
}

Expand All @@ -25,7 +25,7 @@ export class ConnectionString {
hosts?: Array<IHost>;
user?: string;
password?: string;
segments?: string[];
path?: string[];
params?: { [name: string]: string };

static parseHost(host: string): IHost
Expand Down
22 changes: 11 additions & 11 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
}

// Extracting hosts details...
// (if it starts with `/`, it is the first segment, so no hosts specified)
// (if it starts with `/`, it is the first path segment, so no hosts specified)
if (cs[0] !== '/') {

var endOfHosts = cs.search(/\/|\?/);
Expand All @@ -67,10 +67,10 @@
}
}

// Extracting segments:
// Extracting the path:
m = cs.match(/\/([\w-_.+!*'()$%]+)/g);
if (m) {
this.segments = m.map(function (s) {
this.path = m.map(function (s) {
return decode(s.substr(1));
});
}
Expand All @@ -97,7 +97,7 @@
}

function validateUrl(url) {
var idx = url.search(/[^A-Za-z0-9-._~:/?#[\]@!$&'()*+,;=%]/);
var idx = url.search(/[^A-Za-z0-9-._~:/?[\]@!$&'()*+,;=%]/);
if (idx >= 0) {
var s = JSON.stringify(url[idx]).replace(/^"|"$/g, '\'');
throw new Error('Invalid URL character ' + s + ' at position ' + idx);
Expand Down Expand Up @@ -168,8 +168,8 @@
return fullHostName(h, options);
}).join();
}
if (Array.isArray(this.segments) && this.segments.length) {
this.segments.forEach(function (seg) {
if (Array.isArray(this.path) && this.path.length) {
this.path.forEach(function (seg) {
s += '/' + encode(seg, options);
});
}
Expand Down Expand Up @@ -246,12 +246,12 @@
this.password = trim(defaults.password);
}

// Since the order of segments is usually important, we set default
// segments as they are, but only when they are missing completely:
if (!('segments' in this) && Array.isArray(defaults.segments)) {
var s = defaults.segments.filter(isText);
// Since the order of path segments is usually important, we set default
// path segments as they are, but only when they are missing completely:
if (!('path' in this) && Array.isArray(defaults.path)) {
var s = defaults.path.filter(isText);
if (s.length) {
this.segments = s;
this.path = s;
}
}

Expand Down
4 changes: 2 additions & 2 deletions test/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ var src_1 = require("../src");
var a = new src_1.ConnectionString('protocol://');
var b = new src_1.ConnectionString('protocol://', {});
var c = new src_1.ConnectionString('protocol://', {
segments: ['one', 'two']
path: ['one', 'two']
});
if ('protocol' in a) {
var protocol = a.protocol;
var pass = a.password;
}
var segment1 = a.segments[0];
var segment1 = a.path[0];
var param1 = a.params['first'];
a.params['first'] = 'hello';
a.params = {
Expand Down
4 changes: 2 additions & 2 deletions test/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import {ConnectionString, IHost} from '../src'
const a = new ConnectionString('protocol://');
const b = new ConnectionString('protocol://', {});
const c = new ConnectionString('protocol://', {
segments: ['one', 'two']
path: ['one', 'two']
});

if ('protocol' in a) {
const protocol = a.protocol;
const pass = a.password;
}

const segment1: string = a.segments[0];
const segment1: string = a.path[0];
const param1: string = a.params['first'];

a.params['first'] = 'hello';
Expand Down
57 changes: 33 additions & 24 deletions test/mainSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ describe('init', () => {
parse(123);
}).toThrow(error);
});
it('must throw on invalid symbols', () => {
const invalidSymbols = '`"#^<>{}\\| \r\n\t';
invalidSymbols.split('').forEach(s => {
const a = JSON.stringify(s).replace(/^"|"$/g, '\'');
expect(() => {
parse('a' + s + 'b');
}).toThrow('Invalid URL character ' + a + ' at position 1');
});
});
it('must throw on invalid defaults', () => {
const error = 'Invalid \'defaults\' parameter!';
expect(() => {
Expand Down Expand Up @@ -199,29 +208,29 @@ describe('user + password', () => {
});
});

describe('segments', () => {
it('must ignore empty segments', () => {
describe('path', () => {
it('must ignore empty path', () => {
expect(parse('/')).toEqual({});
expect(parse('//')).toEqual({});
expect(parse('///')).toEqual({});
expect(parse('//')).toEqual({});
});
it('must enumerate all segments', () => {
expect(parse('/one/two')).toEqual({segments: ['one', 'two']});
it('must enumerate all path segments', () => {
expect(parse('/one/two')).toEqual({path: ['one', 'two']});
});
it('must recognize after protocol', () => {
expect(parse('abc:///one')).toEqual({protocol: 'abc', segments: ['one']});
expect(parse('abc:////one')).toEqual({protocol: 'abc', segments: ['one']});
expect(parse('abc:///one')).toEqual({protocol: 'abc', path: ['one']});
expect(parse('abc:////one')).toEqual({protocol: 'abc', path: ['one']});
});
it('must recognize with empty protocol', () => {
expect(parse(':///one')).toEqual({segments: ['one']});
expect(parse(':////one')).toEqual({segments: ['one']});
expect(parse(':///one')).toEqual({path: ['one']});
expect(parse(':////one')).toEqual({path: ['one']});
});
it('must decode URL-encoded characters', () => {
expect(parse('/one%20/%3Ftwo')).toEqual({segments: ['one ', '?two']});
expect(parse('/one%20/%3Ftwo')).toEqual({path: ['one ', '?two']});
});
it('must support special symbols', () => {
expect(parse('/$-_.+!*\'()')).toEqual({segments: ['$-_.+!*\'()']});
expect(parse('/$-_.+!*\'()')).toEqual({path: ['$-_.+!*\'()']});
});
});

Expand Down Expand Up @@ -273,14 +282,14 @@ describe('params', () => {
});

describe('complex', () => {
it('protocol + segment', () => {
expect(parse('a:///seg')).toEqual({
it('protocol + path', () => {
expect(parse('a:///path')).toEqual({
protocol: 'a',
segments: ['seg']
path: ['path']
});
expect(parse('a:////seg')).toEqual({
expect(parse('a:////path')).toEqual({
protocol: 'a',
segments: ['seg']
path: ['path']
});
});
it('protocol + params', () => {
Expand All @@ -298,7 +307,7 @@ describe('complex', () => {
});
});
it('must not lose details after the port', () => {
expect(parse(':123/one')).toEqual({hosts: [{port: 123}], segments: ['one']});
expect(parse(':123/one')).toEqual({hosts: [{port: 123}], path: ['one']});
});
});

Expand Down Expand Up @@ -332,13 +341,13 @@ describe('toString', () => {
expect(parse('server:123').toString()).toBe('server:123');
expect(parse('[::]:123').toString()).toBe('[::]:123');
});
it('must encode segments', () => {
it('must encode path segments', () => {
expect(parse('/a%20b').toString()).toBe('/a%20b');
expect(parse('/a/b%20/c').toString()).toBe('/a/b%20/c');
});
it('must ignore empty segment list', () => {
it('must ignore empty path', () => {
const a = parse('');
a.segments = [];
a.path = [];
expect(a.toString()).toBe('');
});
it('must encode params', () => {
Expand Down Expand Up @@ -452,8 +461,8 @@ describe('setDefaults', () => {
it('must set the default password', () => {
expect(parse('').setDefaults({password: 'abc'})).toEqual({password: 'abc'});
});
it('must set the default segments', () => {
expect(parse('').setDefaults({segments: ['abc']})).toEqual({segments: ['abc']});
it('must set the default path', () => {
expect(parse('').setDefaults({path: ['abc']})).toEqual({path: ['abc']});
});
it('must set the default params', () => {
expect(parse('').setDefaults({params: {p1: 'abc'}})).toEqual({params: {p1: 'abc'}});
Expand All @@ -469,9 +478,9 @@ describe('setDefaults', () => {
}
});
});
it('must ignore empty segments', () => {
expect(parse('').setDefaults({segments: ['', 123, true, ' ']})).toEqual({});
expect(parse('').setDefaults({segments: 123})).toEqual({});
it('must ignore empty path segments', () => {
expect(parse('').setDefaults({path: ['', 123, true, ' ']})).toEqual({});
expect(parse('').setDefaults({path: 123})).toEqual({});
});
it('must ignore invalid and empty hosts', () => {
expect(parse('').setDefaults({hosts: 1})).toEqual({});
Expand Down

0 comments on commit c48f84c

Please sign in to comment.