From c48f84cb8e3e5887c172f38df5e1b7bf554c79fa Mon Sep 17 00:00:00 2001 From: Vitaly Tomilov Date: Thu, 19 Jul 2018 13:07:49 +0100 Subject: [PATCH] replacing segments with path --- README.md | 14 ++++++------ package.json | 2 +- src/index.d.ts | 4 ++-- src/index.js | 22 +++++++++---------- test/main.js | 4 ++-- test/main.ts | 4 ++-- test/mainSpec.js | 57 ++++++++++++++++++++++++++++-------------------- 7 files changed, 58 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index ee32f61..ffe443f 100644 --- a/README.md +++ b/README.md @@ -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: @@ -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!' @@ -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]. @@ -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. diff --git a/package.json b/package.json index 637d79b..dbced50 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/index.d.ts b/src/index.d.ts index 699ef4a..ce5eb87 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -14,7 +14,7 @@ interface IConnectionDefaults { hosts?: Array user?: string password?: string - segments?: string[] + path?: string[] params?: { [name: string]: string } } @@ -25,7 +25,7 @@ export class ConnectionString { hosts?: Array; user?: string; password?: string; - segments?: string[]; + path?: string[]; params?: { [name: string]: string }; static parseHost(host: string): IHost diff --git a/src/index.js b/src/index.js index 5a44022..0f770a9 100644 --- a/src/index.js +++ b/src/index.js @@ -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(/\/|\?/); @@ -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)); }); } @@ -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); @@ -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); }); } @@ -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; } } diff --git a/test/main.js b/test/main.js index 1f59526..911465a 100644 --- a/test/main.js +++ b/test/main.js @@ -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 = { diff --git a/test/main.ts b/test/main.ts index 69b7b60..05544ef 100644 --- a/test/main.ts +++ b/test/main.ts @@ -3,7 +3,7 @@ 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) { @@ -11,7 +11,7 @@ if ('protocol' in a) { 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'; diff --git a/test/mainSpec.js b/test/mainSpec.js index c391cbc..c2295f2 100644 --- a/test/mainSpec.js +++ b/test/mainSpec.js @@ -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(() => { @@ -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: ['$-_.+!*\'()']}); }); }); @@ -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', () => { @@ -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']}); }); }); @@ -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', () => { @@ -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'}}); @@ -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({});