Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Public access to jsonObject on GraphQLMap #18

Closed
fruitcoder opened this issue Nov 9, 2016 · 7 comments
Closed

Public access to jsonObject on GraphQLMap #18

fruitcoder opened this issue Nov 9, 2016 · 7 comments

Comments

@fruitcoder
Copy link

Hey.
Could you provide public access to the GraphQLMap's jsonObject property? I try to use apollo-ios with another networking library, so I'll need to have the parameters unencoded.
For this I extract the information from the query/mutation:

[
    "query": type(of: query).queryDocument,
    "variables": query.variables?.dict ?? []
]

The dict is a computed property on GraphQLMap which parses the description back to a dictionary (hacky). This is just unnecessary overhead, because I could use the jsonObject directly. Since it's private clients can't change it anyway :)

@martijnwalraven
Copy link
Contributor

We talked a little about this at the Berlin meetup. As I mentioned there, I'm working on a new version that fixes some issues with GraphQLMap (among many other changes).

I want to make sure the new design works for your use case as well. Can you tell me more about what you're trying to do? What networking library are you using, how do you integrate with it, and why do you need to use it instead of the default network transport?

@martijnwalraven
Copy link
Contributor

I just released version 0.4.0 of Apollo iOS, which includes a redesigned GraphQLMap that will hopefully solve your issue. You should be able to access query.variables.jsonValue to get a representation suitable for JSON serialization.

Please give it a try and let me know what you think! (You'll also have to update apollo-codegen to 0.9, but it will warn you if you don't.)

@fruitcoder
Copy link
Author

Hey Martijn!
I just updated Carthage :) It looks like I imagined it to be, thanks!
I actually dropped Moya for the GraphQL part since the mindset of static routes felt like contradicting the approach of GraphQL. I will stub and test my view models by stubbing the network transport with fake responses.
I really don't know if there should be a separation of concerns for the network and parsing part, since those are so interconnected. Usually, I test whether my parsing code can deal with bad json, but basically you already handle this automatically. Everything that should go wrong should be network errors :) so I'm happy with how you implemented it!

Thanks for your work!

@martijnwalraven
Copy link
Contributor

Good to hear! I agree Maya doesn't add much value here and seems more confusing than helpful.

I'd be interested in hearing more about how you approach testing, to better understand the requirements and to see if there's anything more we could add to make it easier.

Stubbing the network transport is probably the best solution right now, especially if you'd like to test for error conditions. I think you're right and you don't you need to test for bad JSON. The only bad JSON I can think of would be an invalid GraphQL response, probably as a result of a bug in the server library. So you're left with either a network or GraphQL errors to test for.

I'm wondering how adding caching and consistency management (which I'm working on right now) will affect testing. Depending on what you're testing, it may make sense to preload the store with test data for example, instead of stubbing the network transport.

@fruitcoder
Copy link
Author

Hey Martijn,

I've now tried and failed to stub a fake NetworkTransport for a query. I tried:

class DetailsQueryStubTransport: NetworkTransport {
  func send<Operation : GraphQLOperation>(operation: Operation, completionHandler: @escaping (GraphQLResponse<Operation>?, Error?) -> Void) -> Cancellable {
    if operation is DetailsQuery {
      completionHandler?(GraphQLResponse(operation: operation, rootObject: JSONObject(dictionaryLiteral: ??))
    }
  }
}

but I don't really know what to put in there. For me it would be awesome if I could directly stub the a json response string like this:

{
  "data": {
    "viewer": {
      "clip": {
        "id": "abcd",
        "title": "test title",
        "description": null,
        "teaserImages": {
          "edges": []
        }
     }
  }
}

To make that work you could somehow inject an encoded json response (Data) that will in turn will call the completion handler with a valid GraphQLOperation.Data.

Also, I changed the access level of the HTTPNetworkTransport class, to make it easy to subclass and debug, why a request might have gone wrong (easier logging): here

@martijnwalraven
Copy link
Contributor

You should be able to just construct a GraphQLResponse from a Swift-based JSON representation, like this:

let response = GraphQLResponse(operation: query, rootObject: [
  "data": [
    "hero": ["__typename": "Droid", "name": "R2-D2"]
   ]
])

(See ParseQueryResponseTests.swift for more examples.)

If you really want to pass in a JSON string instead, you could create a helper method that uses JSONSerialization.jsonObject to parse the string into a dictionary, something like this:

func jsonObject(string: String) -> JSONObject {
  let data = string.data(using: .utf8)!
  return (try! JSONSerialization.jsonObject(with: data, options: [])) as! JSONObject
}

@fruitcoder
Copy link
Author

I will try that, thanks :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants