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

Scalar query parameter treated as int32 #305

Open
airomega opened this issue Jan 25, 2019 · 14 comments
Open

Scalar query parameter treated as int32 #305

airomega opened this issue Jan 25, 2019 · 14 comments

Comments

@airomega
Copy link

airomega commented Jan 25, 2019

I have an ID that's been generated using Go's Hash32 func

I ran into an issue using the Int type in my schema discovered that uint32 is not supported as Int in Graphql

I introduced a Uint scalar thanks to this post and updated my schema:

type User{
    hash: Uint!
    forename: String
    surname: String
    email: String!
}

scalar Uint

schema {
  query: Query
}

type Query {
  user(hash: Uint!): user
}

When I run the query
{ user(hash: 3626262620) { forename } }

I get the following error:
"message": "graphql: panic occurred: strconv.ParseInt: parsing "3111942731": value out of range"

The error is being thown here after applySelectionSet

I'm hoping that I've made a mistake. However it looks like all integer numbers passed in a query parameter will be treated as an int of size 32 - the surrounding switch uses scanner.Int as a case but has no check on size.

I understand the graphql spec treats Int as a 32 bit int - however this seems like a bug on queries/scalars? Or am I doing something wrong?

@tonyghita
Copy link
Member

tonyghita commented Jan 28, 2019

I think we need to extend the input coercion component to take into account scalar values.

Currently the assumption is that all numbers will be coerced to either Integer or Float, where the corresponding Go values are int32 and float64.

In the meantime, have you considered using an ID scalar type instead? The ID type serializes to String to sidestep this problem. You could then provide the coercion functionality in your application code instead, until the input coercion functionality is extended to support the use case.

type User {
  id: ID!
  forename: String
  surname: String
  email: String!
}

type Query {
  user(id: ID!): User
}

https://facebook.github.io/graphql/draft/#sec-ID

@airomega
Copy link
Author

airomega commented Jan 29, 2019

Thanks for the reply.

I did consider using the graphql.ID type - but I'd really rather avoid doing so as its simply the wrong type. I'd be introducing technical debt into my project.

Happy to assist with this if it's an enhancement. Are there any other considerations before updating?

@pavelnikolov
Copy link
Member

pavelnikolov commented Feb 7, 2019

According to the GraphQL specification/documentation the integers are 32 bit (AFAIK, this is due to the fact that it is mapped to the integers used by JavaScript in the browsers).

https://facebook.github.io/graphql/June2018/#sec-Int

@airomega
Copy link
Author

airomega commented Feb 11, 2019

Hi pavelnikolov and tonyghita

I feel there's been a misunderstanding and that this should not have been closed.

I addressed the integer specification in my initial description:

I understand the graphql spec treats Int as a 32 bit int - however this seems like a bug on queries/scalars?

This is not an Integer! It's a custom Scalar. To treat all numbers as 32 bit is quite course - especially as it locks down the introduction of larger custom types.

I totally agree the Int types should be restricted to 32 bit - however custom types should not have this restriction.

@pavelnikolov
Copy link
Member

pavelnikolov commented Feb 11, 2019

This is a very interesting problem... How are you going to pass a number greater than 32bit from a browser? This is not possible, right? Even if we change the backend to support such types it doesn't make sense... In your custom resolver you can always cast the number to whatever type you want. Unless, I'm missing something obvious... 🤔

@airomega
Copy link
Author

airomega commented Feb 11, 2019

From the Note at the bottom of the link you posted above

Numeric integer values larger than 32‐bit should either use String or a custom‐defined Scalar type, as not all platforms and transports support encoding integer numbers larger than 32‐bit.

It seems my request is in keeping with the graphql spec.

We pass the requests in the request body which is treated as text (or bytes). The number coercion
Dictionary takes places at the receiver. There's no issue in the transport of a larger number

@pavelnikolov
Copy link
Member

OK, I understand. I'll reopen the issue, then. Sorry fro the misunderstanding and thank you for your explanation. PR would be welcome.

@pavelnikolov pavelnikolov reopened this Feb 11, 2019
@airomega
Copy link
Author

Great - happy to help

@gotomgo
Copy link

gotomgo commented Mar 3, 2020

Is this not an issue of the UnmarshalGraphQL for a custom scaler type not being called? Regardless of your interpretation of JSON and int32, if I have a custom scaler type that has marshalers, I always want it to be called. In my case, custom marshaling is invoked only if the value is passed as a string, otherwise it is not called and falls thru to BasicLit validation where it barfs

@gotomgo
Copy link

gotomgo commented Mar 3, 2020

The issue being that we are pre custom marshaling and dealing with very basic types (Json types), then the scanner.Int handling has to be way more lenient:

I don't like this code but it works. Note the use of 0 for the radix, instead of 10. This will allow the parser to look for common radix prefixes and do the right thing. Just cuz you don't pass your #'s via Json as 0xFF00 doesn't mean it should not be allowed. Also, none of this code is covered by unit tests, and given how fundamental it is, it should be.

case scanner.Int:
	value, err := strconv.ParseInt(lit.Text, 0, 64)
	if err != nil {
		value, err := strconv.ParseUint(lit.Text, 0, 64)
		if err != nil {
			panic(err)
		}
		return value
	}

	if (value <= math.MaxInt32) && (value >= math.MinInt32) {
		return int32(value)
	}

	return value

@kolanos
Copy link

kolanos commented Sep 13, 2021

@pavelnikolov This appears to be a bug more than an enhancement? If one defines a custom scalar, it will still overflow because the int32 casting occurs regardless.

@pavelnikolov
Copy link
Member

pavelnikolov commented Sep 16, 2021

@kolanos Pull requests are welcome

@suessflorian
Copy link
Contributor

suessflorian commented Oct 2, 2021

I see @gotomgo's approach to be totally valid, that is simply dropping int32 restrictions on deserialisation of literal scanner.Int's

func (val *PrimitiveValue) Deserialize(vars map[string]interface{}) interface{} {
Inferring radix from input makes total sense too, no reason for us to restrict radix what a great idea.

To my understanding though, there will need to be two separate changes needed as @pavelnikolov earlier comment

According to the GraphQL specification/documentation the integers are 32 bit (AFAIK, this is due to the fact that it is mapped to the integers used by JavaScript in the browsers).

https://facebook.github.io/graphql/June2018/#sec-Int

Primitive Graphql Int should be restricted to int32 sizes, so will investigate where to move this validation.

@pavelnikolov
Copy link
Member

Are you still looking into this issue? A set of failing unit tests would be a good start if you post such an issue.

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

No branches or pull requests

6 participants