{"action":"created","issue":{"url":"https://api.github.com/repos/sangria-graphql/sangria/issues/88","labels_url":"https://api.github.com/repos/sangria-graphql/sangria/issues/88/labels{/name}","comments_url":"https://api.github.com/repos/sangria-graphql/sangria/issues/88/comments","events_url":"https://api.github.com/repos/sangria-graphql/sangria/issues/88/events","html_url":"https://github.com/sangria-graphql/sangria/issues/88","id":111222382,"number":88,"title":"[Middleware] Replace queryVal or context along the way","user":{"login":"luisobo","id":825073,"avatar_url":"https://avatars.githubusercontent.com/u/825073?v=3","gravatar_id":"","url":"https://api.github.com/users/luisobo","html_url":"https://github.com/luisobo","followers_url":"https://api.github.com/users/luisobo/followers","following_url":"https://api.github.com/users/luisobo/following{/other_user}","gists_url":"https://api.github.com/users/luisobo/gists{/gist_id}","starred_url":"https://api.github.com/users/luisobo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/luisobo/subscriptions","organizations_url":"https://api.github.com/users/luisobo/orgs","repos_url":"https://api.github.com/users/luisobo/repos","events_url":"https://api.github.com/users/luisobo/events{/privacy}","received_events_url":"https://api.github.com/users/luisobo/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":3,"created_at":"2015-10-13T16:50:26Z","updated_at":"2015-10-14T23:06:18Z","closed_at":null,"body":"Hey there,\r\n\r\nI'm implementing authorization and I have the current scenario: the request gets a token in a header, I can decode this token locally for authentication but if any of the fields that were requested required to check the roles I have a query an external service to get the roles of the user. Currently I have these options:\r\n\r\n- in the before query make that api call so roles info is available to every `beforeField` call. Problem here is that I'd paying the cost of that api call even for the fields that do not require authorization. This is a no go\r\n- Making the api call for every field that requires authorization. Suboptimal too, I'd be making the same api call multiple times per query. I don't have many fields that require this but I much rather no go with this.\r\n\r\nThe ideal scenario for me would be to do that basic and local authentication in the beforeQuery and in the first field, and only the first, that has a Permission tag, make the external api call and replace the current user object (without role info) with the one I just got from the external service, the one with roles. That way, if any other field needs roles, it does not have to make the external api call.\r\n\r\nOne way to achieve this is by retuning a queryVal in beforeField, so that value becomes the new queryVal.\r\n\r\nI'm not sure how this would play with parallel queries to be honest.\r\n\r\nThat or any other solution that satisfies those limitations would be very much appreciated. In any case, thanks for you time!\r\n"},"comment":{"url":"https://api.github.com/repos/sangria-graphql/sangria/issues/comments/148228791","html_url":"https://github.com/sangria-graphql/sangria/issues/88#issuecomment-148228791","issue_url":"https://api.github.com/repos/sangria-graphql/sangria/issues/88","id":148228791,"user":{"login":"OlegIlyenko","id":156569,"avatar_url":"https://avatars.githubusercontent.com/u/156569?v=3","gravatar_id":"","url":"https://api.github.com/users/OlegIlyenko","html_url":"https://github.com/OlegIlyenko","followers_url":"https://api.github.com/users/OlegIlyenko/followers","following_url":"https://api.github.com/users/OlegIlyenko/following{/other_user}","gists_url":"https://api.github.com/users/OlegIlyenko/gists{/gist_id}","starred_url":"https://api.github.com/users/OlegIlyenko/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/OlegIlyenko/subscriptions","organizations_url":"https://api.github.com/users/OlegIlyenko/orgs","repos_url":"https://api.github.com/users/OlegIlyenko/repos","events_url":"https://api.github.com/users/OlegIlyenko/events{/privacy}","received_events_url":"https://api.github.com/users/OlegIlyenko/received_events","type":"User","site_admin":false},"created_at":"2015-10-14T23:06:18Z","updated_at":"2015-10-14T23:06:18Z","body":"I played a bit with some ideas... I think that `beforeField` would be wrong place for this kind of logic: it's already too late since query execution is already started. As you mentioned, the parallel execution is a big concern here as well. The tag collection is a very good idea I think, but `Middleware` is not very suitable for this.\r\n\r\nI generalized the complexity analysis and rewritten it in terns of simple reduce functions. This new mechanism (`QueryReducer`) can also be used for other things like `FieldTag` collection. You can use it like this:\r\n\r\n```scala\r\nval permissionFetcher = QueryReducer.collectTags[MyContext, String] {\r\n  case Permission(name) ⇒ name\r\n}  { (permissionNames, ctx) ⇒\r\n  val permissions = ctx.myPermissionService.fetchPermissions(permissionNames)\r\n  \r\n  Right(ctx.copy(permissions = permissions))\r\n\r\n  // or if `permissions` is `Future[T]`\r\n  // Left(permissions map (p ⇒ ctx.copy(permissions = p)))\r\n}\r\n\r\nExecutor.execute(schema, query,\r\n          userContext = new MyContext(...),\r\n          queryReducers = permissionFetcher :: Nil)\r\n```\r\n\r\n`permissionFetcher` will go though the normalized query tree and will collect all of the permissions. Then the second function would be called with the collected results. it allows you to transform/enhance `Ctx`. All these things happen before query is executed. What do you think, will this help in your case? \r\n\r\nIt's not released yet, but I published the snapshot:\r\n\r\n```scala\r\nresolvers += \"Sonatype snapshots\" at \"https://oss.sonatype.org/content/repositories/snapshots/\"\r\nlibraryDependencies += \"org.sangria-graphql\" %% \"sangria\" % \"0.4.3-SNAPSHOT\"\r\n```\r\n\r\nI would appreciate you if you could try it out and share your feedback and any thoughts concerning this new mechanism."}}