{"action":"created","issue":{"url":"https://api.github.com/repos/vacationlabs/haskell-webapps/issues/47","repository_url":"https://api.github.com/repos/vacationlabs/haskell-webapps","labels_url":"https://api.github.com/repos/vacationlabs/haskell-webapps/issues/47/labels{/name}","comments_url":"https://api.github.com/repos/vacationlabs/haskell-webapps/issues/47/comments","events_url":"https://api.github.com/repos/vacationlabs/haskell-webapps/issues/47/events","html_url":"https://github.com/vacationlabs/haskell-webapps/pull/47","id":186289935,"number":47,"title":"Product types and queries and user/tenant activation","user":{"login":"wz1000","id":4812498,"avatar_url":"https://avatars.githubusercontent.com/u/4812498?v=3","gravatar_id":"","url":"https://api.github.com/users/wz1000","html_url":"https://github.com/wz1000","followers_url":"https://api.github.com/users/wz1000/followers","following_url":"https://api.github.com/users/wz1000/following{/other_user}","gists_url":"https://api.github.com/users/wz1000/gists{/gist_id}","starred_url":"https://api.github.com/users/wz1000/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wz1000/subscriptions","organizations_url":"https://api.github.com/users/wz1000/orgs","repos_url":"https://api.github.com/users/wz1000/repos","events_url":"https://api.github.com/users/wz1000/events{/privacy}","received_events_url":"https://api.github.com/users/wz1000/received_events","type":"User","site_admin":false},"labels":[{"id":464516618,"url":"https://api.github.com/repos/vacationlabs/haskell-webapps/labels/sprint","name":"sprint","color":"5319e7","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":25,"created_at":"2016-10-31T13:58:51Z","updated_at":"2016-11-10T07:45:38Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/vacationlabs/haskell-webapps/pulls/47","html_url":"https://github.com/vacationlabs/haskell-webapps/pull/47","diff_url":"https://github.com/vacationlabs/haskell-webapps/pull/47.diff","patch_url":"https://github.com/vacationlabs/haskell-webapps/pull/47.patch"},"body":"Goals:\r\n\r\n- Adding activation for user/tenant creation\r\n- Implement Product API\r\n\r\nDone:\r\n\r\n- Type class for DB query monad for finer grained separation of effects\r\n- Interface for product queries\r\n\r\n# How queries on products will work\r\n\r\nServant provides an API to accept `QueryParams` in the URL\r\n\r\n    type ProductAPI = \"products\" :> QueryParams \"filter\" ProductFilter :> Get '[JSON] [Product]\r\n\r\nA handler for this API can be implemented like so:\r\n\r\n    productHandler :: [ProductFilter] -> App Product\r\n    productHandler [] = ..\r\n    productHandler xs = let x = mconcat xs in ...\r\n\r\nThis is possible because `ProductFilter`, like `ProductView` and `ProductComparator` compose and thus form a monoid. Now the user can query like this:\r\n\r\n    /products?filter=title:sometitle&filter=type:physical\r\n\r\nand `mconcat` will compose all the filters supplied to give you a new filter with which we can write the SQL query.\r\n\r\nThe mechanism is the same for `ProductView` and `ProductComparator`. See `ProductQuery.hs` for more details on how this is handled."},"comment":{"url":"https://api.github.com/repos/vacationlabs/haskell-webapps/issues/comments/259622545","html_url":"https://github.com/vacationlabs/haskell-webapps/pull/47#issuecomment-259622545","issue_url":"https://api.github.com/repos/vacationlabs/haskell-webapps/issues/47","id":259622545,"user":{"login":"saurabhnanda","id":28369,"avatar_url":"https://avatars.githubusercontent.com/u/28369?v=3","gravatar_id":"","url":"https://api.github.com/users/saurabhnanda","html_url":"https://github.com/saurabhnanda","followers_url":"https://api.github.com/users/saurabhnanda/followers","following_url":"https://api.github.com/users/saurabhnanda/following{/other_user}","gists_url":"https://api.github.com/users/saurabhnanda/gists{/gist_id}","starred_url":"https://api.github.com/users/saurabhnanda/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/saurabhnanda/subscriptions","organizations_url":"https://api.github.com/users/saurabhnanda/orgs","repos_url":"https://api.github.com/users/saurabhnanda/repos","events_url":"https://api.github.com/users/saurabhnanda/events{/privacy}","received_events_url":"https://api.github.com/users/saurabhnanda/received_events","type":"User","site_admin":false},"created_at":"2016-11-10T07:45:38Z","updated_at":"2016-11-10T07:45:38Z","body":"Comments:\r\n\r\n* Is there any way to run custom SQL statements in-sync with the Persistent migration? For example, if I want to add a custom `CHECK CONSTRAINT` on a table, how do I do it?\r\n* A user can belong to many roles. The current schema restricts a user to belong to a single role only.\r\n* Why have `HasTimestamp` instances not been defined for all relevant tables?\r\n* Shouldn't `dbUpdateTenant` return the updated tenant record back? Shouldn't `activateTenant` return the updated tenant record back? Is there an efficient version of `INSERT... RETURNING` or `UPDATE...RETURNING` in Persistent?\r\n  * Shouldn't **all** DB APIs inserting/updating DB rows, return the updated/inserted rows?\r\n* Any way to write a wrapper on `insert` and `update` to make them take care of `createdAt` and `updatedAt` fields automagically? (I see you already have `applyUpdate`, but that's not being used everywhere)\r\n* I removed the `requirePermission (EditUser uid)` expression from `dbUpdateUsers`, and the code compiled! Is there a way to make the code compilation fails if someone forgets to put a call to `requirePermission`?\r\n* Is it possible to model the Product in such a way that we don't need the following runtime checks? Basically make illegal states unrepresentable?\r\n\r\n```\r\n     case piType of\r\n       Phys -> when (any (\\VariantI{..} -> isNothing viWeightInGrams\r\n                                        || isNothing viWeightDisplayUnit)\r\n                         piVariants) $\r\n                 throwError PhysicalProductFieldsMissing\r\n       Dig -> when (any (\\VariantI{..} -> isJust viWeightInGrams\r\n                                       || isJust viWeightDisplayUnit)\r\n                         piVariants) $\r\n                 throwError DigitalProductExtraFields\r\n```\r\n\r\n(contd)"}}
{"actor":{"display_login":"saurabhnanda"}}