Announcement

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

    Using Velocity expressions and sparseUpdates="true"

    We use sparseUpdates="true" to avoid updating fields when the user hasn't made any changes, but this makes coding Velocity expressions in validators a real pain when they need to refer to multiple field values. There isn't a consistent way to refer to fields in the record being updated. If the user changed the field, the value you want to use is in $record.fieldName. But if they didn't change the field you need to use $dsRequest.oldValues.fieldName.

    Is there some convenient way to refer to the merged set of values, so that if a field is in $record it comes from there, but if not it comes from oldValues?

    #2
    We don't know of a Velocity syntax that makes this any less verbose than "#if ...", but what you could do is use dsRequest.addToTemplateContext() to make an additional variable available (called, say, $mergedValues) that is a Map that combines all new values and oldValues.

    Comment


      #3
      Just a note on this - oldValues come from the client, and should generally not be trusted in a validator. Fetching fresh "oldValues" from the database is the right way to do any kind of change detection or other comparison that is intended to enforce a security rule.

      Comment


        #4
        Thanks for the feedback. I've implemented this but am running into a problem due to server side validation processing that I don't fully understand. The datasource I'm using has both an isUnique validator on one field and my serverCustom validator that uses the new mergedValues template context variable.

        Running in debug I can see that the mergedValues variable is correctly constructed when the update request comes in from the client. But the isUnique validator generates a separate fetch request and that request fails on my serverCustom validator since that new request does not have the mergedValues template in it's context.

        Any tips on how I should handle that? I don't really understand why the other validators are implemented during that isUnique generated fetch.

        Comment


          #5
          Sorry, this has nothing to do with isUnique. I've removed that and still have the problem. In my serverConstructor class
          Code:
          @Override
          public DSResponse validateDSRequest(DSRequest req) throws Exception {
          	req.addToTemplateContext("ipMergedValues", getMergedValues(req));
          	return super.validateDSRequest(req);
          };
          I can see with debug that the request template context has the values I expect, just prior to the call to super.validateDSRequest. Calling req.getTemplateContext().get("ipMergedValues") returns
          Code:
          {_selection_529=true, cardRangeName=BBBB, endingNumberRange=998, startingNumberRange=13}
          But then super.validateDSRequest() generates this.
          Code:
          === 2013-04-05 11:13:37,520 [l0-7] ERROR Velocity - Left side ($ipMergedValues.endingNumberRange) of '>=' operation has null value. Operation not possible. CustomValidator[line 1, column 41]
          === 2013-04-05 11:13:37,522 [l0-7] INFO  Validation - [builtinApplication.IPPECRD_update] Validation error: [
              {
                  endingNumberRange:{
                      errorMessage:"Range To must be greater than or equal to Range From. $mergedValues"
                  }
              }
          ]
          The serverCondition it refers to is.
          Code:
          <validator type="serverCustom">  
              <serverCondition><![CDATA[$ipMergedValues.endingNumberRange >= $ipMergedValues.startingNumberRange]]></serverCondition>  
              <errorMessage>Range To must be greater than or equal to Range From. $mergedValues</errorMessage>  
          </validator>
          Is validateDSRequest the correct method to override? It isn't documented, but the validate method, which is documented, does not provide access to the DSRequest. Just in case, I tried overriding execute() and putting the code there, but with the same results.

          Comment


            #6
            The right API is validator.addErrorMessageVariable() - see this sample.

            addToTemplateContext() affects SQL templating.

            This also means you don't need the DSRequest, so no temptation to override an internal method anymore.

            Comment


              #7
              I was hoping to avoid having to code an entire validator class just to validate that one field is >= another. Is there some other way to do that simple validation that will work when only one of the two fields is being updated? Also, I only put the variable in the message to see what it contained, just for debugging. I wanted to variable to be used by the serverCondition velocity.
              Last edited by jay.l.fisher; 5 Apr 2013, 15:32.

              Comment


                #8
                Velocity can be used to fetch the saved record - the $dataSources variable makes this pretty easy (see this sample).

                There's also the possibility of using Server Scripting so you can write a bit of inline Java, JavaScript or Groovy without the need to write a whole separate .java file.

                Comment


                  #9
                  An example would be a great help. I sort of assumed there would be a one-line solution, along the lines of serverCondition="$record.field1 >= $record.field2", or at least something almost as simple. It seems like such a common type of validation.

                  Comment


                    #10
                    That sample just linked has an example of using $dataSources to fetch a record.

                    Comment


                      #11
                      I don't need to fetch a record, I just need to add something to the velocity context that can be referenced in the serverCondition. Is there no way to do that?

                      Actually, all I really need to do is validate, in some declaritive way, that field1 must be >= field2. I'm just looking for the simplest way to do that.
                      Last edited by jay.l.fisher; 5 Apr 2013, 15:58.

                      Comment


                        #12
                        So, in order to correctly validate that, you should retrieve the stored record, apply the new values on top, then do the check. This isn't a one liner - it will take 2-3 lines. If using Velocity, $dataSources names it easy to do a fetch.

                        You can't currently add entirely new variables to the Velocity context used for serverCondition, however, you can make additional data available by placing it on the httpRequest or dsRequest and using standard Velocity syntax for accessing Java methods.

                        Comment

                        Working...
                        X