Skip to content

Commit

Permalink
Encoding of special characters in prop name
Browse files Browse the repository at this point in the history
- Encode '/' in ui schema generator
- Decode '/' and '~' in:
  - Resolvers
  - Label
  - MaterialTableControl
  - Vanilla TableArrayControl
  - Angular table.renderer
- Add test cases for uischema, resolver and path

Signed-off-by: Max Elia Schweigkofler <max_elia@hotmail.de>
  • Loading branch information
max-elia committed Jan 18, 2022
1 parent 8c3b626 commit be63a89
Show file tree
Hide file tree
Showing 12 changed files with 59 additions and 12 deletions.
4 changes: 3 additions & 1 deletion packages/angular-material/src/other/master-detail/master.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ export const removeSchemaKeywords = (path: string) => {
return path
.split('/')
.filter(s => !some(keywords, key => key === s))
.join('.');
.join('.')
.split('~1')
.join('/')
};

@Component({
Expand Down
3 changes: 2 additions & 1 deletion packages/angular-material/src/other/table.renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ export class TableRenderer extends JsonFormsArrayControl {
): ColumnDescription[] => {
if (schema.type === 'object') {
return this.getValidColumnProps(schema).map(prop => {
const uischema = controlWithoutLabel(`#/properties/${prop}`);
const encProp = prop.split('/').join('~1');
const uischema = controlWithoutLabel(`#/properties/${encProp}`);
if (!this.isEnabled()) {
setReadonly(uischema);
}
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/generators/uischema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ const generateUISchema = (
const nextRef: string = currentRef + '/properties';
Object.keys(jsonSchema.properties).map(propName => {
let value = jsonSchema.properties[propName];
propName = propName.split('/').join('~1');
const ref = `${nextRef}/${propName}`;
if (value.$ref !== undefined) {
value = resolveSchema(rootSchema, value.$ref);
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/util/label.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ const deriveLabel = (
}
if (typeof controlElement.scope === 'string') {
const ref = controlElement.scope;
const label = ref.substr(ref.lastIndexOf('/') + 1);
let label = ref.substr(ref.lastIndexOf('/') + 1);
label = label.split('~1').join('/').split('~0').join('~');

return startCase(label);
}
Expand Down
8 changes: 6 additions & 2 deletions packages/core/src/util/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,13 @@ export const toDataPathSegments = (schemaPath: string): string[] => {
.replace(/oneOf\/[\d]\//g, '');
const segments = s.split('/');

const startFromRoot = segments[0] === '#' || segments[0] === '';
const decodedSegments = segments.map(segment => {
return segment.split('~1').join('/').split('~0').join('~');
});

const startFromRoot = decodedSegments[0] === '#' || decodedSegments[0] === '';
const startIndex = startFromRoot ? 2 : 1;
return range(startIndex, segments.length, 2).map(idx => segments[idx]);
return range(startIndex, decodedSegments.length, 2).map(idx => decodedSegments[idx]);
};

/**
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/util/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ const isRequired = (
): boolean => {
const pathSegments = schemaPath.split('/');
const lastSegment = pathSegments[pathSegments.length - 1];
// Skip "properties", "items" etc. to resolve the parent
const nextHigherSchemaSegments = pathSegments.slice(
0,
pathSegments.length - 2
Expand Down
6 changes: 4 additions & 2 deletions packages/core/src/util/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,9 @@ export const resolveSchema = (
if (isEmpty(schema)) {
return undefined;
}
const validPathSegments = schemaPath.split('/');
const validPathSegments = schemaPath.split('/').map(segment => {
return segment.split('~1').join('/').split('~0').join('~');
});
let resultSchema = schema;
for (let i = 0; i < validPathSegments.length; i++) {
let pathSegment = validPathSegments[i];
Expand All @@ -136,7 +138,7 @@ export const resolveSchema = (
resultSchema?.anyOf ?? []
);
for (let item of schemas) {
curSchema = resolveSchema(item, validPathSegments.slice(i).join('/'));
curSchema = resolveSchema(item, validPathSegments.slice(i).map(s => s.split('/').join('~1')).join('/'));
if (curSchema) {
break;
}
Expand Down
18 changes: 18 additions & 0 deletions packages/core/test/generators/uischema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -556,3 +556,21 @@ test('generate control for nested oneOf', t => {
};
t.deepEqual(generateDefaultUISchema(schema), uischema);
});

test('encode "/" in generated ui schema', t => {
const schema: JsonSchema = {
properties: {
'some / initial / value': {
type : 'integer'
}
}
};
const uischema: Layout = {
type: 'VerticalLayout',
elements: [{
type: 'Control',
scope: '#/properties/some ~1 initial ~1 value'
}] as ControlElement[]
};
t.deepEqual(generateDefaultUISchema(schema), uischema);
});
3 changes: 3 additions & 0 deletions packages/core/test/util/path.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ test('toDataPath use of encoded paths relative without /', t => {
const fooBar = encodeURIComponent('foo/bar');
t.is(toDataPath(`properties/${fooBar}`), `${fooBar}`);
});
test('toDataPath use of encoded special character in pathname', t => {
t.is(toDataPath('properties/foo~0bar~1baz'), 'foo~bar/baz');
});
test('resolve instance', t => {
const instance = { foo: 123 };
const result = Resolve.data(instance, toDataPath('#/properties/foo'));
Expand Down
15 changes: 14 additions & 1 deletion packages/core/test/util/resolvers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,17 @@ test('resolveSchema - resolves schema with any ', t => {
t.deepEqual(resolveSchema(schema, '#/properties/description/properties/index'), {type: 'number'});
t.deepEqual(resolveSchema(schema, '#/properties/description/properties/exist'), {type: 'boolean'});
t.is(resolveSchema(schema, '#/properties/description/properties/notfound'), undefined);
});
});

test('resolveSchema - resolves schema with encoded characters', t => {
const schema = {
type: 'object',
properties: {
'foo / ~ bar': {
type: 'integer'
}
}
};
t.deepEqual(resolveSchema(schema, '#/properties/foo ~1 ~0 bar'), {type: 'integer'});
t.is(resolveSchema(schema, '#/properties/foo / bar'), undefined);
});
2 changes: 1 addition & 1 deletion packages/material/src/complex/MaterialTableControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ interface NonEmptyCellComponentProps {
isValid: boolean
}
const NonEmptyCellComponent = React.memo(({path, propName, schema,rootSchema, errors, enabled, renderers, cells, isValid}:NonEmptyCellComponentProps) => {

propName = propName?.split('/').join('~1');
return (
<NoBorderTableCell>
{schema.properties ? (
Expand Down
7 changes: 4 additions & 3 deletions packages/vanilla/src/complex/TableArrayControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,13 @@ class TableArrayControl extends React.Component<ArrayControlProps & VanillaRende
childPath,
prop.toString()
);

let encProp = prop;
encProp = prop.split('/').join('~1');
return (
<td key={childPropPath}>
<DispatchCell
schema={Resolve.schema(schema, `#/properties/${prop}`, rootSchema)}
uischema={createControlElement(prop)}
schema={Resolve.schema(schema, `#/properties/${encProp}`, rootSchema)}
uischema={createControlElement(encProp)}
path={childPath + '.' + prop}
/>
</td>
Expand Down

0 comments on commit be63a89

Please sign in to comment.