Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    Validation is run on an invalid value when a dependent field uses multipleStorage

    Hello,

    I have an issue with dependentFields declared on a validator. When a dependent field is absent from request it's pulled in from the database and validated, which causes two problems:
    1. A potential performance issue, especially if that validator needs to execute some additional database queries, or perform external I/O - why validate it at all when the value comes straight from the database?
    2. When this dependent field has multipleStorage defined the validation is run on a value that looks like a result of calling ArrayList.toString

    Test case, verified on SmartClient v13.1p_2025-05-12/PowerEdition:

    Database schema:
    Code:
    CREATE TABLE test_vhosts (
      id SERIAL PRIMARY KEY,
      domain TEXT NOT NULL,
      domain_aliases jsonb DEFAULT '[]'::jsonb,
      comment TEXT NOT NULL DEFAULT ''
    );
    Data source:
    Code:
    <DataSource ID="testVhostsDS" serverType="sql" tableName="test_vhosts">
        <fields>
            <field name="id" type="sequence" hidden="true" primaryKey="true"/>
            <field name="domain" type="text" required="true">
                <validators>
                    <validator type="serverCustom" dependentFields="domain_aliases">
                        <serverObject className="pl.test.TestVhostsValidators" methodName="validateDomain"/>
                    </validator>
                </validators>
            </field>
            <field name="domain_aliases" type="text" multiple="true" multipleStorage="json">
                <validators>
                    <validator type="serverCustom" dependentFields="domain">
                        <serverObject className="pl.test.TestVhostsValidators" methodName="validateDomainAlias"/>
                    </validator>
                </validators>
            </field>
            <field name="comment" type="text" defaultValue=""/>
        </fields>
    </DataSource>
    Validators with logging:
    Code:
    package pl.test;
    
    import com.isomorphic.datasource.DataSource;
    import com.isomorphic.datasource.Validator;
    import com.isomorphic.log.Logger;
    
    import java.util.Map;
    
    public class TestVhostsValidators {
        private final Logger log = new Logger(this);
    
        public boolean validateDomain(Object value, Validator validator, String fieldName, Map<?, ?> record, DataSource ds) {
            log.info("Validating domain: " + toLogLine(value, record));
            return true; }
    
        public boolean validateDomainAlias(Object value, Validator validator, String fieldName, Map<?, ?> record, DataSource ds) {
            log.info("Validating domain alias: " + toLogLine(value, record));
            return true; }
    
        private String toLogLine(Object value, Map<?, ?> record) {
            return "value=%s (type=%s) record=%s (domain_aliases type=%s)"
                    .formatted(String.valueOf(value), className(value), record.toString(), className(record.get("domain_aliases")));
        }
    
        private String className(Object value) {
            return value == null ? "null" : value.getClass().getSimpleName();
        }
    }
    When I create a new record I see what I expect, three validations on singular values:
    Code:
    testVhostsDS.addData({domain: "test1", domain_aliases: ["alias1", "alias2"]}, (dsResponse, data) => console.log(data))
    
    // Validating domain alias: value=alias1 (type=String) record={domain_aliases=[alias1, alias2], domain=test1} (domain_aliases type=ArrayList)
    // Validating domain alias: value=alias2 (type=String) record={domain_aliases=[alias1, alias2], domain=test1} (domain_aliases type=ArrayList)
    // Validating domain: value=test1 (type=String) record={domain_aliases=[alias1, alias2], domain=test1} (domain_aliases type=ArrayList)
    but when I update only the domain field:
    Code:
    testVhostsDS.updateData({id: 1, domain: "test2"}, (dsResponse, data) => console.log(data))
    
    // Validating domain alias: value=[alias1, alias2] (type=String) record={domain_aliases=[alias1, alias2], domain=test2, id=2} (domain_aliases type=ArrayList)
    // Validating domain: value=test2 (type=String) record={domain_aliases=[alias1, alias2], domain=test2, id=2} (domain_aliases type=ArrayList)
    validateDomainAlias was called with a String value of "[alias1, alias2]", which is neither a valid single item value nor a valid representation conforming to configured multipleStorage.

    #2
    What we call "validation" in the framework is actually a bit broader and more basic than that, in that, when we retrieve values from various external data sources - SQL, XML, JSON, etc - some processing is required to change the data into the type that is expected for further processing.

    What is your concern with your validator being called here, performance? In the case of your particular validator, it doesn't look like you'd have to do a DB query in this case, so you could skip it? Or did we miss something?

    Comment


      #3
      For me the issue is that the second validator is run on a value that does not exist: "[alias1, alias2]"

      Comment


        #4
        A developer is assigned to look at this, we'll report back here in the next few days

        Comment


          #5
          We have now committed a fix for this issue. Your validator will no longer be called in this circumstance (ie, when the data value is not part of the update and is only present because of dependent fields), and we have also fixed the issue of a list value being serialized to a string in this specific circumstance - the built-in type validators like "isString" now preserve list values for multiple:true fields, whilst validating (and converting if necessary) the individual list members.

          The fixes will be in builds of 13.1 and greater as of tomorrow, Saturday 24th May. Please give it a try and let us know.

          Comment

          Working...
          X