Giter Site home page Giter Site logo

graphql-crystal's People

Contributors

andilavera avatar blaet avatar cpunion avatar docelic avatar jney avatar rcmoret avatar stanislav-lapata avatar timscott avatar wealth avatar ziprandom avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

graphql-crystal's Issues

Ready for production?

Hey, I'm wondering if it can be used in production and if it's faster than the go version 1F9VowF8vVFcCHoNdaTZAXGT5H4gJ9mo35

Also, if the parser is x50 times slower, why isn't changed with the c bindings to libgraphqlparser??

Git tags

Please add a version 0.1.0 release due to shards specs.

When libraries are installed from Git repositories, the repository is expected to have version tags following the semver format, prefixed with a v. Examples: v1.2.3 or v2.0.0-rc1.

Response for graphiQL does not work correctly

I use https://github.com/skevy/graphiql-app
request with params

query IntrospectionQuery {
  __schema {
    queryType {
      name
    }
    mutationType {
      name
    }
    subscriptionType {
      name
    }
    types {
      ...FullType
    }
    directives {
      name
      description
      locations
      args {
        ...InputValue
      }
    }
  }
}

fragment FullType on __Type {
  ...
}

fragment InputValue on __InputValue {
  ...
}

fragment TypeRef on __Type {
  ...
}

return

{"data":{"__schema":{...}, "errors":[{"message":"field not defined.","path":["__schema","subscriptionType"]}]}

Should be without the "errors" key or with the "errors" key but without the "__schema" key
See please here and here
Validate struct should be before execution code in app
also method max_depth should ignore this request

Sorry for my English

Enum Input Error

While working on a PR for this issue, I found another issue. When running this in a test:

TestSchema::Schema.execute(
  "query getAddresses($city: [City]!) { addresses(city: $city) { city } }", {
    "city" => ["London"]
  }
)

I get this error:

{
  "data" => nil, 
  "errors" => [{
    "message" => "variable $city is expected to be of type [City]!", "path" => []
  }]
}

I guess somewhere the raw string values of variables needs to be converted to enum values before the type check, or else the type check for enum needs to allow String.

I'm having trouble seeing where this would be fixed. If you can point me in the right direction, I can add this fix to my upcoming PR.

Introspection

I'm just getting started with graphql-crystal and have created a simple kemal app based on the README sample here. Do I need to do something special to get introspection working?

With this query:

{
  __schema {
    types {
      name
    }
  }
}

I get this result:

{
  "data": {
    "__schema": null
  },
  "errors": [
    {
      "message": "field __schema is not defined for Class",
      "path": [
        "__schema"
      ]
    }
  ]
}

Here's the execution:

post "/graph" do |env|
  query = env.params.json["query"].as(String)
  SCHEMA.execute(query).to_json
end

Thanks for the help.

Use libgraphqlparser instead

AFAIK Crystal allows to bind C libraries; according to the benchmarks in README, current implementation is kinda slow. Maybe reimplement with C bindings?

Rename FIELDS constant to __GRAPHQL_FIELDS

hi,
using both graphql-crystal and crecto generates conflicts on the class.
It looks like they both add FIELDS to the model

error:

already initialized constant User::FIELDS

Is there a way to avoid this conflict?

Use with Jennifer

This might be more of a feature request than a bug. I'm not sure.

I'm trying to use graphql-crystal with jennifer ORM. My type looks like this:

class Post < Jennifer::Model::Base
  mapping(
    id: Primary32,
    title: String,
    body: String
    user_id: Int32,
  )

  belongs_to :user, User
  has_many :comments, Comment

  include GraphQL::ObjectType
  field :id
  field :title
  field :body
  field :comments
end

And I get this compile time error:

Error in src/app.cr:33: instantiating 'GraphQL::Schema::Schema#execute(String, Hash(String, JSON::Type)+)'

    SCHEMA.execute query, variables.as(Hash)
           ^~~~~~~

in lib/graphql-crystal/src/graphql-crystal/schema/schema_execute.cr:34: instantiating 'execute(String, Hash(String, JSON::Type)+, Nil, GraphQL::Schema::Context)'

      def execute(document : String, params = nil, operation_name = nil, context = Context.new(self, max_depth))
      ^

in lib/graphql-crystal/src/graphql-crystal/schema/schema_execute.cr:35: instantiating 'execute(GraphQL::Language::Document, Hash(String, JSON::Type)+, Nil, GraphQL::Schema::Context)'

        execute(Language.parse(document), params, operation_name, context)
        ^~~~~~~

in lib/graphql-crystal/src/graphql-crystal/schema/schema_execute.cr:78: instantiating '(GraphQL::ObjectType | Nil)#try()'

            {query_resolver, @types[query_resolver.try &.graphql_type]}
                                                   ^~~

in lib/graphql-crystal/src/graphql-crystal/schema/schema_execute.cr:78: undefined method 'graphql_type' for Jennifer::Migration::Version (compile-time type is Jennifer::Model::Base+)

            {query_resolver, @types[query_resolver.try &.graphql_type]}
                                                         ^~~~~~~~~~~~

I've tried various things like re-ordering the graphql and jennifer bits. I also tried closing and re-opening the class to add the graphql bits (as shown in the readme), but to no avail.

Crystal 0.25.0 released!

and ... this project can not even compiled now ...

macro didn't expand to a valid program, it expanded to:

================================================================================
--------------------------------------------------------------------------------
   1.         
   2.         private def __typename_field(nil, context)
   3.           
   4.               context.with_self(nil) do
   5.                 self.graphql_type
   6.               end
   7.           
   8.         end
   9.         
--------------------------------------------------------------------------------
Syntax error in expanded macro: field:2: cannot use 'nil' as an argument name

        private def __typename_field(nil, context)

Error When Optional Fields Omitted

I think it should be allowed to omit optional fields from variables. It's seems it's not possible.

Query:

query getShows($date: String!, $location: LocationInput, $favorites: Boolean) {
  shows(date: $date, location: $location, favorites: $favorites) {
    id
  }
}

Variables:

{
  "date": "2018-03-08"
}

Server Schema

type Query {
  shows(date: String!, location: LocationInput, favorites: Boolean): [Show]
}

Result:

{
  "data": null,
  "errors": [
    {
      "message": "missing variable location, missing variable favorites",
      "path": []
    }
  ]
}

The query succeeds with the following:

{
  "date": "2018-03-08",
  "location": null,
  "favorites": null
}

From the docs:

When default values are provided for all variables, you can call the query without passing any variables.

Okay, so I maybe I have to specify defaults of null:

Query:

query getShows($date: String!, $location: LocationInput = null, $favorites: Boolean = null) {
  shows(date: $date, location: $location, favorites: $favorites) {
    id
  }
}

Nope:

{
  "data": null,
  "errors": [
    {
      "message": "variable $location is expected to be of type LocationInput, variable $favorites is expected to be of type Boolean",
      "path": []
    }
  ]
}

Returning classes or modules instead of instances?

The following code does not work because "field :dir do ... " is set to return a class (::MyTest::Dir) instead of an instance (::MyTest::Dir.new).

module MyTest
  schema = GraphQL::Schema.from_schema(%{
    schema {
      query: RootType,
      mutation: RootType
    }

    type RootType {
      dir: DirType
    }

    type DirType {
      current: String
    }
  })

  class Dir
    include GraphQL::ObjectType
    field :current do
      ::Dir.current
    end
  end

  module RootType
    include GraphQL::ObjectType
    extend self

    field :dir do
      ::MyTest::Dir
    end
  end

  schema.query_resolver = ::MyTest::RootType
  schema.mutation_resolver = ::MyTest::RootType

  print schema.execute %Q{
    query {
      dir { current }
    }
  }
end

The behavior is the same if the class is converted to a module, but it is in any case unexpected because on the top of the tree RootType is a module and it works there.

Is this a design decision or unhandled case?

New methods for schema

We already have add_input_types to the schema which allows us to use structs to define inputs. Can we possibly do this for all other things? Such as types, interfaces & fragments.

Before I go diving in the code base, I wanted to see if there a reason it wasn't implemented such as certain limitations.

Dependency Installation

At some point, I realized I was using a pretty outdated version of this lib. The type conversion from JSON::Any didn't work for me but I saw an example where it supposed to work. Anyway, I figured out I can let it run if I point to master in dependencies config like below. There are quite useful commits in the latest master. How about making a release?

dependencies:
  graphql-crystal:
    github: ziprandom/graphql-crystal
    branch: master

Issues with Types

Hi @ziprandom,

First of all thanks for the great work on this!

I'm trying to use it for a project but i'm having a rough time understanding what i'm doing wrong.
So the idea is i'm integrating your lib and fetching data from a database. I'm using Crecto as the database wrapper and i'm defining my models as such:

module Breaktime::Api
  class University < Crecto::Model
    include GraphQL::ObjectType
    property id
    field :id

    property name
    field :name

    property short_name
    field :short_name

    field :bands do
      uni = Repo.get(University, self.id).as(University)
      bands = Repo.get_association(uni, :bands).as(Array(Band))
      bands
    end

    schema "universities" do
      field :name, String
      field :short_name, String
      has_many :bands, Band, dependent: :destroy
    end
  end
end

module Breaktime::Api
  class Band < Crecto::Model
    include GraphQL::ObjectType
    property id
    field :id

    property name
    field :name

    property bio
    field :bio

    property university
    field :university

    schema "bands" do
      field :name, String
      field :bio, String
      belongs_to :university, University
    end
  end
end

If i declare just the University class without the Band class and association i'm having no issues and i'm able to fetch that data without problems from the database.

But when i add the Band class/association then i start getting into these errors:

Error in src/breaktime-api.cr:44: instantiating 'GraphQL::Schema::Schema#execute(Tuple(String, Hash(String, JSON::Type) | Nil))'

    SCHEMA.execute(
           ^~~~~~~

in lib/graphql-crystal/src/graphql-crystal/schema/schema_execute.cr:34: instantiating 'execute(String, (Hash(String, JSON::Type) | Nil), Nil, GraphQL::Schema::Context)'

      def execute(document : String, params = nil, operation_name = nil, context = Context.new(self, max_depth))
      ^

in lib/graphql-crystal/src/graphql-crystal/schema/schema_execute.cr:35: instantiating 'execute(GraphQL::Language::Document, (Hash(String, JSON::Type) | Nil), Nil, GraphQL::Schema::Context)'

        execute(Language.parse(document), params, operation_name, context)
        ^~~~~~~

in lib/graphql-crystal/src/graphql-crystal/schema/schema_execute.cr:83: instantiating 'resolve_selections_for(GraphQL::Language::TypeDefinition+, Array(GraphQL::Language::AbstractNode), (GraphQL::ObjectType | Nil), GraphQL::Schema::Context)'

        result, errors = resolve_selections_for(
                         ^~~~~~~~~~~~~~~~~~~~~~

in lib/graphql-crystal/src/graphql-crystal/schema/schema_execute.cr:138: instantiating '_resolve_selections_for(GraphQL::Language::TypeDefinition+, Array(GraphQL::Language::AbstractNode), (GraphQL::ObjectType | Nil), GraphQL::Schema::Context)'

          _resolve_selections_for(field_definition, _selections, resolved, context)
          ^~~~~~~~~~~~~~~~~~~~~~~

in lib/graphql-crystal/src/graphql-crystal/schema/schema_execute.cr:216: instantiating 'resolve_selections_for((GraphQL::Language::FieldDefinition | Nil), Array(GraphQL::Language::Field), GraphQL::ObjectType, GraphQL::Schema::Context)'

            _result, _errors = resolve_selections_for(
                               ^~~~~~~~~~~~~~~~~~~~~~

in lib/graphql-crystal/src/graphql-crystal/schema/schema_execute.cr:138: instantiating '_resolve_selections_for((GraphQL::Language::FieldDefinition | Nil), Array(GraphQL::Language::AbstractNode), GraphQL::ObjectType, GraphQL::Schema::Context)'

          _resolve_selections_for(field_definition, _selections, resolved, context)
          ^~~~~~~~~~~~~~~~~~~~~~~

in lib/graphql-crystal/src/graphql-crystal/schema/schema_execute.cr:272: instantiating 'resolve_selections_for(GraphQL::Language::AbstractNode+, Array(GraphQL::Language::AbstractNode), (Array(Breaktime::Api::Band) | Array(Breaktime::Api::University) | Array(GraphQL::Language::Directive) | Array(GraphQL::Language::DirectiveDefinition) | Array(GraphQL::Language::EnumValueDefinition) | Array(GraphQL::Language::FieldDefinition) | Array(GraphQL::Language::InputValueDefinition) | Array(GraphQL::Language::InterfaceTypeDefinition) | Array(GraphQL::Language::TypeDefinition) | Array(String) | Bool | Crecto::Model | GraphQL::Language::AbstractNode | GraphQL::Schema::Schema | Int32 | Int64 | String | Time | Nil), GraphQL::Schema::Context)'

          resolve_selections_for(
          ^~~~~~~~~~~~~~~~~~~~~~

in lib/graphql-crystal/src/graphql-crystal/schema/schema_execute.cr:138: instantiating '_resolve_selections_for(GraphQL::Language::AbstractNode+, Array(GraphQL::Language::AbstractNode), (Array(Breaktime::Api::Band) | Array(Breaktime::Api::University) | Array(GraphQL::Language::Directive) | Array(GraphQL::Language::DirectiveDefinition) | Array(GraphQL::Language::EnumValueDefinition) | Array(GraphQL::Language::FieldDefinition) | Array(GraphQL::Language::InputValueDefinition) | Array(GraphQL::Language::InterfaceTypeDefinition) | Array(GraphQL::Language::TypeDefinition) | Array(String) | Bool | Crecto::Model | GraphQL::Language::AbstractNode | GraphQL::Schema::Schema | Int32 | Int64 | String | Time | Nil), GraphQL::Schema::Context)'

          _resolve_selections_for(field_definition, _selections, resolved, context)
          ^~~~~~~~~~~~~~~~~~~~~~~

in lib/graphql-crystal/src/graphql-crystal/schema/schema_execute.cr:210: instantiating 'run_directives(GraphQL::Language::Field, GraphQL::Language::FieldDefinition, Array(GraphQL::Language::AbstractNode), (Crecto::Model | GraphQL::Language::AbstractNode | GraphQL::Schema::Schema), GraphQL::Schema::Context)'

          run_directives(
          ^~~~~~~~~~~~~~

in lib/graphql-crystal/src/graphql-crystal/schema/schema_execute.cr:119: can't cast Tuple(GraphQL::Language::FieldDefinition, Array(GraphQL::Language::AbstractNode), Crecto::Model | GraphQL::Language::AbstractNode | GraphQL::Schema::Schema, GraphQL::Schema::Context) to Tuple(GraphQL::Language::AbstractNode, Array(GraphQL::Language::AbstractNode), GraphQL::ResolveCBReturnType, GraphQL::Schema::Context)

            block.call(*args.as(Args))

Funny thing is if i declare an extra model for Band (the Bnd class seen below) just with your stuff and without Crecto it works (i just have to transfer the data from the Band to the Bnd model on every query:

  class Bnd
    include GraphQL::ObjectType
    property id
    field :id

    property name
    field :name

    property bio
    field :bio

    property university
    field :university

    def initialize(
      @id : Int64? | Int32?,
      @name : String | Nil,
      @bio : String | Nil,
      @university : University?); end
  end

I'm kinda new to Crystal and even GraphQL so it's possible i'm doing something wrong. What strikes me as odd is that it works with the University model isolated (and inheriting from Crecto::Model) and works when i declare the temp class Bnd. But not with the Band (inheriting from Crecto::Model).
What am i doing wrong? If you need access to the full source code let me know.

Once again thank you for your time!

schema init

Can we declare schema in one place?
Your kemal example has schema_string and type declaration:

  # A User
  type User implements UniqueId {
    # users first name
    firstName: String!
    # users last name
    lastName: String!
    # full name string for the user
    fullName: String! @deprecated(reason: "no need to construct this serverside..")
    # users role
    role: UserRole!
    # posts published
    # by this user
    posts: [Post!]
    # total number of posts
    # published by this user
    postsCount: Int!
  }
class User
  include ::GraphQL::ObjectType
  include UniqueId
  field :firstName { first_name }
  field :lastName { last_name }
  field :fullName { "#{@first_name} #{@last_name}" }
  field :posts { POSTS.select &.author.==(self)}
  field :postsCount { POSTS.select( &.author.==(self) ).size }
  field :role
end

rmosolgo/graphql-ruby uses some LateBoundType but it is magic.
Can we add to schema.cr some simple method like:
from_paths(paths : Array)

  1. It will search files in directories
  2. It will filter files by included GraphQL::ObjectType
  3. It will use method to_graphql
  4. it will create schema from converted strings

::GraphQL::Schema.from_paths(['path1', 'path2'])

Uppercase as first field letter

It appears that even though GraphQL supports case-sensitivity, creating fields with first letter uppercase does not work here because it generates invalid function names:

  field :Dir do ... end

Results in:

Error in src/x.cr:32: expanding macro

    field :"Dir" do
    ^

in macro 'field' expanded macro: injection:1, line 1:

>  1.         field(:Dir, "", args, "")  do
   2.   ::MyTest::Dir.new
   3. end
   4.         

macro didn't expand to a valid program, it expanded to:

================================================================================
--------------------------------------------------------------------------------
   1.         
   2.         private def Dir_field(args, context)
   3.           
   4.               context.with_self(args) do
   5.                 ::MyTest::Dir.new
   6.               end
   7.           
   8.         end
   9.         
--------------------------------------------------------------------------------
Syntax error in expanded macro: field:2: unexpected token: (

        private def Dir_field(args, context)

Would a simple fix be to generate functions named field_NAME instead of NAME_field?

Use generics to avoid context casting

Hi, im using your shard in a private project and so far it works great (errors in fields are little difficult to track down though). A thing that is a bit annoying is the casting of the context in every field. Would it be possible to make the Schema-class or the ObjectType-class generics so one does not need to cast the context?

Allow empty strings as valid values ?

I have an issue when I try to make a mutation with an empty string as a value of a create/update.

This is the Schema =>

GRAPHQL_SCHEMA = GraphQL::Schema.from_schema(
  %{
    schema {
      query: QueryType,
      mutation: MutationType
    }

    type QueryType {
      meeting(id: String!): Meeting
    }

    type Meeting {
      id: String!
      title: String
    }

    type MutationType {
      update_meeting(id: String!, meeting: MeetingInputType!): Meeting
    }
  }
)

Everything works like a charm =>

 GRAPHQL_SCHEMA.execute("mutation { update_meeting(id: \"11\", meeting: {title: \"a\"}) {id}}", nil, nil, graphql_context)
 => {"data" => {"update_meeting" => {"id" => "11"}}}

even

 GRAPHQL_SCHEMA.execute("mutation { update_meeting(id: \"11\", meeting: {title: null}) {id}}", nil, nil, graphql_context)
 => {"data" => {"update_meeting" => {"id" => "11"}}}

but when it comes to empty strings, I got

 GRAPHQL_SCHEMA.execute("mutation { update_meeting(id: \"11\", meeting: {title: \"\"}) {id}}", nil, nil, graphql_context)
Unhandled exception:  (CLTK::Parser::Exceptions::NotInLanguage)
  from GraphQL::Language::Parser@CLTK::Parser::_parse<Hash(Int32, Tuple(CLTK::Parser::ProdProc, Int32)), Hash(Int32, String), Array(String), Array(CLTK::Parser::State), Hash(String, Array(Proc(CLTK::Parser::Environment, Nil))), Array(CLTK::Token), NamedTuple()>:(Array(CLTK::Type) | Bool | Float64 | GraphQL::Language::AbstractNode+ | Int32 | String | Tuple(String, String) | Nil)
  from GraphQL::Language::Parser@CLTK::Parser::parse<Array(CLTK::Token), NamedTuple()>:(Array(CLTK::Type) | Bool | Float64 | GraphQL::Language::AbstractNode+ | Int32 | String | Tuple(String, String) | Nil)
  from GraphQL::Language::parse<String, NamedTuple()>:GraphQL::Language::Document
  from GraphQL::Language::parse<String>:GraphQL::Language::Document
  from GraphQL::Schema::Schema#execute<String, Nil, Nil, CustomContext>:(Hash(String, Array(GraphQL::ReturnType) | Array(Hash(String, Array(Int32 | String) | String) | Nil) | Bool | Float64 | GraphQL::Schema::InputType+ | Hash(String, GraphQL::ReturnType) | Int32 | Int64 | String | Nil) | Hash(String, Array(GraphQL::ReturnType) | Bool | Float64 | GraphQL::Schema::InputType+ | Hash(String, GraphQL::ReturnType) | Int32 | Int64 | String | Nil) | Hash(String, Array(Hash(String, Array(String) | String | Nil)) | Nil) | Hash(String, Array(Hash(String, Array(String) | String))))
  from __icr_exec__:(Hash(String, Array(GraphQL::ReturnType) | Array(Hash(String, Array(Int32 | String) | String) | Nil) | Bool | Float64 | GraphQL::Schema::InputType+ | Hash(String, GraphQL::ReturnType) | Int32 | Int64 | String | Nil) | Hash(String, Array(GraphQL::ReturnType) | Bool | Float64 | GraphQL::Schema::InputType+ | Hash(String, GraphQL::ReturnType) | Int32 | Int64 | String | Nil) | Hash(String, Array(Hash(String, Array(String) | String | Nil)) | Nil) | Hash(String, Array(Hash(String, Array(String) | String))))
  from __crystal_main
  from Crystal::main_user_code<Int32, Pointer(Pointer(UInt8))>:Nil
  from Crystal::main<Int32, Pointer(Pointer(UInt8))>:Int32
  from main

Is this a bug ?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.