{"action":"created","issue":{"url":"https://api.github.com/repos/square/lgtm/issues/15","labels_url":"https://api.github.com/repos/square/lgtm/issues/15/labels{/name}","comments_url":"https://api.github.com/repos/square/lgtm/issues/15/comments","events_url":"https://api.github.com/repos/square/lgtm/issues/15/events","html_url":"https://github.com/square/lgtm/issues/15","id":94497176,"number":15,"title":"Composing chained validations from multiple validators","user":{"login":"sarus","id":306319,"avatar_url":"https://avatars.githubusercontent.com/u/306319?v=3","gravatar_id":"","url":"https://api.github.com/users/sarus","html_url":"https://github.com/sarus","followers_url":"https://api.github.com/users/sarus/followers","following_url":"https://api.github.com/users/sarus/following{/other_user}","gists_url":"https://api.github.com/users/sarus/gists{/gist_id}","starred_url":"https://api.github.com/users/sarus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sarus/subscriptions","organizations_url":"https://api.github.com/users/sarus/orgs","repos_url":"https://api.github.com/users/sarus/repos","events_url":"https://api.github.com/users/sarus/events{/privacy}","received_events_url":"https://api.github.com/users/sarus/received_events","type":"User","site_admin":false},"labels":[],"state":"closed","locked":false,"assignee":null,"milestone":null,"comments":2,"created_at":"2015-07-11T18:25:32Z","updated_at":"2015-07-21T16:20:03Z","closed_at":"2015-07-14T01:33:43Z","body":"I'm using LGTM to validate REST API inputs and found myself repeating the same validations over and over.  For example, all my GET endpoints need to validate LIMIT and OFFSET so I have something like this:\r\n\r\n```javascript\r\nvar getUsersValidator = LGTM.validator()\r\n    \r\n    // Validate limit\r\n    .validates('limit')\r\n    .optional()\r\n    .integer(validationJsonError.serialize({\r\n        detail:'The \"limit\" value must be an integer',\r\n        errorAttribute: 'limit'\r\n    }))\r\n    .minValue(0, validationJsonError.serialize({\r\n            detail:'The minimum value for the \"limit\" is 0',\r\n            errorAttribute: 'limit'\r\n        }))\r\n\r\n    // Validate 'offset'\r\n    .validates('offset')\r\n    .optional()\r\n    .integer(validationJsonError.serialize({\r\n                detail:'The \"offset\" value must be an integer',\r\n                errorAttribute: 'offset'\r\n            }))\r\n    .minValue(0, validationJsonError.serialize({\r\n                detail: 'The minimum value for the offset is 0',\r\n                errorAttribute: 'offset'\r\n            }))\r\n\r\n// Additional validations\r\n.build();\r\n\r\nvar getPostsValidator =  LGTM.validator()\r\n// Repeat above for limit and offset\r\n```\r\n\r\nNote that instead of returning string messages I return a JSON API error object (http://jsonapi.org/format/#errors).  I know in all the examples you return a string but returning the object and then formatting all the JSON API errors into a single JSON API compliant response works really well (might be worth mentioning in the docs that you can return a custom object).\r\n\r\nWhat I'd like to be able to do is compose together different validators to run together as a single compound validation or be able to easily construct a new compound validator from primitive validators (a validator that validates one attribute or property).  I'm having a hard time figuring out a clean way to do this.\r\n\r\nI basically want to encapsulate the validation logic for `limit` and the validation logic for `offset` and then use those as primitives to create a new more complex validator (there are other things like sort and order as well).  This is made up but something like this:\r\n\r\n```\r\nvar limit = LGTM.validator()    \r\n    .validates('limit')\r\n    .optional()\r\n    .integer(validationJsonError.serialize({\r\n        detail:'The \"limit\" value must be an integer',\r\n        errorAttribute: 'limit'\r\n    }))\r\n    .minValue(0, validationJsonError.serialize({\r\n            detail:'The minimum value for the \"limit\" is 0',\r\n            errorAttribute: 'limit'\r\n        }));\r\n\r\nvar offset = LGTM.validator()\r\n    .validates('offset')\r\n    .optional()\r\n    .integer(validationJsonError.serialize({\r\n                detail:'The \"offset\" value must be an integer',\r\n                errorAttribute: 'offset'\r\n            }))\r\n    .minValue(0, validationJsonError.serialize({\r\n                detail: 'The minimum value for the offset is 0',\r\n                errorAttribute: 'offset'\r\n            }));\r\n\r\n\r\nvar user = LGTM.validator()\r\n    .maxLength(25)\r\n   .required();\r\n\r\nvar posts = LGTM.validator()\r\n   .maxLength(200)\r\n   .required();\r\n\r\n\r\n// My new validators\r\nvar getUserValidator = LGTM.build([limit, offset, user]);\r\nvar getPostsValidator = LGTM.build([limit, offset, posts]);\r\n```\r\n\r\nAre there some best practices on how I can achieve this?  Thanks!"},"comment":{"url":"https://api.github.com/repos/square/lgtm/issues/comments/123390649","html_url":"https://github.com/square/lgtm/issues/15#issuecomment-123390649","issue_url":"https://api.github.com/repos/square/lgtm/issues/15","id":123390649,"user":{"login":"eventualbuddha","id":1938,"avatar_url":"https://avatars.githubusercontent.com/u/1938?v=3","gravatar_id":"","url":"https://api.github.com/users/eventualbuddha","html_url":"https://github.com/eventualbuddha","followers_url":"https://api.github.com/users/eventualbuddha/followers","following_url":"https://api.github.com/users/eventualbuddha/following{/other_user}","gists_url":"https://api.github.com/users/eventualbuddha/gists{/gist_id}","starred_url":"https://api.github.com/users/eventualbuddha/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/eventualbuddha/subscriptions","organizations_url":"https://api.github.com/users/eventualbuddha/orgs","repos_url":"https://api.github.com/users/eventualbuddha/repos","events_url":"https://api.github.com/users/eventualbuddha/events{/privacy}","received_events_url":"https://api.github.com/users/eventualbuddha/received_events","type":"User","site_admin":false},"created_at":"2015-07-21T16:20:03Z","updated_at":"2015-07-21T16:20:03Z","body":"Your approach seems like a reasonable one. I'm not opposed to adding something in core if it doesn't interfere with the existing flexibility to do what you ended up with. Perhaps changing the behavior of `.build()` to return distinct objects rather than simply returning the same one over and over again?"}}