Giter Club home page Giter Club logo

graphql-query-generator's Introduction

GitHub last commit npm

GraphQL Query Generator

This library will generate randomized GraphQL queries from a given schema.

It can be used in a few ways:

  • Engineering: If you operate a GraphQL service, you might use this library to:
    • develop a static test suite of GraphQL queries
    • develop a battery of queries to test the effect of performance improvements
  • Scientific: Understand the characteristics of various GraphQL services

Usage

Install the library using:

npm i ibm-graphql-query-generator

Usage typically relies on the generateRandomQuery function, which can be imported like this:

const { generateRandomQuery } = require("ibm-graphql-query-generator")

Minimal working example

All arguments are exposed as variables. Providers can be passed to provide values for these variables. For example:

const { generateRandomQuery } = require("ibm-graphql-query-generator")

const { buildSchema, print } = require("graphql")

const schema = `
  type Query {
    getUser(name: String!): User
    getCompany(companyName: String!): Company
  }

  type User {
    firstName: String
    lastName: String
    employer: Company
    friends: [User]
  }

  type Company {
    name: String
    CEO: User
    employees: [User]
  }
`

const configuration = {
  depthProbability: 0.5,
  breadthProbability: 0.5,
  providerMap: {
    "*__*__name": () => {
      const nameList = ["Alfred", "Barbara", "Charles", "Dorothy"]

      return nameList[Math.floor(Math.random() * nameList.length)]
    },
    "*__*__companyName": () => {
      const companyNameList = [
        "All Systems Go",
        "Business Brothers",
        "Corporate Comglomerate Company",
        "Data Defenders"
      ]

      return companyNameList[
        Math.floor(Math.random() * companyNameList.length)
      ]
    }
  }
}

const { queryDocument, variableValues, seed } = generateRandomQuery(
  buildSchema(schema),
  configuration
)

console.log(print(queryDocument))
console.log(variableValues)

Example configurations

We provide sample query generators for the following APIs:

Generating random queries

This library exposes two functions for generating random GraphQL queries:

  • getRandomQuery(schema: GraphQLSchema, config: Configuration): Produces a random query from the given schema, and considering the passed configuration.
  • getRandomMutation(schema: GraphQLSchema, config: Configuration): Produces a random mutation from the given schema, and considering the passed configuration.

Configuration

Functions of this library accept a configuration object with the following properties:

  • depthProbability (type: number, default: 0.5): The probability (from 0 to 1) that, if existent, fields that themselves have subfields are selected at every level of the generated query. The number of so selected fields depends on the breadthProbability.
  • breadthProbability (type: number, default: 0.5): The probability (from 0 to 1) that a field (nested or flat) is selected at every level of the generated query.
  • maxDepth (type: number, default: 5): The maximum depths of the query / mutation to generate. This library ensures that leave nodes do not require children fields to be selected.
  • ignoreOptionalArguments (type: boolean, default: true): If set to true, non-mandatory arguments will not be included in the generated query / mutation.
  • argumentsToIgnore (type: string[], default: []): List of argument names that should be ignored in any case. If non-null arguments are configured to be ignored, an error will be thrown.
  • argumentsToConsider (type: string[], default: []): List of argument names that should be considered, even if the argument is optional and ignoreOptionalArguments is set to true.
  • providerMap (type: {[varNameQuery: string]: any | ProviderFunction }, default: {}): Map of values or functions to provide values for the variables present in the generated query / mutation. See details below.
  • providePlaceholders (type: boolean, default: false): If enabled, instead of defaulting to null, placeholder values of correct type are provided for variables. Specifically, the placeholders are 10 for type Int, 10.0 for type Float, true for type Boolean, and "PLACEHOLDER" for types String and ID, and custom scalars.
  • considerInterfaces (type: boolean, default: false): Create queries containing interfaces (by calling fields in the interfaces and/or creating fragments on objects implementing the interfaces)
  • considerUnions (type: boolean, default: false): Create queries containing unions (by creating fragments on objects of the unions)
  • seed (type: number, optional): Allows the generator to produce queries deterministically based on a random number generator seed. If no seed is provided, a random seed will be provided. The seed that is used to produce the query, whether user-provided or randomly generated, will be included in the output.
  • pickNestedQueryField (type: boolean, default: false): Guarantees that the generator will pick at least one nested field.

Example:

const cfg = {
  'depthProbability':        0.5,
  'breadthProbability':      0.5,
  'maxDepth':                5,
  'ignoreOptionalArguments': true,
  'argumentsToIgnore':       [],
  'argumentsToConsider':     [],
  'providerMap':             {'*__*__*': null},
  'considerInterfaces':      false,
  'considerUnions':          false,
  'seed':                    1,
  'pickNestedQueryField':    false
}

Provider map

Whenever a randomly generated query or mutation requires an argument, this library exposes that argument as a variable. The names of these variables reflect the type and field that the argument applies to, as well as the argument name like so:

<type>__<fieldName>__<argumentName>

Alternatively, you can match using:

<type>__<fieldName>

In this case, the provider function returns an object where multiple arguments are present.

The providerMap contains values or value producing functions for the variables in a query.

The keys of the providerMap are either the exact name of the variable or a wildcard where either the type, fieldName, and/or argumentName are replaced by a *. For example, the key *__*__limit matches all variables for arguments of name limit, no matter for what field the argument is used or in which type. If no providerMap is passed, a default map {'*__*__*': null} is used, which provides a null value to all variables (Note: this can be problematic if an argument defines a non-null value).

The values of the providerMap are either the concrete argument values, or a function that will be invoked to provide that value. A provider function will get passed a map of all already provided variable values, which allows to provide values based on previous ones. It will also get passed the argument type (as a GraphQLNamedType) as a second argument.

This library also exposes a function matchVarName(query: string, candidates: string[]): string that, from a given list of variable names and/or variable name queries, finds the one matching the given variable name or query.

Note that for variables with an enumeration type, this library automatically chooses one value at random.

Errors

Generating random queries or mutations may fail in some cases:

  • An error is thrown if a query hits the defined maxDepth, but there are only fields with children to choose from. Choosing such a field but then not choosing a sub-field for it (due to the maxDepth constraint) would result in an invalid query and thus causes this library to throw an error.
  • An error is thrown if there is no provider defined for a variable in the generated query.

Citing this library

If you use this library in a scientific publication, please cite:

  1. The library, as: IBM, graphql-query-generator, 2020. https://github.com/IBM/graphql-query-generator.
  2. The work in which we introduced it, as: Cha, Wittern, Baudart, Davis, Mandel, and Laredo. A Principled Approach to GraphQL Query Cost Analysis. ESEC/FSE 2020.

graphql-query-generator's People

Contributors

alan-cha avatar davisjam avatar erikwittern avatar mandel avatar martolini avatar stevemar avatar yahelnachum 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  avatar  avatar  avatar  avatar

graphql-query-generator's Issues

Configure required arguments with @listSize directive

This library, by default, exposes variables for non-null arguments. However, many APIs rely on cursor connections, where one of the two slicing arguments first or last is required to be present. This constraint cannot be expressed with existing GraphQL constructs. In consequence, API providers like GitHub implement custom validation rules that ensure that exactly one of these arguments is present in a query.

IBM uses an @listSize SDL directive, applicable to field definitions, to identify possibly multiple slicing arguments, and to express that exactly one of them is required in a query. The idea is to publish a specification (similar to the cursor connections specification) about the exact nature of this directive, so that it can be used by GraphQL API providers for validation and query cost analysis.

This issue proposes that this library considers @listSize directives, if present, and exposes one of the required slicing argument. The feature would not impact any schema not using the directive, so it is an additional way to configure the behaviour of this library.

GraphQL v16 support

The library has graphql@^14.0.2 as peer dependency. Are there any chance to get support for graphql v16?

Create placeholder variable values, even when no "provider" function is defined

As it stands, this library relies on provider functions / values to produce values for variables used in a generated query.

To make the library easier to use out of the box, the library could instead produce placeholder values, e.g.:

  • A small number like 10 or 10.0 for variables of type Int or Float
  • A string like "PLACEHOLDER" for variables of type String or ID
  • true for variables of type Boolean
  • Mock input objects whose leaf fields are filled with scalars as described above

Possibility to generate only one query or mutation

We have specified tests in separate files and with different settings. We don't want to generate all queries and mutations at once. It would be great if we could provide the full graphql schema but generate only one query or mutation.

RangeError encountered when recursive required argument is present in schema

Schema:

input Args {
  first: Int
  recursive: Args!
}

type Order {
  id: ID
  date: String
}

type Query {
  orders(args: Args!): [Order]
}

Error seen when running test.

    RangeError: Maximum call stack size exceeded
        at Array.map (<anonymous>)

      150 |         // Identify requiredArguments
      151 |         const requiredArguments = Object.entries(fields)
    > 152 |           .map(([_, value]) => value)
          |            ^
      153 |           .filter((type) => {
      154 |             return considerArgument(type, config)
      155 |           })

      at getDefaultArgValue (src/provide-variables.ts:152:12)
      at getDefaultArgValue (src/provide-variables.ts:132:12)
      at src/provide-variables.ts:159:33
          at Array.reduce (<anonymous>)
      at getDefaultArgValue (src/provide-variables.ts:158:34)
      at getDefaultArgValue (src/provide-variables.ts:132:12)
      at src/provide-variables.ts:159:33
          at Array.reduce (<anonymous>)
      at getDefaultArgValue (src/provide-variables.ts:158:34)
      at getDefaultArgValue (src/provide-variables.ts:132:12)
      at src/provide-variables.ts:159:33
          at Array.reduce (<anonymous>)
          
... (repeats at least 20 times)

      at getDefaultArgValue (src/provide-variables.ts:158:34)
      at getDefaultArgValue (src/provide-variables.ts:132:12)
      at src/provide-variables.ts:159:33

npm release?

When will you release on npm? you should also change the name given that graphql-query-generator is already taken

"Citing it"

The README has a section about citing this library. Can we update it to request citations to the associated ESEC/FSE'20 paper?

Edgecase where no flat field can be selected at the maximum depth

An error is thrown if a query hits the defined maxDepth, but there are only fields with children to choose from. Choosing such a field but then not choosing a sub-field for it (due to the maxDepth constraint) would result in an invalid query and thus causes this library to throw an error.

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.