Skip to content

Commit

Permalink
feat: implement encodePointerUriFragment and consume it in pathToPoin…
Browse files Browse the repository at this point in the history
…ter (#122)
  • Loading branch information
P0lip authored May 23, 2023
1 parent 79cfa74 commit 4a2f9e8
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 6 deletions.
54 changes: 54 additions & 0 deletions src/__tests__/bundle.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1021,6 +1021,60 @@ describe('bundleTargetPath()', () => {
});
});

it('should encode pointers', () => {
const document = {
definitions: {
'User Admin': {
id: 'foo',
address: {
$ref: '#/definitions/%25Address',
},
},
'%Address': {
street: 'foo',
user: {
$ref: '#/definitions/user',
},
},
card: {
zip: '20815',
},
},
__target__: {
entity: {
$ref: '#/definitions/User%20Admin',
},
},
};

const clone = cloneDeep(document);

const result = bundleTarget({
document: clone,
path: '#/__target__',
});

expect(result).toEqual({
__bundled__: {
'%Address': {
street: 'foo',
user: {
$ref: '#/definitions/user',
},
},
'User Admin': {
address: {
$ref: '#/__bundled__/%25Address',
},
id: 'foo',
},
},
entity: {
$ref: '#/__bundled__/User%20Admin',
},
});
});

describe('when custom keyProvider is provided', () => {
it('should work', () => {
const document = {
Expand Down
8 changes: 6 additions & 2 deletions src/__tests__/pathToPointer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ test('pathToPointer', () => {
expect(pathToPointer(['paths', 'foo~users'])).toEqual('#/paths/foo~0users');
expect(pathToPointer([])).toEqual('#');
expect(pathToPointer([''])).toEqual('#/');
expect(pathToPointer(['paths', '/user/{userId}'])).toEqual('#/paths/~1user~1{userId}');
expect(pathToPointer(['paths', '/user/{userId}/~foo'])).toEqual('#/paths/~1user~1{userId}~1~0foo');
expect(pathToPointer(['paths', '/user/{userId}'])).toEqual('#/paths/~1user~1%7BuserId%7D');
expect(pathToPointer(['paths', '/user/{userId}/~foo'])).toEqual('#/paths/~1user~1%7BuserId%7D~1~0foo');
expect(pathToPointer(['$defs', 'User Model'])).toEqual('#/%24defs/User%20Model');
expect(pathToPointer(['\uD803\uDE6D', 'valid-pair'])).toEqual('#/%F0%90%B9%AD/valid-pair');
expect(pathToPointer(['\uD83D', 'unmatched-surrogate'])).toEqual('#/\uD83D/unmatched-surrogate');
expect(pathToPointer(['\uD83D', '\uDC00unmatched-surrogate'])).toEqual('#/\uD83D/\uDC00unmatched-surrogate');
});
1 change: 1 addition & 0 deletions src/decodePointerUriFragment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { decodePointer as decodePointerUriFragment } from './decodePointer';
15 changes: 15 additions & 0 deletions src/encodePointerUriFragment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Segment } from '@stoplight/types';

import { encodePointerFragment } from './encodePointerFragment';
import { encodeUriPointer } from './encodeUriPointer';

/**
* Escapes special json pointer characters in a value.
* Percent-encode characters.
*
* @example encodePointer('/paths/~users) => '~1paths~1~0users'
*/
export const encodePointerUriFragment = (value: Segment): Segment => {
const encoded = encodePointerFragment(value);
return typeof encoded === 'number' ? encoded : encodeUriPointer(encoded);
};
10 changes: 10 additions & 0 deletions src/encodeUriPointer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const ENCODABLE_CHAR = /[^a-zAZ09_.!~*'()\/\-\u{D800}-\u{DFFF}]/gu;

/**
* Percent-encode a JSON Pointer.
*
* encodePointer('paths/users) => '#/paths/~1users'
*/
export function encodeUriPointer(pointer: string): string {
return pointer.replace(ENCODABLE_CHAR, encodeURIComponent);
}
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
export * from './bundle';
export * from './decodePointer';
export * from './decodePointerFragment';
export * from './decodePointerUriFragment';
export * from './decycle';
export * from './encodePointer';
export * from './encodePointerFragment';
export * from './encodePointerUriFragment';
export * from './encodeUriPointer';
export * from './extractPointerFromRef';
export * from './extractSourceFromRef';
export * from './getFirstPrimitiveProperty';
Expand Down
4 changes: 2 additions & 2 deletions src/pathToPointer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { JsonPath } from '@stoplight/types';

import { encodePointerFragment } from './encodePointerFragment';
import { encodePointerUriFragment } from './encodePointerUriFragment';

export const pathToPointer = (path: JsonPath): string => {
return encodeUriFragmentIdentifier(path);
Expand All @@ -15,5 +15,5 @@ const encodeUriFragmentIdentifier = (path: JsonPath): string => {
return '#';
}

return `#/${path.map(encodePointerFragment).join('/')}`;
return `#/${path.map(encodePointerUriFragment).join('/')}`;
};
4 changes: 2 additions & 2 deletions src/pointerToPath.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { JsonPath } from '@stoplight/types';

import { decodePointer } from './decodePointer';
import { decodePointerUriFragment } from './decodePointerUriFragment';

export const pointerToPath = (pointer: string): JsonPath => {
return decodeUriFragmentIdentifier(pointer);
Expand All @@ -12,7 +12,7 @@ const decodeFragmentSegments = (segments: string[]): string[] => {
let i = -1;

while (++i < len) {
res.push(decodePointer(segments[i]));
res.push(decodePointerUriFragment(segments[i]));
}

return res;
Expand Down

0 comments on commit 4a2f9e8

Please sign in to comment.