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

Cast Int as String if the jsonValue is of type Int. (Custom Scalars better compatibility) #402

Merged
merged 3 commits into from
Aug 5, 2019
Merged

Cast Int as String if the jsonValue is of type Int. (Custom Scalars better compatibility) #402

merged 3 commits into from
Aug 5, 2019

Conversation

cadizjavier
Copy link
Contributor

@cadizjavier cadizjavier commented Nov 6, 2018

The support of Apollo for custom scalars is quite simple.
Referring to this issue all custom scalars are mapped to String.
The recommended option for this cases is try to adjust the GraphQL backend for using the built-in scalars or just pass the --passthroughCustomScalars flag at the codegen tool.
Passing the flag is a really good option but it is super annoying when the Backend defines dozens of custom types that potentially conflicts with the app types.
The solution is to use the built-in scalars and just expect that everything custom returns as a String and do the parsing in the Data layer.

The main issue with this is that the Apollo parser breaks when the custom scalar is of type Int since it fails to do the casting to String.
This happens for example when a custom type called Timestamp represents the value as 1540993726. Then the Apollo parser treats it as String but fails to do the casting since isn't a String really, it comes in the JSON as Int.

This PR solves this. I adjusted all the tests to pass but they need some check if I did something weird there, specially the testGetScalarListWithWrongType one since I don't know if I used the proper approach there.
This can be potentially used for Bools too if necessary.

As a side note this kinda helps to fix the following issues without using the --passthroughCustomScalars flag and then parse the data to the proper type in the Model layer or something.

  • 23 Open
  • 262 Closed
  • 218 Open
  • 94 Open. Kinda handles this one too since Date is a custom scalar and regarding the returned value it will parse everything to String.

@almassapargali
Copy link

Hey @martijnwalraven, can you take a look at this PR, we're having this issue as well, and looks like it's pretty common.

XCTFail("Unexpected error: \(error)")
}
}
let value = try readFieldValue(field, from: object) as! String
Copy link
Contributor

@designatednerd designatednerd Jul 22, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would strongly recommend against force-unwrapping here as instead of failing the test, it crashes the whole test suite. Usually I do something like

guard let foo = bar as? String else {
   XCTFail("Wrong type!")
   return 
}

XCTAssertEqual(foo, "expectedFoo")

Same deal on the other three tests

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally agree. Force unwrapping here is a non go. Your solution sounds very clean to me. I will update this code too.

@designatednerd
Copy link
Contributor

@cadizjavier are you interested in continuing this PR?

@cadizjavier
Copy link
Contributor Author

Yes, sorry for the long delay. I provided feedback and context to the comments and will update the PR this week. Thanks for taking over this amazing project !!

@designatednerd
Copy link
Contributor

Hooray! Thank YOU!

@designatednerd designatednerd mentioned this pull request Aug 2, 2019
@cadizjavier
Copy link
Contributor Author

Updated PR with your feedback and some improvements in the testing side.
Test cases now have a differentiation between a different type and a wrong type.
A testGetScalarWithDifferentType test, for example, handle the cases where I pass an Int and the parser returns a String as expected and a testGetScalarWithWrongType test just works as expected the old way (a type that can't be cast to the specified type).

There is actually a kinda confusing test which you commented previously regarding
XCTAssertEqual(result.data?.hero?.name, "10") where you mention why we don't throw if isn't a String.
I'm not exactly sure what to do here. It occurs to me that we can check the original type and see if it isn't a custom scalar type and act accordingly.
I don't know if we have this kind of introspection in the defined types or we can't do better at the implementation level.
Technically speaking I think it is correct to cast as String something that comes as Int with the change made in the PR but I can see pros and cons of both arguments.
What do you think about it ?

@designatednerd
Copy link
Contributor

Yeah - after having spent the last week diving into some codegen and typesystem stuff, we can punt on handling the "Well is this a custom scalar or should it have been a string in the first place?" thing until codegen is in swift. I'll make a follow-up issue for it.

let object: JSONObject = ["name": 10]
let field = GraphQLField("name", type: .scalar(String.self))


XCTAssertNoThrow(try readFieldValue(field, from: object))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is redundant since the test throws - if this were to throw here or where you're getting the value, the whole test would fail

let object: JSONObject = ["name": 10]
let field = GraphQLField("name", type: .nonNull(.scalar(String.self)))


XCTAssertNoThrow(try readFieldValue(field, from: object))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Samesies on the redundancy of no throw

@@ -46,10 +46,26 @@ class ParseQueryResponseTests: XCTestCase {
]
])

XCTAssertNoThrow(try response.parseResult().await())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Samesies on the redundancy

@designatednerd
Copy link
Contributor

I think just fixing the test redundancies and this should be 👍

@designatednerd
Copy link
Contributor

Eh, I'll remove the redundant checks myself, I'd like to get this out with 0.14.0 😃

@cadizjavier
Copy link
Contributor Author

Wohooo, excited that this gets approved. Thanks for the feedback and being supportive.

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

Successfully merging this pull request may close these issues.

3 participants