text: import { Utils } from './utils';
  
  export const convertToGraphQL = (fields, indentation) => {
    return fields.length > 0 ? fields.map(f => fieldTemplate(f, indentation)).join('\n') : '';
  };
  
  export const arrayToGraphQL = fields => fields.map(f => `${f.name}: ${f.type}`).join(', ');
  
  /*
  
  For backwards-compatibility reasons, args can either be a string or an array of objects
  
  */
  export const getArguments = args => {
    if (Array.isArray(args) && args.length > 0) {
      return `(${arrayToGraphQL(args)})`;
    } else if (typeof args === 'string') {
      return `(${args})`;
    } else {
      return '';
    }
  };
  
  /* ------------------------------------- Generic Field Template ------------------------------------- */
  
  // export const fieldTemplate = ({ name, type, args, directive, description, required }, indentation = '') =>
  // `${description ?  `${indentation}# ${description}\n` : ''}${indentation}${name}${getArguments(args)}: ${type}${required ? '!' : ''} ${directive ? directive : ''}`;
  
  // version that does not make any fields required
  export const fieldTemplate = ({ name, type, args, directive, description, required }, indentation = '') =>
  `${description ?  `${indentation}# ${description}\n` : ''}${indentation}${name}${getArguments(args)}: ${type} ${directive ? directive : ''}`;
  
  /* ------------------------------------- Main Type ------------------------------------- */
  
  /*
  
  The main type
  
  type Movie{
    _id: String
    title: String
    description: String
    createdAt: Date
  }
  
  */
  export const mainTypeTemplate = ({ typeName, description, interfaces, fields }) =>
  `# ${description}
  type ${typeName} ${interfaces.length ? `implements ${interfaces.join(', ')} ` : ''}{
  ${convertToGraphQL(fields, '  ')}
  }
  `;
  
  /* ------------------------------------- Selector Types ------------------------------------- */
  
  /*
  
  The selector type is used to query for one or more documents
  
  type MovieSelectorInput {
    AND: [MovieSelectorInput]
    OR: [MovieSelectorInput]
    id: String
    id_not: String
    id_in: [String!]
    id_not_in: [String!]
    ...
    name: String
    name_not: String
    name_in: [String!]
    name_not_in: [String!]
    ...
  }
  
  see https://www.opencrud.org/#sec-Data-types
  
  */
  export const selectorInputTemplate = ({ typeName, fields }) =>
  `input ${typeName}SelectorInput {
    AND: [${typeName}SelectorInput]
    OR: [${typeName}SelectorInput]
  ${convertToGraphQL(fields, '  ')}
  }`;
  
  /*
  
  The unique selector type is used to query for exactly one document
  
  type MovieSelectorUniqueInput {
    _id: String
    slug: String
  }
  
  */
  export const selectorUniqueInputTemplate = ({ typeName, fields }) =>
  `input ${typeName}SelectorUniqueInput {
    _id: String
    documentId: String # OpenCRUD backwards compatibility
    slug: String
  ${convertToGraphQL(fields, '  ')}
  }`;
  
  /*
  
  The orderBy type defines which fields a query can be ordered by
  
  enum MovieOrderByInput {
    title
    createdAt
  }
  
  */
  export const orderByInputTemplate = ({ typeName, fields }) =>
  `enum ${typeName}OrderByInput {
    foobar
    ${fields.join('\n  ')}
  }`;
  
  /* ------------------------------------- Query Types ------------------------------------- */
  
  /*
  
  A query for a single document
  
  movie(input: SingleMovieInput) : SingleMovieOutput
  
  */
  export const singleQueryTemplate = ({ typeName }) => `${Utils.camelCaseify(typeName)}(input: Single${typeName}Input): Single${typeName}Output`;
  
  
  /*
  
  A query for multiple documents
  
  movies(input: MultiMovieInput) : MultiMovieOutput
  
  */
  export const multiQueryTemplate = ({ typeName }) => `${Utils.camelCaseify(Utils.pluralize(typeName))}(input: Multi${typeName}Input): Multi${typeName}Output`;
  
  /* ------------------------------------- Query Input Types ------------------------------------- */
  
  /*
  
  The argument type when querying for a single document
  
  type SingleMovieInput {
    selector {
      documentId: String
      # or `_id: String`
      # or `slug: String`
    }
    enableCache: Boolean
  }
  
  */
  export const singleInputTemplate = ({ typeName }) =>
  `input Single${typeName}Input {
    selector: ${typeName}SelectorUniqueInput
    # Whether to enable caching for this query
    enableCache: Boolean
    # Return null instead of throwing MissingDocumentError
    allowNull: Boolean
  }`;
  
  
  export const selectorInputTermsTemplate = ({ typeName, fields }) => `
  input ${typeName}TermsInput {
    # A JSON object that contains the query terms in MongoDB format (e.g. { isFeatured: true })
    selector: JSON
    # A JSON object that contains the sort options in MongoDB format (e.g. { createdAt: -1 })
    sort: JSON
    # A limit for the query
    limit: Int
    # How much to offset the results by
    offset: Int
    # Which view to use
    view: String
    # Not actively used (regex search) 
    query: String
    # Not tested
    itemsPerPage: Int
    # Not tested
    orderBy: JSON
  }`;
  
  /*
  
  The argument type when querying for multiple documents
  
  type MultiMovieInput {
    terms: JSON
    offset: Int
    limit: Int
    enableCache: Boolean
  }
  
  */
  export const multiInputTemplate = ({ typeName }) =>
  `input Multi${typeName}Input {
    # A JSON object that contains the query terms used to fetch data
    terms: ${typeName}TermsInput,
    # terms: JSON
    # Whether to enable caching for this query
    enableCache: Boolean
    # Whether to calculate totalCount for this query
    enableTotal: Boolean
    # NOT USED!
    where: ${typeName}SelectorInput
    # NOT USED!
    orderBy: ${typeName}OrderByInput
    # NOT USED!
    skip: Int
    # NOT USED!
    after: String
    # NOT USED!
    before: String
    # NOT USED!
    first: Int
    # NOT USED!
    last: Int
    # NOT USED!
    offset: Int,
    # NOT USED!
    limit: Int,
  }`;
  
  /* ------------------------------------- Query Output Types ------------------------------------- */
  
  /*
  
  The type for the return value when querying for a single document
  
  type SingleMovieOuput{
    result: Movie
  }
  
  */
  export const singleOutputTemplate = ({ typeName }) =>
  `type Single${typeName}Output{
    result: ${typeName}
  }`;
  
  /*
  
  The type for the return value when querying for multiple documents
  
  type MultiMovieOuput{
    results: [Movie]
    totalCount: Int
  }
  
  */
  export const multiOutputTemplate = ({ typeName }) =>
  `type Multi${typeName}Output{
    results: [${typeName}]
    totalCount: Int
  }`;
  
  /* ------------------------------------- Query Queries ------------------------------------- */
  
  
  /*
  
  Single query used on the client
  
  query singleMovieQuery($input: SingleMovieInput) {
    movie(input: $input) {
      result {
        _id
        name
        __typename
      }
      __typename
    }
  }
  
  */
  export const singleClientTemplate = ({ typeName, fragmentName, extraQueries }) =>
  `query single${typeName}Query($input: Single${typeName}Input) {
    ${Utils.camelCaseify(typeName)}(input: $input) {
      result {
        ...${fragmentName}
      }
      __typename
    }
    ${extraQueries ? extraQueries : ''}
  }`;
  
  
  /*
  
  Multi query used on the client
  
  mutation multiMovieQuery($input: MultiMovieInput) {
    movies(input: $input) {
      results {
        _id
        name
        __typename
      }
      totalCount
      __typename
    }
  }
  
  */
  export const multiClientTemplate = ({ typeName, fragmentName, extraQueries }) =>
  `query multi${typeName}Query($input: Multi${typeName}Input) {
    ${Utils.camelCaseify(Utils.pluralize(typeName))}(input: $input) {
      results {
        ...${fragmentName}
      }
      totalCount
      __typename
    }
    ${extraQueries ? extraQueries : ''}
  }`;
  
  /* ------------------------------------- Mutation Types ------------------------------------- */
  
  /*
  
  Mutation for creating a new document
  
  createMovie(input: CreateMovieInput) : MovieOutput
  
  */
  export const createMutationTemplate = ({ typeName }) =>
  `create${typeName}(data: Create${typeName}DataInput!) : ${typeName}Output`;
  
  /*
  
  Mutation for updating an existing document
  
  updateMovie(input: UpdateMovieInput) : MovieOutput
  
  */
  export const updateMutationTemplate = ({ typeName }) =>
  `update${typeName}(selector: ${typeName}SelectorUniqueInput!, data: Update${typeName}DataInput! ) : ${typeName}Output`;
  
  /*
  
  Mutation for updating an existing document; or creating it if it doesn't exist yet
  
  upsertMovie(input: UpsertMovieInput) : MovieOutput
  
  */
  export const upsertMutationTemplate = ({ typeName }) =>
  `upsert${typeName}(selector: ${typeName}SelectorUniqueInput!, data: Update${typeName}DataInput! ) : ${typeName}Output`;
  
  /*
  
  Mutation for deleting an existing document
  
  deleteMovie(input: DeleteMovieInput) : MovieOutput
  
  */
  export const deleteMutationTemplate = ({ typeName }) =>
  `delete${typeName}(selector: ${typeName}SelectorUniqueInput!) : ${typeName}Output`;
  
  /* ------------------------------------- Mutation Input Types ------------------------------------- */
  
  // note: not currently used
  
  /*
  
  Type for create mutation input argument
  
  type CreateMovieInput {
    data: CreateMovieDataInput!
  }
  
  */
  export const createInputTemplate = ({ typeName }) =>
  `input Create${typeName}Input{
    data: Create${typeName}DataInput!
  }`;
  
  /*
  
  Type for update mutation input argument
  
  type UpdateMovieInput {
    selector: MovieSelectorUniqueInput!
    data: UpdateMovieDataInput!
  }
  
  */
  export const updateInputTemplate = ({ typeName }) =>
  `input Update${typeName}Input{
    selector: ${typeName}SelectorUniqueInput!
    data: Update${typeName}DataInput!
  }`;
  
  /*
  
  Type for upsert mutation input argument
  
  Note: upsertInputTemplate uses same data type as updateInputTemplate
  
  type UpsertMovieInput {
    selector: MovieSelectorUniqueInput!
    data: UpdateMovieDataInput!
  }
  
  */
  export const upsertInputTemplate = ({ typeName }) =>
  `input Upsert${typeName}Input{
    selector: ${typeName}SelectorUniqueInput!
    data: Update${typeName}DataInput!
  }`;
  
  /*
  
  Type for delete mutation input argument
  
  type DeleteMovieInput {
    selector: MovieSelectorUniqueInput!
  }
  
  */
  export const deleteInputTemplate = ({ typeName }) =>
  `input Delete${typeName}Input{
    selector: ${typeName}SelectorUniqueInput!
  }`;
  
  /*
  
  Type for the create mutation input argument's data property
  
  type CreateMovieDataInput {
    title: String
    description: String
  }
  
  */
  export const createDataInputTemplate = ({ typeName, fields }) =>
  `input Create${typeName}DataInput {
  ${convertToGraphQL(fields, '  ')}
  }`;
  
  /*
  
  Type for the update mutation input argument's data property
  
  type UpdateMovieDataInput {
    title: String
    description: String
  }
  
  */
  export const updateDataInputTemplate = ({ typeName, fields }) =>
  `input Update${typeName}DataInput {
  ${convertToGraphQL(fields, '  ')}
  }`;
  
  /* ------------------------------------- Mutation Output Type ------------------------------------- */
  
  /*
  
  Type for the return value of all mutations
  
  type MovieOutput {
    data: Movie
  }
  
  */
  export const mutationOutputTemplate = ({ typeName }) =>
  `type ${typeName}Output{
    data: ${typeName}
  }`;
  
  /* ------------------------------------- Mutation Queries ------------------------------------- */
  
  /*
  
  Create mutation query used on the client
  
  mutation createMovie($data: CreateMovieDataInput!) {
    createMovie(data: $data) {
      data {
        _id
        name
        __typename
      }
      __typename
    }
  }
  
  */
  export const createClientTemplate = ({ typeName, fragmentName }) =>
  `mutation create${typeName}($data: Create${typeName}DataInput!) {
    create${typeName}(data: $data) {
      data {
        ...${fragmentName}
      }
    }
  }`;
  
  /*
  
  Update mutation query used on the client
  
  mutation updateMovie($selector: MovieSelectorUniqueInput!, $data: UpdateMovieDataInput!) {
    updateMovie(selector: $selector, data: $data) {
      data {
        _id
        name
        __typename
      }
      __typename
    }
  }
  
  */
  export const updateClientTemplate = ({ typeName, fragmentName }) =>
  `mutation update${typeName}($selector: ${typeName}SelectorUniqueInput!, $data: Update${typeName}DataInput!) {
    update${typeName}(selector: $selector, data: $data) {
      data {
        ...${fragmentName}
      }
    }
  }`;
  
  /*
  
  Upsert mutation query used on the client
  
  mutation upsertMovie($selector: MovieSelectorUniqueInput!, $data: UpdateMovieDataInput!) {
    upsertMovie(selector: $selector, data: $data) {
      data {
        _id
        name
        __typename
      }
      __typename
    }
  }
  
  */
  export const upsertClientTemplate = ({ typeName, fragmentName }) =>
  `mutation upsert${typeName}($selector: ${typeName}SelectorUniqueInput!, $data: Update${typeName}DataInput!) {
    upsert${typeName}(selector: $selector, data: $data) {
      data {
        ...${fragmentName}
      }
    }
  }`;
  
  /*
  
  Delete mutation query used on the client
  
  mutation deleteMovie($selector: MovieSelectorUniqueInput!) {
    deleteMovie(selector: $selector) {
      data {
        _id
        name
        __typename
      }
      __typename
    }
  }
  
  */
  export const deleteClientTemplate = ({ typeName, fragmentName }) =>
  `mutation delete${typeName}($selector: ${typeName}SelectorUniqueInput!) {
    delete${typeName}(selector: $selector) {
      data {
        ...${fragmentName}
      }
    }
  }`;
  