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

Convert arbitrary scalar values into ASTs and back #1817

Open
anhldbk opened this issue Apr 8, 2019 · 6 comments
Open

Convert arbitrary scalar values into ASTs and back #1817

anhldbk opened this issue Apr 8, 2019 · 6 comments
Labels

Comments

@anhldbk
Copy link

anhldbk commented Apr 8, 2019

Reporting issues with GraphQL.js

This issue is related to: #1815

Expected:

  • Successfully convert arbitrary scalars (including default values) to ASTs
  • Convert ASTs into dedicated values

Code:

const {
  makeExecutableSchema,
} = require('graphql-tools')

const {
  astFromValue,
  valueFromAST,
  isValidJSValue
} = require('graphql/utilities')

const {
  GraphQLScalarType
} = require('graphql')


const JsonScalarType = new GraphQLScalarType({
  name: 'JSON',
  serialize: (value) => value,
});

const resolveFunctions = {
  JSON: JsonScalarType
};

const typeDefs = `
  scalar JSON
  input Message {
    extra: JSON
    meta: JSON = {}
  }
`

const schema = makeExecutableSchema({
  typeDefs,
  resolvers: resolveFunctions
})

const messageType = schema.getType('Message')

const value = {
  extra: {
    'key': 'andy',
    "kafka.producer.batch": 0
  }
}

// check if there's any error in our value
const errors = isValidJSValue(value, messageType)
// errors will contain detail errors if any
console.log(`Valid: ${errors.length==0}`)

// parse and get value 
const ast = astFromValue(value, messageType)
const conf = valueFromAST(ast, messageType)

Result

$ npm start                                                                                                              master ✱ ◼

> gql@1.0.0 start /Users/andy/Works/gql
> babel-node --presets es2015 run.js

Valid: true
/Users/andy/Works/gql/node_modules/graphql/utilities/astFromValue.js:130
          throw _iteratorError;
          ^

TypeError: Cannot convert value to AST: { key: "andy", kafka.producer.batch: 0 }
    at astFromValue (/Users/andy/Works/gql/node_modules/graphql/utilities/astFromValue.js:193:11)
    at astFromValue (/Users/andy/Works/gql/node_modules/graphql/utilities/astFromValue.js:107:26)

Solution

I have to patch astFromValue: https://github.com/graphql/graphql-js/blob/8aef229cb2/src/utilities/astFromValue.js#L139-L140 by adding following lines before L139

if (typeof serialized === 'object' ){
    return {
      kind: _kinds.Kind.OBJECT,
      value: serialized
    };
}

Question

The solution above may not be adequate. Would you please tell me it's worth to make a RP against this issue and how to properly resolve it?

@anhldbk
Copy link
Author

anhldbk commented Apr 8, 2019

@IvanGoncharov FYI

@IvanGoncharov IvanGoncharov added PR: feature 🚀 requires increase of "minor" version number bug and removed PR: feature 🚀 requires increase of "minor" version number labels Apr 10, 2019
@IvanGoncharov
Copy link
Member

@anhldbk Thanks for detail report.
Also, note that isValidJSValue is deprecated and will be removed in upcoming 15.0.0:

/**
* Deprecated. Use coerceValue() directly for richer information.
*
* This function will be removed in v15
*/
export function isValidJSValue(
value: mixed,
type: GraphQLInputType,
): Array<string> {

I have to patch astFromValue: https://github.com/graphql/graphql-js/blob/8aef229cb2/src/utilities/astFromValue.js#L139-L140 by adding following lines before L139

It's a little bit more complex, correct AST node for object value should contain fields instead of value:

export type ObjectValueNode = {
+kind: 'ObjectValue',
+loc?: Location,
+fields: $ReadOnlyArray<ObjectFieldNode>,
};

I think correct solution would be introduce serializeAsLiteral (opposite to parseLiteral) and make it default to astFromValueUntypped(this.serialize(value)) (opposite to valueFromASTUntyped).

@anhldbk
Copy link
Author

anhldbk commented Apr 14, 2019

@IvanGoncharov So when users define a custom scalar, they must provide a function named serializeAsLiteral?

@IvanGoncharov
Copy link
Member

IvanGoncharov commented Apr 15, 2019

@anhldbk It's designed proposal for a fix. Idea is to have optional serializeAsLiteral that if omitted equals to astFromValueUntypped(this.serialize(value)).

@IvanGoncharov IvanGoncharov added this to the v14.x.x milestone May 27, 2019
@vitramir
Copy link

I think this is a better fix:

    if (typeof serialized === 'object') {
      if (Array.isArray(serialized)) {
        return {
          kind: Kind.LIST,
          values: serialized.map(v => astFromValue(v, type)),
        };
      }

      return {
        kind: Kind.OBJECT,
        fields: Object.entries(serialized).map(([k, v]) => {
          return {
            kind: 'ObjectField',
            name: { kind: 'Name', value: k },
            value: astFromValue(v, type),
          };
        }),
      };
    }

I think we can keep Enum and GraphQLID logic in this function and move all the stuff regarding scalars (boolean, string, number and this fix also) to astFromValueUntyped function.

@IvanGoncharov Do you want me to make PR for these changes?

@andimarek
Copy link
Contributor

andimarek commented Jan 17, 2020

just fyi: GraphQL Java will add a very similar method to the proposed serializeAsLiteral. (cc @bbakerman)

graphql-java/graphql-java#1745

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

No branches or pull requests

4 participants