kloeckner-i / ember-cli-mirage-graphql

A library for mocking GraphQL with Ember CLI Mirage
MIT License
24 stars 10 forks source link

Compatibility with Mirage’s ORM layer? #18

Open jgwhite opened 5 years ago

jgwhite commented 5 years ago

It seems like this library currently interfaces directly with Mirage’s DB layer, rather than the ORM layer. This causes some compatibility problems for projects that use the ORM layer, particularly in code paths such as filterByParent†.

Interfacing with the ORM layer would potentially simplify loading of associated records and handling of things like non-standard inverse field names.

Is there a reason not to interface with the ORM layer?

Would a PR that switches ember-cli-mirage-graphql over to the ORM layer be valuable?‡


filterByParent filters on someParentField.id on objects from the DB. When using the ORM layer, the relationship is stored in the DB as someParentFieldId.

‡ We’d probably have to take backwards compatibility into account for projects that don’t use the ORM layer yet.

jneurock commented 5 years ago

Thanks for opening this issue! There's no reason not to interface with Mirage's ORM layer. A PR to address this issue would certainly be valuable :)

jneurock commented 5 years ago

@jgwhite, if you want, you can create a test that fails and send it my way.

jgwhite commented 5 years ago

@jneurock thanks. The team is digging back into this in the next few weeks so hopefully that’ll force us to contribute some test cases and/or an implementation.

jneurock commented 4 years ago

@jgwhite, it seems like this might only work if you use Mirage models. Do you think that's accurate?

In my apps, I don't use any Mirage models, only factories. I don't want to go through the trouble of creating Mirage models since there's a lot of overlap with the GraphQL schema. As a result, I seem to only be able to work with Mirage's DB layer.

I would like to continue not using Mirage models so maybe a better approach here would be to examine the differences at the DB layer when using Mirage models and relationships vs. having no Mirage models at all.

That or the logic could be split for the two cases. One strategy for working with the ORM layer and a fallback for the DB layer, if needed.

Let me know what you think.

jgwhite commented 4 years ago

it seems like this might only work if you use Mirage models. Do you think that's accurate?

Yes, I think that’s the case.

In my apps, I don't use any Mirage models, only factories. I don't want to go through the trouble of creating Mirage models since there's a lot of overlap with the GraphQL schema. As a result, I seem to only be able to work with Mirage's DB layer.

That makes sense. I know Mirage is able to automatically infer models from Ember Data models. I wonder if a similar approach could be taken with the schema? Perhaps this indicates the need for a new public API in Mirage for registering models?

I would like to continue not using Mirage models so maybe a better approach here would be to examine the differences at the DB layer when using Mirage models and relationships vs. having no Mirage models at all.

Yeah, that sounds like a good strategy, assuming that the way Mirage stores the relationship data at the DB layer is stable.

That or the logic could be split for the two cases. One strategy for working with the ORM layer and a fallback for the DB layer, if needed.

If the above strategy works it would certainly be simpler to maintain.

jneurock commented 4 years ago

Thanks, @jgwhite.

Yeah, that sounds like a good strategy, assuming that the way Mirage stores the relationship data at the DB layer is stable.

I think this has been stable for a long time but I guess I'll find out...

That makes sense. I know Mirage is able to automatically infer models from Ember Data models. I wonder if a similar approach could be taken with the schema? Perhaps this indicates the need for a new public API in Mirage for registering models?

I think we could create Mirage models from the schema with some work but yeah, how do we make Mirage aware of them? Any thoughts @samselikoff? Is this already possible or would we need a public API?

samselikoff commented 4 years ago

Could definitely create models from a schema, and yes I think the ORM is still valuable + should be the way a GraphQL lib interacts with Mirage's data layer. The ORM is what lets you pass models into other models while doing server.create, updates inverse relationship foreign keys in the db, and many other things. I think our goal should be to auto-define Mirage's models + relationships from a graphql schema, in the same way we do with Ember Data. I haven't worked with graphql relationships yet so an example to start would be very helpful.

jneurock commented 4 years ago

Thanks, @samselikoff.

Quick example of a blog post type in a GraphQL schema:

type Post {
  id: ID!
  author: User # belongsTo
  body: String! # scalar
  comments: [Comment] # hasMany
  title: String! # scalar
}

When this gets parsed into JavaScript, we get all kinds of good info. For example, the parsed schema would tell us the comments field has a return type of GraphQLList with a property, ofType, of GraphQLObjectType which has the name "Comment". Plenty of info from which to create Mirage models.

jneurock commented 4 years ago

I can also make it part of my work on #34 to go ahead and create Mirage models from the schema but is there a way to make them known to Mirage programmatically? I'll try to find out when I get to it.

samselikoff commented 4 years ago

The programmatic answer would be to get the models/relationships from graphql into some data structure and then pass them into Mirage's config.

Here's what it looks like for a new server:

import { Model, hasMany } from 'miragejs'

new Server({
  models: {
    user: Model.extend({
      comments: hasMany()
    }),

    comment: Model.extend({
      user: belongsTo()
    })
  }
})

but you can "augment" existing mirage server's config by calling config(options) again:

let server = new Server();

// later...

server.config({
  models: {
    user: Model.extend({
      comments: hasMany()
    }),

    comment: Model.extend({
      user: belongsTo()
    })
  }
})

Basic goal would just be to form that models hash using data from graphql schema.

If it helps, we can also expose some method on server like server.registerModel('user', definition), something like this. Believe schema object already has this but if we can start just using config options to start that might be easiest.

jneurock commented 4 years ago

Thanks for the info! It looks like I can go ahead and try this out: [https://github.com/miragejs/miragejs/blob/master/lib/orm/schema.js#L58]().

samselikoff commented 4 years ago

Exactly, nice find 👍

jneurock commented 4 years ago

Well, it's been a long time but I started working on the plan proposed here a few weeks ago and wanted to leave a progress update.

  1. I started by creating a separate GraphQL auto resolver library like I mentioned. I got that mostly working but then realized that I didn't need it. More on that later...
  2. Working with Mirage's ORM layer seems to be working well so far.
  3. Sam created a Mirage GraphQL project so my plan is to retire this add-on in favor of the new library when it's ready. There was no Ember specific code in here anyway.

Some technical details...

The graphql-js library actually provides a lot of what I need, I just needed to learn more about the internals. This allows me to avoid creating a separate GraphQL auto resolve library like I planned.

Additionally, I'm able to pretty easily create Mirage models with proper relationships based on a GraphQL schema. The GraphQL schema, as tested so far, is a good stand-in for defining Mirage models explicitly and it could be that the conventional workflow is to avoid the redundancy of defining Mirage models when using GraphQL.

All but 4 of the test modules from this add-on are currently passing and 2 of the 4 are mutation tests which rely on user-defined resolvers vs. automatic resolution.

Remaining tasks:

Feel free to leave questions about the progress here. Thanks for the patience.

samselikoff commented 4 years ago

Awesome. Can't wait to see the work.

I mentioned to Rocky in Discord, I'm about to wrap up the REST Tutorial for Mirage, and it would be so great to eventually have a GraphQL version of it that we maintain alongside of it 👍

Will be more than happy to help with the docs/examples side once we get to that point.

@jgwhite , has anything changed on your end in terms of how y'all are using Mirage + GraphQL?

jgwhite commented 4 years ago

has anything changed on your end in terms of how y'all are using Mirage + GraphQL?

@samselikoff nothing’s changed on our end. Still excited to try out any new ideas in our apps. I know @chadian has been exploring some ideas with GraphQL and Mirage too.

jneurock commented 4 years ago

With our powers combined...

chadian commented 4 years ago

Hello! Yes, I have been toying with the graphql testing ideas the past few months. I had an initial spike in December that I picked up a few months ago and has since come along quite far. The idea I've been working on has a mirage component for "auto resolving" but also even more broadly provides a suite of tools for bootstrapping a graphql handler or resolver maps for testing (ie: getting automated sinon or logging added quickly).

The mirage piece received some updates yesterday (https://github.com/chadian/graphql-test-resolvers/pull/10) and there's a few other ideas I would like to add later if I have time. I'm currently working on documentation and hoping it'll be ready for people to try in a week or two.

jneurock commented 4 years ago

It's been a long time coming but almost there... https://github.com/miragejs/graphql/pull/1 for anyone that would like to review.