From ced0f45fdd21534c78cbddbd9b3c1f81703947ce Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Fri, 17 May 2024 20:06:52 -0700 Subject: [PATCH] transformTest for valid props --- .../utils.chain.remove.test.ts.snap | 4 +- .../__snapshots__/utils.chain.test.ts.snap | 20 ++++++- __tests__/utils.assetlist.test.ts | 13 ++++- __tests__/utils.chain.remove.test.ts | 12 +++- __tests__/utils.chain.test.ts | 14 ++++- src/index.ts | 6 +- src/utils.ts | 57 ++++++++++++------- types/utils.d.ts | 2 +- 8 files changed, 95 insertions(+), 33 deletions(-) diff --git a/__tests__/__snapshots__/utils.chain.remove.test.ts.snap b/__tests__/__snapshots__/utils.chain.remove.test.ts.snap index f49ed75..dcb671f 100644 --- a/__tests__/__snapshots__/utils.chain.remove.test.ts.snap +++ b/__tests__/__snapshots__/utils.chain.remove.test.ts.snap @@ -145,7 +145,7 @@ exports[`JSONSchemaPatch transformer w excludes 1`] = ` "apis": { "additionalProperties": false, "properties": { - "evm-http-jsonrpc": { + "evmHttpJsonrpc": { "items": { "$ref": "#/$defs/endpoint", }, @@ -157,7 +157,7 @@ exports[`JSONSchemaPatch transformer w excludes 1`] = ` }, "type": "array", }, - "grpc-web": { + "grpcWeb": { "items": { "$ref": "#/$defs/endpoint", }, diff --git a/__tests__/__snapshots__/utils.chain.test.ts.snap b/__tests__/__snapshots__/utils.chain.test.ts.snap index 4cb90a9..b2ad29e 100644 --- a/__tests__/__snapshots__/utils.chain.test.ts.snap +++ b/__tests__/__snapshots__/utils.chain.test.ts.snap @@ -145,7 +145,7 @@ exports[`JSONSchemaPatch transformer 1`] = ` "apis": { "additionalProperties": false, "properties": { - "evm-http-jsonrpc": { + "evmHttpJsonrpc": { "items": { "$ref": "#/$defs/endpoint", }, @@ -157,7 +157,7 @@ exports[`JSONSchemaPatch transformer 1`] = ` }, "type": "array", }, - "grpc-web": { + "grpcWeb": { "items": { "$ref": "#/$defs/endpoint", }, @@ -1003,6 +1003,22 @@ exports[`createJSONSchemaPatchOperations 1`] = ` "oldName": "persistent_peers", }, }, + { + "op": "renameProperty", + "path": "/properties/apis", + "value": { + "newName": "grpcWeb", + "oldName": "grpc-web", + }, + }, + { + "op": "renameProperty", + "path": "/properties/apis", + "value": { + "newName": "evmHttpJsonrpc", + "oldName": "evm-http-jsonrpc", + }, + }, { "op": "renameProperty", "path": "/$defs/explorer", diff --git a/__tests__/utils.assetlist.test.ts b/__tests__/utils.assetlist.test.ts index 0f98732..b7ccac5 100644 --- a/__tests__/utils.assetlist.test.ts +++ b/__tests__/utils.assetlist.test.ts @@ -3,6 +3,15 @@ import assetList from '../__fixtures__/assetlist.schema.json'; import { createJSONSchemaPatchOperations, findAllProps } from '../src/utils'; import JSONSchemaPatch from '../src'; +// FROM schema-typescript +// // Determine if the key is a valid JavaScript identifier + +// Determine if the key is a valid JavaScript-like identifier, allowing internal hyphens +function isValidIdentifierCamelized(key: string) { + return /^[$A-Z_][0-9A-Z_$\-]*$/i.test(key) && !/^[0-9]+$/.test(key) && !/^-/.test(key); +} + +// FROM strfy-js function camelCaseTransform(key: string): string { return key.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : ''); } @@ -13,13 +22,13 @@ it('findAllProps', () => { it('createJSONSchemaPatchOperations', () => { - expect(createJSONSchemaPatchOperations(findAllProps(assetList), camelCaseTransform)).toMatchSnapshot(); + expect(createJSONSchemaPatchOperations(findAllProps(assetList), camelCaseTransform, isValidIdentifierCamelized)).toMatchSnapshot(); }) it('JSONSchemaPatch transformer', () => { const patcher = new JSONSchemaPatch(assetList); - patcher.transform(camelCaseTransform); + patcher.transform(camelCaseTransform, isValidIdentifierCamelized); patcher.applyPatch(); expect(patcher.schema).toMatchSnapshot(); }) diff --git a/__tests__/utils.chain.remove.test.ts b/__tests__/utils.chain.remove.test.ts index 6849956..ace1c07 100644 --- a/__tests__/utils.chain.remove.test.ts +++ b/__tests__/utils.chain.remove.test.ts @@ -2,11 +2,19 @@ import chain from '../__fixtures__/chain.schema.json'; import JSONSchemaPatch from '../src'; +// FROM schema-typescript +// // Determine if the key is a valid JavaScript identifier + +// Determine if the key is a valid JavaScript-like identifier, allowing internal hyphens +function isValidIdentifierCamelized(key: string) { + return /^[$A-Z_][0-9A-Z_$\-]*$/i.test(key) && !/^[0-9]+$/.test(key) && !/^-/.test(key); +} + +// FROM strfy-js function camelCaseTransform(key: string): string { return key.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : ''); } - it('JSONSchemaPatch transformer w excludes', () => { const patcher = new JSONSchemaPatch(chain); patcher.prepareOperation({ @@ -14,7 +22,7 @@ it('JSONSchemaPatch transformer w excludes', () => { path: '/properties/images/items/properties/image_sync' }); // patcher.applyPatch(); - patcher.transform(camelCaseTransform); + patcher.transform(camelCaseTransform, isValidIdentifierCamelized); patcher.applyPatch(); expect(patcher.schema).toMatchSnapshot(); }) diff --git a/__tests__/utils.chain.test.ts b/__tests__/utils.chain.test.ts index 01ef62d..02c7764 100644 --- a/__tests__/utils.chain.test.ts +++ b/__tests__/utils.chain.test.ts @@ -3,22 +3,32 @@ import chain from '../__fixtures__/chain.schema.json'; import { createJSONSchemaPatchOperations, findAllProps } from '../src/utils'; import JSONSchemaPatch from '../src'; +// FROM schema-typescript +// // Determine if the key is a valid JavaScript identifier + +// Determine if the key is a valid JavaScript-like identifier, allowing internal hyphens +function isValidIdentifierCamelized(key: string) { + return /^[$A-Z_][0-9A-Z_$\-]*$/i.test(key) && !/^[0-9]+$/.test(key) && !/^-/.test(key); +} + +// FROM strfy-js function camelCaseTransform(key: string): string { return key.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : ''); } + it('findAllProps', () => { expect(findAllProps(chain)).toMatchSnapshot(); }) it('createJSONSchemaPatchOperations', () => { - expect(createJSONSchemaPatchOperations(findAllProps(chain), camelCaseTransform)).toMatchSnapshot(); + expect(createJSONSchemaPatchOperations(findAllProps(chain), camelCaseTransform, isValidIdentifierCamelized)).toMatchSnapshot(); }) it('JSONSchemaPatch transformer', () => { const patcher = new JSONSchemaPatch(chain); - patcher.transform(camelCaseTransform); + patcher.transform(camelCaseTransform, isValidIdentifierCamelized); patcher.applyPatch(); expect(patcher.schema).toMatchSnapshot(); }) diff --git a/src/index.ts b/src/index.ts index 9a5acd7..55f1f6c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ import { applyPatch, Operation } from 'fast-json-patch'; -import { createJSONSchemaPatchOperations, dirname, findAllProps, TransformFunction } from './utils'; +import { BooleanFunction, createJSONSchemaPatchOperations, dirname, findAllProps, TransformFunction } from './utils'; export interface JSONSchemaPatchOperation { op: 'add' | 'remove' | 'replace' | 'rename' | 'addProperty' | 'removeProperty' | 'renameProperty' | 'addDefinition' | 'removeDefinition'; @@ -62,10 +62,10 @@ export class JSONSchemaPatch { this.ops.push(op); } - transform(transformFunction: TransformFunction): void { + transform(transformFunction: TransformFunction, transformTest: BooleanFunction): void { this.ops = [ ...this.ops, - ...createJSONSchemaPatchOperations(findAllProps(this.schema), transformFunction) + ...createJSONSchemaPatchOperations(findAllProps(this.schema), transformFunction, transformTest) ]; } diff --git a/src/utils.ts b/src/utils.ts index c724773..0fe56e4 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -15,6 +15,7 @@ export interface PropertyPath { } export type TransformFunction = (str: string) => string; +export type BooleanFunction = (str: string) => boolean; function sortByDeepestPath(propertyPaths: PropertyPath[]): PropertyPath[] { return propertyPaths.sort((a, b) => { @@ -90,15 +91,19 @@ export function findAllProps(schema: JSONSchema, currentPath: string = ''): Prop return sortByDeepestPath(findProps(schema, currentPath)); } -export function createJSONSchemaPatchOperations(propertyPaths: PropertyPath[], transform: TransformFunction): JSONSchemaPatchOperation[] { +export function createJSONSchemaPatchOperations( + propertyPaths: PropertyPath[], + transform: TransformFunction, + transformTest: BooleanFunction +): JSONSchemaPatchOperation[] { const ops = propertyPaths.map(propertyPath => { switch (propertyPath.type) { case 'property': - return createRenamePropertyOperation(propertyPath, transform); + return createRenamePropertyOperation(propertyPath, transform, transformTest); case '$def': - return createReplaceDefOperation(propertyPath, transform); + return createReplaceDefOperation(propertyPath, transform, transformTest); case '$ref': - return createReplaceRefOperation(propertyPath, transform); + return createReplaceRefOperation(propertyPath, transform, transformTest); default: throw new Error(`Unknown property type: ${propertyPath.type}`); } @@ -113,12 +118,16 @@ export function createJSONSchemaPatchOperations(propertyPaths: PropertyPath[], t } -function createRenamePropertyOperation(propertyPath: PropertyPath, transform: TransformFunction): JSONSchemaPatchOperation { +function createRenamePropertyOperation( + propertyPath: PropertyPath, + transform: TransformFunction, + transformTest: BooleanFunction +): JSONSchemaPatchOperation { const grandParent = dirname(dirname(propertyPath.path)); const oldName = basename(propertyPath.path); const newName = transform(oldName); - if (!isSimpleKey(oldName) || oldName === newName) return; + if (!transformTest(oldName) || oldName === newName) return; return { op: 'renameProperty', @@ -127,44 +136,54 @@ function createRenamePropertyOperation(propertyPath: PropertyPath, transform: Tr }; } -function createReplaceRefOperation(propertyPath: PropertyPath, transform: TransformFunction): JSONSchemaPatchOperation { - const newRef = transformJsonPath(propertyPath.name, transform); +function createReplaceRefOperation( + propertyPath: PropertyPath, + transform: TransformFunction, + transformTest: BooleanFunction +): JSONSchemaPatchOperation { + const newRef = transformJsonPath(propertyPath.name, transform, transformTest); if (newRef === propertyPath.name) return; return { op: 'replace', - path: transformJsonPath(propertyPath.path + '/$ref', transform), + path: transformJsonPath(propertyPath.path + '/$ref', transform, transformTest), value: newRef }; } -function createReplaceDefOperation(propertyPath: PropertyPath, transform: TransformFunction): JSONSchemaPatchOperation { +function createReplaceDefOperation( + propertyPath: PropertyPath, + transform: TransformFunction, + transformTest: BooleanFunction +): JSONSchemaPatchOperation { const oldName = basename(propertyPath.path); const newName = transform(oldName); - if (!isSimpleKey(oldName) || oldName === newName) return; + if (!transformTest(oldName) || oldName === newName) return; return { op: 'rename', path: propertyPath.path, - value: transformJsonPath(propertyPath.name, transform) + value: transformJsonPath(propertyPath.name, transform, transformTest) }; } -function transformJsonPath(path: string, transformFunc: (str: string) => string): string { +function transformJsonPath( + path: string, + transform: TransformFunction, + transformTest: BooleanFunction +): string { const specialKeywords = ['#', '$defs', '$ref', 'definitions', 'properties']; return path.split('/').map(segment => { const normalizedSegment = segment.replace(/^#/, ''); - if (specialKeywords.includes(normalizedSegment) || !isSimpleKey(segment)) { + if (specialKeywords.includes(normalizedSegment) || !transformTest(segment)) { return segment; } - return transformFunc(segment); + return transform(segment); }).join('/'); } // MARKED AS NOT DRY -// from strfy-js -export function isSimpleKey(key: string): boolean { - return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key); -} + +// from strfy-js export function dirname(path: string): string { return path.replace(/\/[^\/]*$/, ''); // Removes last segment after the last '/' } diff --git a/types/utils.d.ts b/types/utils.d.ts index c4b7b1c..ef1b38c 100644 --- a/types/utils.d.ts +++ b/types/utils.d.ts @@ -1,4 +1,4 @@ -import { JSONSchemaPatchOperation } from "src"; +import { JSONSchemaPatchOperation } from "./"; interface JSONSchema { properties?: { [key: string]: JSONSchema;