Altair: Refactor/transform queries features

Created on 27 Apr 2019  路  21Comments  路  Source: imolorhe/altair

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

  • extract selected query fields to fragment
  • extract selected query fields to variable

All 21 comments

Hey,

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

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"
      }
    ]
  }
}

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).

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.

@imolorhe Is it really wontfix?

No it's not 馃槃

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.

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.

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.

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

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

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?

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

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.

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

https://github.com/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

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.

Please reopen this issue.

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

ah! i understand this a lot better now that i鈥檝e 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

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.
Was this page helpful?
0 / 5 - 0 ratings