Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transform (trim) on nested objects via definitions is not working #152

Open
Vitaljok opened this issue Jan 22, 2021 · 3 comments
Open

Transform (trim) on nested objects via definitions is not working #152

Vitaljok opened this issue Jan 22, 2021 · 3 comments

Comments

@Vitaljok
Copy link

Vitaljok commented Jan 22, 2021

Hello!

I have found interesting behavior of transform keyword, which is probably a bug.
In short: "transform": [ "trim" ] is not working on nested objects if specified via definitions.

The schema:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {
    "trimmedString": {
      "type": "string",
      "transform": ["trim"]
    }
  },
  "type": "object",
  "properties": {
    "a": {
      "$ref": "#/definitions/trimmedString"
    },
    "nested": {
      "type": "object",
      "properties": {
        "b": {
          "$ref": "#/definitions/trimmedString"
        },
        "c": {
          "type": "string",
          "transform": ["trim"]   
        }
      }
    }
  }  
}

Input data:

{
  "a": "   x   ",
  "nested": {
    "b": "   y   ",
    "c": "   z   "
  }
}

Expected results: all strings are trimmed.

Actual results: string b in nested objects is not trimmed

{
  "a": "x",
  "nested": {
    "b": "   y   ",
    "c": "z"
  }
}

My versions:

"ajv": "^7.0.3",
"ajv-formats": "^1.5.1",
"ajv-keywords": "^4.0.0",

Thanks!

@randomhash
Copy link

you need to activate ajv-keywords with transform as a keyword

@Vitaljok
Copy link
Author

you need to activate ajv-keywords with transform as a keyword

Obviously, I did activated "transform" keyword. Without it I get:
Error: strict mode: unknown keyword: "transform"

Here is full example (./schemas/test.json is exactly as I posted above):

import Ajv from "ajv";
import addKeywords from 'ajv-keywords';
import testSchema from './schemas/test.json';

describe('bug', () => {
  it('all strings should be trimmed', () => {
    const ajv = new Ajv({ allErrors: true });
    addKeywords(ajv, ['transform']);
    const validate = ajv.compile(testSchema);

    const data = {
      a: '   x   ',
      nested: {
        b: '   y   ',
        c: '   z   '
      }
    };

    console.log('Data before validation:', data);
    const res = validate(data);    
    console.log('Data after validation:', data);

    expect(res).toBeTrue();
    expect(data.a).toEqual('x');
    expect(data.nested.b).toEqual('y');
    expect(data.nested.c).toEqual('z');
  });
});

And the output of the spec is as follows:

Data before validation: { a: '   x   ', nested: { b: '   y   ', c: '   z   ' } }
Data after validation: { a: 'x', nested: { b: '   y   ', c: 'z' } }

Failures:
1) bug all strings should be trimmed
  Message:
    Expected '   y   ' to equal 'y'.

As you can see, trim if specified via custom definitions works for the non-nested field a, but does not not work for nested field b.

Field c is nested, but it is specified without definition.

@anlerandy
Copy link

Hi!

I do have the same issue @Vitaljok is encoutering.
I use

    "ajv": "^8.6.0",
    "ajv-keywords": "^5.0.0",
    "ajv-merge-patch": "^5.0.1"

Origin

I have a lot of big schema with nested element or $ref.
Some of my schema repeat themselves: create schema has multiple required but not its update sibling. I use different method to simplify my code but all of them disable trim

  • Using $merge to remove my required options
  • Using lodash.omit, json-merge-patch or handmade deepMerge

Notice

It appears that all schema coming from a function disable trim. If I console.log the result of my deepMerge and paste it in the code, the trim seem to work.

Test:

const Ajv = require('ajv');

const ajv = new Ajv({ coerceTypes: 'array', allowUnionTypes: true });
require('ajv-merge-patch')(ajv);
require("ajv-keywords")(ajv);

const create = {
  type: 'object',
  properties: {
    body: {
      type: 'object',
      required: ['name'],
      properties: {
        name: {
          type: 'string',
          transform: ['trim']
        }
      }
    }
  }
};

const update = {
  $merge: {
    source: create,
    with: {
      properties: {
        body: {
          required: []
        }
      }
    }
  }
};

const createValidate = ajv.compile(create);
const updateValidate = ajv.compile(update);

const data = {
  body: {
    name: '    Test string to Trim        '
  }
};

function _(validate, data) {
  try {
    validate(data);
    console.log({ data });
  } catch (e) {
    console.log({ e });
  }
}

_(updateValidate, data);
_(createValidate, data);

Output:

updateCase = { data: { body: { name: '    Test string to Trim        ' } } }

createCase = { data: { body: { name: 'Test string to Trim' } } }

I can send the other methods I used, but ultimately, I am forced to paste my entire schema just to remove required.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

3 participants