Giter Club home page Giter Club logo

graphql-jpa's People

Contributors

jcrygier avatar jegp avatar lesiak avatar manuel-mauky avatar mkuzmentsov avatar ralfeis avatar richard1122 avatar tfraserdg avatar timtebeek 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  avatar  avatar

graphql-jpa's Issues

Custom scalar types

As far as I was able to see, there is no easy way to introduce new scalar types into your handler. Currently, the method seems to be limited to statically coded in the list of types, as follows:

 private GraphQLType getAttributeType(Attribute attribute) {
        if (attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.BASIC) {
            if (String.class.isAssignableFrom(attribute.getJavaType()))
                return Scalars.GraphQLString;
            else if (UUID.class.isAssignableFrom(attribute.getJavaType()))
                return JavaScalars.GraphQLUUID;
            else if (Integer.class.isAssignableFrom(attribute.getJavaType()) || int.class.isAssignableFrom(attribute.getJavaType()))
                return Scalars.GraphQLInt;
            else if (Short.class.isAssignableFrom(attribute.getJavaType()) || short.class.isAssignableFrom(attribute.getJavaType()))
                return Scalars.GraphQLShort;
            else if (Float.class.isAssignableFrom(attribute.getJavaType()) || float.class.isAssignableFrom(attribute.getJavaType())
                    || Double.class.isAssignableFrom(attribute.getJavaType()) || double.class.isAssignableFrom(attribute.getJavaType()))
                return Scalars.GraphQLFloat;
            else if (Long.class.isAssignableFrom(attribute.getJavaType()) || long.class.isAssignableFrom(attribute.getJavaType()))
                return Scalars.GraphQLLong;
            else if (Boolean.class.isAssignableFrom(attribute.getJavaType()) || boolean.class.isAssignableFrom(attribute.getJavaType()))
                return Scalars.GraphQLBoolean;
            else if (Date.class.isAssignableFrom(attribute.getJavaType()))
                return JavaScalars.GraphQLDate;
            else if (LocalDateTime.class.isAssignableFrom(attribute.getJavaType()))
                return JavaScalars.GraphQLLocalDateTime;
            else if (LocalDate.class.isAssignableFrom(attribute.getJavaType()))
                return JavaScalars.GraphQLLocalDate;
            else if (attribute.getJavaType().isEnum()) {
                return getTypeFromJavaType(attribute.getJavaType());
            } else if (BigDecimal.class.isAssignableFrom(attribute.getJavaType())) {
                return Scalars.GraphQLBigDecimal;
            }
        }
//[...]
}

Please, do correct me if I am wrogn here and misunderstood something.

It would be beneficial to be able to register this way or another new type for which there is an implementation of GraphQLScalarType made. We had several (to say the least) such types in our systems, without them we can't use this library at the current stage of development.

I am happy to help if you will let me, but I would like to know what kind of implementation you would like to see here? We can do it in several different ways IMHO:

  1. Annotation to find and register all classes implementing GraphQLScalarType automatically - it will require a bit of work, but it's doable nevertheless. It would be way easier if this would be a Spring library, not a general library...
  2. Static map or singleton to hold all the types as your application will boot up. In this case, it would enable further extension via annotations based on frameworks and their prefered way of handling them.
  3. ...? Waiting to any proposals...

I think a combination of 2. followed by 1. per framework makes most of sense here...

Publish New Release to Bintray

Could you please publish a new release to Bintray? It would be helpful if the fixes since the last release (such as BigDecimal support) would be packaged in an artifact.

Clarify License

Please clarify the license for this project. There is no LICENSE file in the root of the repository.
The only hint I've found was at bintray where it says the license is MIT.
This is also the license that graphql-java uses.
However, it would be nice to make the license clear and easy to find.

Jar file

How can i get the jar file of this project. I think its not in maven repository and also i'm facing some issue while using this also

<dependency>
  <groupId>com.crygier</groupId>
  <artifactId>graphql-jpa</artifactId>
  <version>0.3</version>
  <type>pom</type>
</dependency>

Remove dependency on Spring and Groovy

First, congratulations for your project and thanks for your open source contribution!

We have rewritten your test cases in pure Java - just keeping junit dependency - to help us track some corner issues. Please let me know if you are interested by a pull request. Maybe it could help the project since Groovy can be scary for some developpers and not everyone want to add Spring to their stack.

Documentation

Hi,

I was just wondering if you have some basic documentation about this. Interested in using it but just wondering if its the right fit.

ElementCollection seems to only support enums

I have an element collection of 'Class' which is used to persist the class name of an object. When the GraphQLSchemaBuilder hits this attribute, it checks to see if it's an enum (which it isn't) and if not, returns null.

This then causes a null to be passed into the constructor for GraphQLList.

Am I doing something wrong, or is this a bug?

Add an option to avoid lazy loading

Hello, and thanks for your amazing work.

I tried graphql-jpa on very simple projects and it worked simply, out-of-the-box, which is really neat !

Something I'm concerned about though, is lazy loading.

GraphQL is very good for the client to tell "I want that piece of information, and only that", which, translated into the ORM-world could mean : "I want that entity, this relation to that other entity, but not this other one".

What would be awesome, would be to add an ability, when creating the GraphQLQuery, to generate an "adaptative" query, using FETCH JOIN to fetch all the entities needed at once, avoiding lazy loading when sending the response to the client.

Not sure how to implement it, but by parsing the client's query (and the field he is asking for), and analyzing the annotations on the fields of the entity returned, I guess it should be possible.

What do you think ? Is this something you're thinking about ? Working on ? Is it too difficult ? Out of scope ?

Thanks a lot !

Eager Fetching all Many-to-one/One-to-one Mappings removes result, when column is "nullable"

All many-to-one and one-to-one Mappings are eagerly fetched.
This removes all results, when the many-to-one/one-to-one mappings has no data.

So this query, will have no result:

def query = '''
        {
            Human(id: "1004") {
                name
                homePlanet
                favoriteDroid {
                    name
                }
            }
        }
'''

But I think, the correct result should be

Human: [
	[name:'Wilhuff Tarkin', homePlanet:null, favoriteDroid:null]
]

Allow Schema manipulation to support mutations

At the moment the whole Schema is generated under the hood by the library without any way of editing it as developer. This way it's not possible to have mutations.

The reason for this is in GraphQLSchemaBuilder.getGraphQLSchema()`:

public GraphQLSchema getGraphQLSchema() {
    GraphQLSchema.Builder schemaBuilder = GraphQLSchema.newSchema();
    schemaBuilder.query(getQueryType());
    return schemaBuilder.build();
}

This method is invoked by GraphQLExecutor.createGraphQL():

@PostConstruct
protected void createGraphQL() {
    if (entityManager != null)
        this.graphQL = new GraphQL(new GraphQLSchemaBuilder(entityManager).getGraphQLSchema());
}

For testing I've changed this locally so that the GraphQLSchemaBuilder now also gets a "schemaEnhancer" function which can be used to "enhance" the schema. The function has the signature BiConsumer<GraphQLSchema.Builder, EntityManager>:

public GraphQLSchemaBuilder(EntityManager entityManager, BiConsumer<GraphQLSchema.Builder, EntityManager> schemaEnhancer) {
    this.entityManager = entityManager;
    this.schemaEnhancer = schemaEnhancer;
}

public GraphQLSchema getGraphQLSchema() {
    GraphQLSchema.Builder schemaBuilder = GraphQLSchema.newSchema();
    schemaBuilder.query(getQueryType());

    if(schemaEnhancer != null) {
        schemaEnhancer.accept(schemaBuilder, entityManager);
    }

    return schemaBuilder.build();
}

This way I can provide a function to manipulate the schema after the query type is generated but before the actual schema is build. This way I can add mutations like this:

public class Mutation {

public void addMutation(GraphQLSchema.Builder schemaBuilder, EntityManager entityManager) {
	GraphQLObjectType mutation = newObject()
			.name("Mutation")
			.field(createMyMutation(entityManager))
			.build();

	schemaBuilder.mutation(mutation);
}

private GraphQLFieldDefinition createMyMutation(EntityManager entityManager) {
		return newFieldDefinition()
				.name("myMutation")
				.type(new GraphQLTypeReference("Person"))
				.argument(newArgument()
						.name("name")
						.type(new GraphQLNonNull(GraphQLString))
						.build())
				.dataFetcher(env -> {
					String name = env.getArgument("name");
					Person person = new Person(name);

					return entityManager.merge(person);
				}).build();
	}
}

// Spring Boot
@Configuration
public class DiSetup {
    @Bean
    public GraphQLExecutor graphQLExecutor(EntityManager entityManager, Mutation mutation) {
        return new GraphQLExecutor(entityManager, mutation::addMutation);
    }
}

I need to pass through the enhancer function to the GraphQLExecutor and from there to the GraphQLSchemaBuilder. In my test project I'm using Spring Boot and therefore have to configure the dependency injection accordingly.

It works for me and I would be happy to provide a PullRequest. However, I'd like to discuss the approach beforehand because I'm not sure if this approach would work for environments other then Spring Boot and I'm not sure if the approach of passing an enhancer function is the best possible way. Maybe there is a better way that better uses dependency injection mechanisms?

EDIT:
Another aspect is that there may be use cases where the developer likes to add or manipulate the query part of the Schema. At the moment this isn't possible and it wouldn't be possible with my proposes solution either because the creation of the query part of the Schema is hard coded. Maybe we could find a more flexible way of defining these kinds of things.

Embedded Annotation does not work correctly

Hi,

I had problems to use the @Embedded Annotation with the previous solution from:
#26

This solution does not "flatten" the attributes, instead it keeps the "normal" java model hierarchy.
So @embeddable Annotations behave like @manytoone and @OnetoOne Annotations.

The query looks like that:
Spaceship (id: "1000"){ name, created { user {id}}, modified {date} }

where "created" and "modified" are of Typ DateAndUser, which is a @embeddable Class

@Embeddable public class DateAndUser { public Date date; @ManyToOne public User user; }

Long ID argument results in ValidationError("Variable type doesn't match")

As seen in https://github.com/timtebeek/graphql-jpa-enum/commit/a918b08b59716fb50422ac49bc86b80c085206ef#diff-5f96389b4149619689e68e09eb2cd84dR59

When I try to pass an id argument of type Long to execute I get a ValidationError("Variable type doesn't match").
Debugging results in the following state:

inputType = GraphQLScalarType{name='Long', description='Long type', coercing=graphql.Scalars$2@25dc2c0}
variableType = GraphQLNonNull{wrappedType=GraphQLScalarType{name='Long', description='Built-in Long', coercing=org.crygier.graphql.JavaScalars$1@192ecf8}}

How can I update graphl-jpa to handle the Long id argument correctly?

Move annotations to api sub module

Currently the entire graphql-jpa has to be on the package of your domain EJB/JAR. It would be convenient to ship the annotation classes in a separate module.

Adding GregorianCalendar support

Currently appears to support Date, but not GregorianCalendar.

Model attributes of java type GregorianCalendar throw UnsupportedOperationException (GraphQLSchemaBuilder, line 135).

Upgrade to graphql-java v3.0.0

Attempts to upgrade to v3.0.0 lead to the following (from @timtebeek):

java.lang.IllegalStateException: Failed to load ApplicationContext
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
...
Caused by: graphql.AssertException: All types within a GraphQL schema must have unique names. No two provided types may have the same name.
No provided type may have a name which conflicts with any built in types (including Scalar and Introspection types).
You have redefined the type 'Episode' from being a 'GraphQLEnumType' to a 'GraphQLEnumType'
	at graphql.schema.SchemaUtil.assertTypeUniqueness(SchemaUtil.java:86)
	at graphql.schema.SchemaUtil.collectTypes(SchemaUtil.java:50)
	at graphql.schema.SchemaUtil.collectTypes(SchemaUtil.java:48)
	at graphql.schema.SchemaUtil.collectTypesForObjects(SchemaUtil.java:130)
	at graphql.schema.SchemaUtil.collectTypes(SchemaUtil.java:56)
	at graphql.schema.SchemaUtil.allTypes(SchemaUtil.java:153)
	at graphql.schema.GraphQLSchema.<init>(GraphQLSchema.java:42)
	at graphql.schema.GraphQLSchema$Builder.build(GraphQLSchema.java:130)
	at graphql.schema.GraphQLSchema$Builder.build(GraphQLSchema.java:125)
	at org.crygier.graphql.GraphQLSchemaBuilder.getGraphQLSchema(GraphQLSchemaBuilder.java:37)
	at org.crygier.graphql.GraphQLExecutor.createGraphQL(GraphQLExecutor.java:27)
...

Caused by repeated invocations of this function for org.crygier.graphql.model.starwars.Episode:
https://github.com/jcrygier/graphql-jpa/blob/master/src/main/java/org/crygier/graphql/GraphQLSchemaBuilder.java#L218

Deploy to Maven Central

Usage would be infinitely easier if the artifact was uploaded to Maven Central. Any chance of this happening?

@GraphQLIgnore does not always ignore unsupported types

An exception is thrown if an entity in the JPA model contains an unsupported data type, eg a byteArray:

java.lang.UnsupportedOperationException: Attribute could not be mapped to GraphQL: field 'data' of entity class 'org.crygier.graphql.model.starwars.Droid'

It is possible to suppress this exception by adding @GraphQLIgnore to the offending field. However this does not work if the entity is referenced by another entity in the model.

Issue can be duplicated by adding

@GraphQLIgnore byte[] data;

to the Droid model; all the test cases fail when trying to build the schema.

Exception on Connection query when no content field is queried

Given the following query:

{
    HumanConnection {
        totalElements
    }
}

My expectation would be that I'm getting the number of elements for "Human" but actually an exception is thrown because the query doesn't contain the "content" field.
The exception is:

java.util.NoSuchElementException: No value present
	at java.util.Optional.get(Optional.java:135) ~[na:1.8.0_121]
	at org.crygier.graphql.ExtendedJpaDataFetcher.get(ExtendedJpaDataFetcher.java:45) ~[graphql-jpa-0.2.jar:na]
	at graphql.execution.ExecutionStrategy.resolveField(ExecutionStrategy.java:40) ~[graphql-java-2.2.0.jar:na]
	at graphql.execution.SimpleExecutionStrategy.execute(SimpleExecutionStrategy.java:18) [graphql-java-2.2.0.jar:na]
	at graphql.execution.Execution.executeOperation(Execution.java:60) [graphql-java-2.2.0.jar:na]
	at graphql.execution.Execution.execute(Execution.java:33) [graphql-java-2.2.0.jar:na]
	at graphql.GraphQL.execute(GraphQL.java:78) [graphql-java-2.2.0.jar:na]
	at graphql.GraphQL.execute(GraphQL.java:55) [graphql-java-2.2.0.jar:na]
	at graphql.GraphQL.execute(GraphQL.java:47) [graphql-java-2.2.0.jar:na]
	at graphql.GraphQL.execute(GraphQL.java:43) [graphql-java-2.2.0.jar:na]
...

The actual reason is that get is invoked on an Optional instance that has no value without a check.
In my opinion there are two possible solutions:

  1. Allow queries without a "content" field.
  2. Implement a more suitable error handling that tells the developer what's wrong and document that such a query isn't possible. As far as I know it's not possible to actually express such limitations in the GraphQL schema itself so that a client (like GraphiQL) would immediately show this error.

I'm not sure if 1) is technically possible.

I would be happy to to work on this and provide a pull request if you can give some guidance on what the desired behavior is.

"Attribute could not be mapped to GraphQL" with @Enumerated Enum

As seen in: https://github.com/timtebeek/graphql-jpa-enum
When using the following:

public enum Genre {
    DRAMA, HORROR, MYSTERY, THRILLER
}
@Entity
public class Book {
    @Id
    Long id;

    String title;

    @ManyToOne
    Author author;

    @Enumerated(EnumType.STRING)
    Genre genre;
}

And loading my application context in a test, I get the following stacktrace:

...
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.crygier.graphql.GraphQLExecutor]: Factory method 'graphQLExecutor' threw exception; nested exception is java.lang.UnsupportedOperationException: Attribute could not be mapped to GraphQL: org.hibernate.jpa.internal.metamodel.SingularAttributeImpl@54ae1240
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    ... 42 common frames omitted
Caused by: java.lang.UnsupportedOperationException: Attribute could not be mapped to GraphQL: org.hibernate.jpa.internal.metamodel.SingularAttributeImpl@54ae1240
    at org.crygier.graphql.GraphQLSchemaBuilder.getAttributeType(GraphQLSchemaBuilder.java:151) ~[classes/:na]
    at org.crygier.graphql.GraphQLSchemaBuilder.getObjectField(GraphQLSchemaBuilder.java:96) ~[classes/:na]
...

Trace debugging reveals that the expection is raised for Book.genre. How can the model generation be updated to allow for such a mapped enum field?

Exception about unsupported attributes could be more informative

Currently in GraphQLSchemaBuilder, lines 135 and 157 throw an UnsupportedOperationException for attribute types that aren't supported.

When the error is printed to the log, it only shows something like Caused by: java.lang.UnsupportedOperationException: Attribute could not be mapped to GraphQL: org.hibernate.ejb.metamodel.SingularAttributeImpl@3fb97fd.

This would be more useful if instead of org.hibernate.ejb.metamodel.SingularAttributeImpl@3fb97fd, it printed the model class and attribute that is not supported.

Support for automatic mutations

It would be awesome to have auto generated mutations based on JPA entities, just like queries. Is this feature on the roadmap?

perform mutation operation

how to perform mutation operation ...i want to insert record so it is possible and how ?
so plz.replay asap

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.