Core Contract Model Validators

Trevor.Conn@...
 

Hi all -- I wanted to follow up on this discussion we've been having in the Core Working Group about validating our contract models and where that responsibility lies. This past week we talked about two options.


1.) Using a JSON Schema-based validation

2.) Creating a validator implementation that can be called as part of the UnmarshalJSON operation found in some of our models.


I explored the JSON Schema solution a bit on Thursday afternoon with help from Intel, but I did not find it palatable. The schema has to be created and maintained then loaded at runtime from either URL, file or inline string. Child types have their schema, but that schema must be either be repeated in the schema of the parent type or referenced via a pointer. This all seemed like too much work so before getting seriously into it, I decided to work on the validator idea.


I created a couple feature branches which you can review/pull at the links below. This involves changes to both core-contracts and edgex-go.


https://github.com/tsconn23/go-mod-core-contracts/tree/validators

https://github.com/tsconn23/edgex-go/tree/validators


Rather than ProvisionWatcher I have used DeviceService in my example above because it already had an UnmarshalJSON() method in place, and also with the ProvisionWatcher I'd have to account for DeviceProfile as well. I just wanted to get something up and going to try and prove the concept, so the scope was more reduced by using DeviceService.


Here's a sample request I'm using to test:

POST http://localhost:48081/api/v1/deviceservice

{"_class":"org.edgexfoundry.domain.meta.DeviceService","adminState":"unlocked","operatingState":"disabled1","name":"ds_test1","description":"manage home variable speed motors","lastConnected":0,"lastReported":0,"labels":["hvac","variable speed motor"],"origin":1471806386919}


In the request body above there are two issues. First, the operatingState value is invalid. Second, the Addressable is missing. If you execute the above locally, you'll receive a 400 (Bad Request) with the error message "invalid OperatingState 'disabled1'".

If you fix the OperatingState value, then you'll still receive a 400 with a new error message "Addressable ID and Name are both blank". This is coming from the validator inside of the Addressable model within core-contracts. I noticed that we'll have the opportunity to slim our controller logic by moving the server-side validation we do today into the models. We'll also have consistent state validation (example: validation today is different depending on if you're adding a new Device Service versus updating an existing one).


The order of operations with regard to unmarshaling and validation looks like this

  • DeviceService
    • UnmarshalJSON
      • OperatingState (if provided in the request)
        • UnmarshalJSON
        • validate()
      • AdminState (if provided in the request)
        • UnmarshalJSON
        • validate()
    • validate()
      • OperatingState
        • validate()
      • AdminState
        • validate()
Herein is the only thing that gives me minor heartburn about the solution. The validate() method of both OperatingState and AdminState are called as part of their respective UnmarshalJSON implementations. It doesn't have to be but I thought it made sense to do so since it allows us to fail faster if we detect a problem.

Assuming those pass, then DeviceService calls its own validate() method which takes care of validating all child types. This is necessary in case a child type's key was omitted in the JSON. This means validation for any of the child types can occur twice -- a cheap operation to be sure, but redundant at times.

As an aside, you will see changes in the feature branches related to the elimination of models.Service from core-contracts. Nothing else was composed from this model other than the DeviceService and I felt that eliminating it would simplify the solution.

Comments are welcome. If this looks like a viable approach to folks, then there might be some bandwidth soon to undertake adding UnmarshalJSON to the models that do not have them, and implementing the necessary validate() handlers.


Trevor Conn
Technical Staff Engineer
Dell Technologies | IoT DellTech
Trevor_Conn@...
Round Rock, TX  USA

Join EdgeX-GoLang@lists.edgexfoundry.org to automatically receive all group messages.