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

Move gui option into constructor and use playground #1297

Merged
merged 9 commits into from
Jul 11, 2018
Merged

Conversation

zionts
Copy link
Contributor

@zionts zionts commented Jul 4, 2018

Although I know we want to remain less tied to the GraphQL Playground
GUI options, we definitely want to support a wider variety of options to
be passed in. This adds support for specifying partial options either
statically or dynamically for the gui, which can be extended to allow
for a wider array of guis than only GraphQL playground.

TODO:

  • Update CHANGELOG.md with your change (include reference to issue & this PR)
  • Make sure all of the significant new logic is covered by tests
  • Rebase your changes on master so that they can be merged easily
  • Make sure all tests and linter rules pass

@zionts zionts changed the title Add a wider diversity of gui options [WIP] Add a wider diversity of gui options Jul 4, 2018
Although I know we want to remain less tied to the GraphQL Playground
GUI options, we definitely want to support a wider variety of options to
be passed in. This adds support for specifying partial options either
statically or dynamically for the gui, which can be extended to allow
for a wider array of guis than only GraphQL playground.
@zionts zionts changed the title [WIP] Add a wider diversity of gui options Add a wider diversity of gui options Jul 4, 2018
Copy link
Contributor

@evans evans left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zionts Thank you so much for the PR! A couple of requests to bring the settings more in line with the other patterns we uses in the ApolloServer constructor.

Love the is Partial, definitely learned something with this PR.

partialGuiOverrides = gui(req);
}

console.log('partial enabled', partialGuiOverrides.enabled);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove the console.log's before we merge

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops! Thanks for catching.

@@ -29,9 +30,27 @@ export interface ServerRegistration {
bodyParserConfig?: OptionsJson | boolean;
onHealthCheck?: (req: express.Request) => Promise<any>;
disableHealthCheck?: boolean;
gui?: boolean;
gui?: ((req: express.Request) => Partial<GuiOptions>) | Partial<GuiOptions>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's important to retain the option to use or return a boolean for the gui option. Also, we'll have to include this logic across all of the integrations that use a gui. If you think of another way to encapsulate this logic, I'm happy to go for it.

}

export interface GuiOptions {
enabled: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need to include an enabled boolean, provided that we can use a boolean instead of a configuration. subscriptions are a good example of how this configuration is done in the engine constructor.

}

export interface GuiOptions {
enabled: boolean;
playgroundSettings: Partial<ISettings>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stubailo was interested in a defaultQuery option. This should probably include a query, variables, operationName, and headers. The playground settings could take precedence with a console.warn

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added in the GraphQL Tabs options, which support this by default. If we add more options in the future, we may want to have a graphqlPlayground field which specifies all of the settings we allow and do a check that exactly one gui option is configured, but I really don't feel the need to over-abstract something that only has one allowable GUI at the moment.

@evans
Copy link
Contributor

evans commented Jul 5, 2018

It's also important to note that these changes should be added to the Playground documentation https://www.apollographql.com/docs/apollo-server/v2/features/playground.html. Probably in a separate PR

@zionts
Copy link
Contributor Author

zionts commented Jul 6, 2018

@evans I'm happy to add to the docs as well, but I'm waiting on a merge since I don't want to have to re-write them 😝

@linonetwo
Copy link

linonetwo commented Jul 7, 2018

Does GUI have a defaultApiUrl option?

For example, https://scaneos.io/gqapi/graphql When opened, it uses https://scaneos.io/graphql as default API URL, while it's wrong.

GraphQL Engine have this option:

engine.listen(
  {
    port: 3002,
    expressApp: app,
    graphqlPaths: [process.env.NODE_ENV === 'production' ? '/gqapi/graphql' : '/graphql'],
  })

? {}
: nonDynamicGui;

// Disallow endpoints in pre-fill settings
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we actually need to do this. I think this was my misunderstanding what "endpoint" is

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're assertion is correct. The endpoint is where that tab is pointed at

Copy link
Contributor

@evans evans left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're almost there! I'm okay with the fact that we are only supporting apollo-server/-express for now. It will make future contributions much easier if the gui configuration functions are placed apollo-server-core.

Thank you again for the PR!

? {}
: nonDynamicGui;

// Disallow endpoints in pre-fill settings
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're assertion is correct. The endpoint is where that tab is pointed at

app.use(path, (req, res, next) => {
if (guiEnabled && req.method === 'GET') {
// We cannot do an existence check on `gui` since it may be a boolean
const nonDynamicGui: NonDynamicGuiConfig =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we put this configuration in a helper function and place the setting of guiOptions after the cors options above?

It will put the configuration code together and reduce the complexity of this middleware


// This is necessary to make TypeScript happy, since it cannot smart-cast types via conditional logic
function isBoolean(gui: NonDynamicGuiConfig): gui is boolean {
return typeof gui === typeof true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be possible with an if statement like so:

if(typeof gui === 'boolean') {
  ...
}

Copy link
Contributor Author

@zionts zionts Jul 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay! Yay for TS compile-time type-casting not being absolute trash 🎉 I preferred doing it the ... typeof true way to leave out the hard-coded string. I assume that's fine, but you'd prefer this logic not to be made a function if it doesn't need to be.

@@ -160,6 +224,8 @@ export class ApolloServer extends ApolloServerBase {
endpoint: path,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to make this endpoint configurable? or should we be using tabs like:

{
  tabs: [{ endpoint: '/graphql' }]
}

Though it doesn't you can set a subscription endpoint for a tab.

@ashtonsix
Copy link

ashtonsix commented Jul 10, 2018

@zionts Hey, it looks like this supports Tabs & ISettings but not GraphQLConfigData. GraphQLConfigData has this really awesome feature which lets you group tabs by endpoint & environment; additionally, new tabs within a group use the correct headers and endpoint automatically. It's incredibly useful, but not possible to use in apollo-server@rc.6 because gui passthrough was turned off - if you could add support for grouping tabs to your pull request it'd make me really super happy! 🙏 😄

This is what it looks like:

screenshot showing tabs grouped by endpoint & environment in a sidebar on the right-hand-side

The number next to each group indicates how many tabs are open. I'm not sure how, but the playground remembers which tabs I have open between server restarts. I'd love to configure default tabs for new team members & to add these "remembered" tabs with a partial (& a restore defaults button!) but that might be out of scope.

Here's how I got this working with apollo-server@rc.5:

.graphqlconfig

{
  "projects": {
    "salamander-apollo": {
      "schemaPath": "schema/schema.gql",
      "extensions": {
        "endpoints": {
          "dev": "http://localhost:6100/graphql",
          "prod": "https://salamander.ai/graphql"
        }
      }
    },
    "salamander-prisma": {
      "schemaPath": "../salamander-prisma/generated.graphql",
      "extensions": {
        "endpoints": {
          "dev": "http://localhost:4466/salamander",
          "prod": {
            "url": "${env:PRISMA_ENDPOINT}",
            "headers": {
              "Authorization": "Bearer ${env:PRISMA_TOKEN}"
            }
          }
        }
      }
    }
  }
}

getGraphQLConfig.js

const treeMap = (v, f) => {
  v = f(v)
  if (typeof v === 'object' && v) {
    Object.keys(v).forEach(k => (v[k] = treeMap(v[k], f)))
  }
  return v
}

const getGraphQLConfig = () => {
  const graphqlConfigPath = path.resolve(__dirname, '../../.graphqlconfig')
  const graphqlConfig = JSON.parse(fs.readFileSync(graphqlConfigPath, 'utf8'))
  treeMap(graphqlConfig, v => {
    if (typeof v !== 'string') return v
    return v.replace(/\${env:([^}]+)}/g, (m, k) => process.env[k])
  })

  return graphqlConfig
}

index.js

server.applyMiddleware({app, gui: {config: getGraphQLConfig()}})

@martijnwalraven
Copy link
Contributor

Thinking about this more, I think a cleaner solution is to rename gui to playground and nest the options directly under there. At this point, playgroundOptions is just another level of indirection, but we're not really abstracting over different GUIs.

export function createPlaygroundOptions(
gui: GuiConfig = {},
): PlaygroundRenderPageOptions | undefined {
const isDev = process.env.NODE_ENV === 'production';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!== ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes! Good catch. Thank you

@evans evans changed the title Add a wider diversity of gui options Move gui option into constructor and use playground Jul 11, 2018
@evans evans merged commit 11b8671 into version-2 Jul 11, 2018
@ashtonsix
Copy link

@evans I only have my phone until Monday, so it's a little hard to tell but this supports playground.config, right? Does this mean we can close #1290?

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 23, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants