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

Refactor/transform queries features #745

Closed
FluorescentHallucinogen opened this issue Apr 27, 2019 · 21 comments
Closed

Refactor/transform queries features #745

FluorescentHallucinogen opened this issue Apr 27, 2019 · 21 comments
Milestone

Comments

@FluorescentHallucinogen
Copy link
Contributor

It would be nice to have the following query refactoring features:

  • extract selected query fields to fragment
  • extract selected query fields to variable
@imolorhe
Copy link
Collaborator

Hey,

This would need more details. What is the use case? How would you propose the feature works?

@FluorescentHallucinogen
Copy link
Contributor Author

FluorescentHallucinogen commented Apr 28, 2019

What is the use case?

I'm working on a very complex API that has large number of types and large number of fields in each type. (BTW, that's why I opened #690.) The API is auto-generated. Currently I'm working on refactoring queries in my app, and I do the same actions over and over again by hand.

How would you propose the feature works?

  1. The "Extract selected fields to fragment" works as follows:

Suppose there is the following query:

query getUsers {
  users {
    id
    firstName
    lastName
    username
    phone
    email
    github
    twitter
  }
}

The user selects the phone, email, github and twitter fields by mouse. And a tooltip with "Extract selected fields to fragment" item appears next to the selected fields. (Alternative option: the tooltip with "Extract selected fields to fragment" item is shown after right mouse button click at selected fields.) After clicking on the item, the query will be transformed into:

query getUsers {
  users {
    id
    firstName
    lastName
    username
    ...fragment1
  }
}

fragment fragment1 on User {
  phone
  email
  github
  twitter
}
  1. The "Extract input fields to variable" works as follows:

Suppose there is the following query:

query getPosts {
  posts(
    where: {
      OR: [
        {
          AND: [
            { title_in: ["My biggest Adventure", "My latest Hobbies"] }
            { published: true }
          ]
        }
        { id: "cixnen24p33lo0143bexvr52n" }
      ]
    }
  ) {
    id
    title
    published
  }
}

The user puts the mouse cursor on the input argument where. And a tooltip with "Extract input fields to variable" item appears next to the input field. (Alternative option: the tooltip with "Extract input fields to variable" item is shown after right mouse button click at the input argument.) After clicking on the item, the query will be transformed into:

Query:

query getPosts($where: PostWhereInput) {
  posts(where: $where) {
    id
    title
  }
}

Variables:

{
  "where": {
    "OR": [{
        "AND": [{
          "title_in": ["My biggest Adventure", "My latest Hobbies"]
        }]
      },
      {
        "id": "cixnen24p33lo0143bexvr52n"
      }
    ]
  }
}

@FluorescentHallucinogen
Copy link
Contributor Author

Why I extract fields to fragments?

In my real case, the queries are a bit more complicated, and the fragments are reusable:

query getUsers {
  admins: users(role: admin) {
    id
    firstName
    lastName
    username
    phone
    email
    github
    twitter
  }
  accountants: users(role: accountant) {
    id
    firstName
    lastName
    username
    phone
    email
    github
    twitter
  }
}
query getUsers {
  admins: users(role: admin) {
    ...userFields
  }
  accountants: users(role: accountant) {
    ...userFields
  }
}

fragment userFields on User {
  id
  firstName
  lastName
  username
  phone
  email
  github
  twitter
}

Why I extract input fields to variables?

Because I need to not just change the values in the input, but dynamically change the shape of the query input using nested logical combinators OR, AND and NOT (generate the input using JS depending on what the user chooses in the UI for search, filtering, ordering/sorting and pagination).

@stale
Copy link

stale bot commented Jun 27, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the wontfix label Jun 27, 2019
@FluorescentHallucinogen
Copy link
Contributor Author

@imolorhe Is it really wontfix?

@stale stale bot removed the wontfix label Jun 27, 2019
@imolorhe
Copy link
Collaborator

imolorhe commented Jul 3, 2019

No it's not 😄

@FluorescentHallucinogen
Copy link
Contributor Author

Also it's sometimes useful to do the opposite:

  • merge all fragments into one query/subscription/mutation
  • inline all variable values into query/subscription/mutation

The first (as well as remove duplicated fields feature that is also very useful) is already implemented in graphql-prettier package by @Emetrop.

@stale
Copy link

stale bot commented Sep 1, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the wontfix label Sep 1, 2019
@acao
Copy link

acao commented Sep 3, 2019

I think we can implement this in the LSP so it works in monaco/vscode/etc, but Altair will need to migrate from codemirror-graphql to the forthcoming monaco mode to use that.

@stale stale bot removed the wontfix label Sep 3, 2019
@imolorhe
Copy link
Collaborator

imolorhe commented Sep 5, 2019

That would be a major migration but would be worth looking into once the monaco mode is completed.

@acao
Copy link

acao commented Sep 5, 2019

Yeah its unfortunate we have to make such a jump, but after I showed the preview some of the main contributors to the original codemirror mode see how this would reduce tons of redundant code, and allow us to have closer parity with the official LSP standard, potentially being able to roll out features across the ecosystem (including vscode, atom, etc) with a fraction of the code

Here's a quick preview, only query pane working now with completions, diagnostics, hovers. We will add declarations very soon and eventually symbol support:
https://graphiql-monaco-mode-example.netlify.com/

As you can see, its set up for my development of the variables editor, so if you just add the variable you'll see all the query editor features.

Here's the branch for it, where i add the monaco-graphql package and a monaco test under graphiql-examples that you see in that netlify example

@imolorhe
Copy link
Collaborator

imolorhe commented Sep 5, 2019

Just went through the monaco-graphql mode and the example. Looking good so far. If I understand correctly, the LSP standard you are referring to is a standardised interface accepted by most IDEs and development environments, and is different from the interface used by codemirror, hence implementing the LSP interface has a better impact in terms of possible integration with more environments?

@imolorhe
Copy link
Collaborator

imolorhe commented Sep 5, 2019

Also you mentioned that declarations and symbol support are missing. What are you referring to by those?

@imolorhe
Copy link
Collaborator

imolorhe commented Sep 5, 2019

And after a bit of googling around, I found what I was looking for 🙂https://microsoft.github.io/language-server-protocol/

Language Server Protocol documentation and specification page.

@acao
Copy link

acao commented Sep 5, 2019

yup thats it! my bad, running errands all day, trying to make sure i can overcome this illness

our entry for the official LSP site needs updated, but thats a great start to understanding it

the monaco team says monaco-graphql is looking pretty good, just need to support webworkers and declarations, variables and it should be mostly ready. we will want to sprinkle in some more features for parity with the codemirror mode. so far, I find many of these features to be much easier to implement in monaco, or already present with a very simple implementation.

my goal is to instantiate the full gls-interface/GraphQLLanguageService class in the browser with the monaco mode, similar to the language servers. We will be forking or possibly taking over graphql-config to acheive this universality, but everything in monaco is.file URI driven which will make that easier.

Then, by insantiating that class everywhere, we can add to it a plugin capability that can work universally across vscode server/browser/vscode client/atom. Typescript LS's plugin system and the json language service (pluggable schema loading), monaco-typescript and monaco-json provide nice prior art for this planned architecture

@acao
Copy link

acao commented Sep 7, 2019

https:/cypress-io/cypress-example-recipes/tree/master/examples/blogs__a11y
bingo!

GitHub
Various recipes for testing common scenarios with Cypress - cypress-io/cypress-example-recipes

@stale
Copy link

stale bot commented Nov 6, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the wontfix label Nov 6, 2019
@stale stale bot closed this as completed Nov 13, 2019
@FluorescentHallucinogen
Copy link
Contributor Author

Please reopen this issue.

@imolorhe
Copy link
Collaborator

This will be blocked for now until the implementation on the LSP level, unless there's time available to implement it on Altair itself.

@imolorhe imolorhe reopened this Dec 17, 2019
@stale stale bot removed the wontfix label Dec 17, 2019
@acao
Copy link

acao commented Dec 17, 2019

ah! i understand this a lot better now that i’ve reread it @FluorescentHallucinogen. very useful. can see how we could accomplish this with monaco in a vscode style. If you all want to make it work with codemirror first here in altair be my guest! for us to implement this in the LSP (probably in the new monaco mode) it might take a little bit for us to get there

@imolorhe
Copy link
Collaborator

imolorhe commented Dec 20, 2019

I'm going to attempt to implement this feature. However the implementation I would be doing would attempt to refactor the entire query, without selective refactoring.

What that means is this:

  • if there are multiple queries referencing fields from the same type, I would extract those fields into a fragment.
  • if there is a query with complex arguments (not primitives), I would extract those into a variable.

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

No branches or pull requests

3 participants