// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#ifndef SCHEMALOADER_H
#define SCHEMALOADER_H
#include "GeneratorLoader.h"
#include "graphqlservice/GraphQLParse.h"
#include "graphqlservice/GraphQLService.h"
#include "graphqlservice/internal/Grammar.h"
#include
#include
#include
namespace graphql::generator {
// These are the set of built-in types in GraphQL.
enum class [[nodiscard]] BuiltinType {
Int,
Float,
String,
Boolean,
ID,
};
using BuiltinTypeMap = std::map;
// These are the C++ types we'll use for them.
using CppTypeMap = std::array;
// Keep track of the positions of each type declaration in the file.
using PositionMap = std::unordered_map;
// For all of the named types we track, we want to keep them in order in a vector but
// be able to lookup their offset quickly by name.
using TypeNameMap = std::unordered_map;
// Scalar types are opaque to the generator, it's up to the service implementation
// to handle parsing, validating, and serializing them. We just need to track which
// scalar type names have been declared so we recognize the references.
struct [[nodiscard]] ScalarType
{
std::string_view type;
std::string_view description;
std::string_view specifiedByURL {};
};
using ScalarTypeList = std::vector;
// Enum types map a type name to a collection of valid string values.
struct [[nodiscard]] EnumValueType
{
std::string_view value;
std::string_view cppValue;
std::string_view description;
std::optional deprecationReason;
std::optional position;
};
struct [[nodiscard]] EnumType
{
std::string_view type;
std::string_view cppType;
std::vector values;
std::string_view description;
};
using EnumTypeList = std::vector;
// Input types are complex types that have a set of named fields. Each field may be
// a scalar type (including lists or non-null wrappers) or another nested input type,
// but it cannot include output object types.
enum class [[nodiscard]] InputFieldType {
Builtin,
Scalar,
Enum,
Input,
};
struct [[nodiscard]] InputField
{
std::string_view type;
std::string_view name;
std::string_view cppName;
std::string_view defaultValueString;
response::Value defaultValue;
InputFieldType fieldType = InputFieldType::Builtin;
TypeModifierStack modifiers;
std::string_view description;
std::optional position;
};
using InputFieldList = std::vector;
struct [[nodiscard]] InputType
{
std::string_view type;
std::string_view cppType;
InputFieldList fields;
std::string_view description;
std::unordered_set dependencies {};
std::vector declarations {};
};
using InputTypeList = std::vector;
// Directives are defined with arguments and a list of valid locations.
struct [[nodiscard]] Directive
{
std::string_view name;
bool isRepeatable = false;
std::vector locations;
InputFieldList arguments;
std::string_view description;
};
using DirectiveList = std::vector;
// Union types map a type name to a set of potential concrete type names.
struct [[nodiscard]] UnionType
{
std::string_view type;
std::string_view cppType;
std::vector options;
std::string_view description;
};
using UnionTypeList = std::vector;
// Output types are scalar types or complex types that have a set of named fields. Each
// field may be a scalar type (including lists or non-null wrappers) or another nested
// output type, but it cannot include input object types. Each field can also take
// optional arguments which are all input types.
enum class [[nodiscard]] OutputFieldType {
Builtin,
Scalar,
Enum,
Union,
Interface,
Object,
};
constexpr std::string_view strGet = "get";
constexpr std::string_view strApply = "apply";
struct [[nodiscard]] OutputField
{
std::string_view type;
std::string_view name;
InputFieldList arguments;
OutputFieldType fieldType = OutputFieldType::Builtin;
TypeModifierStack modifiers;
std::string_view description;
std::optional deprecationReason;
std::optional position;
bool interfaceField = false;
bool inheritedField = false;
std::string_view accessor { strGet };
};
using OutputFieldList = std::vector;
// Interface types are abstract complex output types that have a set of fields. They
// are inherited by concrete object output types which support all of the fields in
// the interface, and the concrete object matches the interface for fragment type
// conditions. The fields can include any output type.
struct [[nodiscard]] InterfaceType
{
std::string_view type;
std::string_view cppType;
std::vector interfaces;
OutputFieldList fields;
std::string_view description;
};
using InterfaceTypeList = std::vector;
// Object types are concrete complex output types that have a set of fields. They
// may inherit multiple interfaces.
struct [[nodiscard]] ObjectType
{
std::string_view type;
std::string_view cppType;
std::vector interfaces;
std::vector unions;
OutputFieldList fields;
std::string_view description;
};
using ObjectTypeList = std::vector;
// The schema maps operation types to named types.
struct [[nodiscard]] OperationType
{
std::string_view type;
std::string_view cppType;
std::string_view operation;
};
using OperationTypeList = std::vector;
struct [[nodiscard]] SchemaOptions
{
const std::string schemaFilename;
const std::string filenamePrefix;
const std::string schemaNamespace;
const bool isIntrospection = false;
};
class [[nodiscard]] SchemaLoader
{
public:
// Initialize the loader with the introspection schema or a custom GraphQL schema.
explicit SchemaLoader(SchemaOptions&& schemaOptions);
[[nodiscard]] bool isIntrospection() const noexcept;
[[nodiscard]] std::string_view getSchemaDescription() const noexcept;
[[nodiscard]] std::string_view getFilenamePrefix() const noexcept;
[[nodiscard]] std::string_view getSchemaNamespace() const noexcept;
[[nodiscard]] static std::string_view getIntrospectionNamespace() noexcept;
[[nodiscard]] static const BuiltinTypeMap& getBuiltinTypes() noexcept;
[[nodiscard]] static const CppTypeMap& getBuiltinCppTypes() noexcept;
[[nodiscard]] static std::string_view getScalarCppType() noexcept;
[[nodiscard]] SchemaType getSchemaType(std::string_view type) const;
[[nodiscard]] const tao::graphqlpeg::position& getTypePosition(std::string_view type) const;
[[nodiscard]] size_t getScalarIndex(std::string_view type) const;
[[nodiscard]] const ScalarTypeList& getScalarTypes() const noexcept;
[[nodiscard]] size_t getEnumIndex(std::string_view type) const;
[[nodiscard]] const EnumTypeList& getEnumTypes() const noexcept;
[[nodiscard]] size_t getInputIndex(std::string_view type) const;
[[nodiscard]] const InputTypeList& getInputTypes() const noexcept;
[[nodiscard]] size_t getUnionIndex(std::string_view type) const;
[[nodiscard]] const UnionTypeList& getUnionTypes() const noexcept;
[[nodiscard]] size_t getInterfaceIndex(std::string_view type) const;
[[nodiscard]] const InterfaceTypeList& getInterfaceTypes() const noexcept;
[[nodiscard]] size_t getObjectIndex(std::string_view type) const;
[[nodiscard]] const ObjectTypeList& getObjectTypes() const noexcept;
[[nodiscard]] const DirectiveList& getDirectives() const noexcept;
[[nodiscard]] const tao::graphqlpeg::position& getDirectivePosition(
std::string_view type) const;
[[nodiscard]] const OperationTypeList& getOperationTypes() const noexcept;
[[nodiscard]] static std::string_view getSafeCppName(std::string_view type) noexcept;
[[nodiscard]] std::string_view getCppType(std::string_view type) const noexcept;
[[nodiscard]] std::string getInputCppType(const InputField& field) const noexcept;
[[nodiscard]] std::string getOutputCppType(const OutputField& field) const noexcept;
[[nodiscard]] static std::string getOutputCppAccessor(const OutputField& field) noexcept;
[[nodiscard]] static std::string getOutputCppResolver(const OutputField& field) noexcept;
private:
[[nodiscard]] static bool isExtension(const peg::ast_node& definition) noexcept;
void visitDefinition(const peg::ast_node& definition);
void visitSchemaDefinition(const peg::ast_node& schemaDefinition);
void visitSchemaExtension(const peg::ast_node& schemaExtension);
void visitScalarTypeDefinition(const peg::ast_node& scalarTypeDefinition);
void visitScalarTypeExtension(const peg::ast_node& scalarTypeExtension);
void visitEnumTypeDefinition(const peg::ast_node& enumTypeDefinition);
void visitEnumTypeExtension(const peg::ast_node& enumTypeExtension);
void visitInputObjectTypeDefinition(const peg::ast_node& inputObjectTypeDefinition);
void visitInputObjectTypeExtension(const peg::ast_node& inputObjectTypeExtension);
void visitUnionTypeDefinition(const peg::ast_node& unionTypeDefinition);
void visitUnionTypeExtension(const peg::ast_node& unionTypeExtension);
void visitInterfaceTypeDefinition(const peg::ast_node& interfaceTypeDefinition);
void visitInterfaceTypeExtension(const peg::ast_node& interfaceTypeExtension);
void visitObjectTypeDefinition(const peg::ast_node& objectTypeDefinition);
void visitObjectTypeExtension(const peg::ast_node& objectTypeExtension);
void visitDirectiveDefinition(const peg::ast_node& directiveDefinition);
static void blockReservedName(
std::string_view name, std::optional position = std::nullopt);
[[nodiscard]] static OutputFieldList getOutputFields(const peg::ast_node::children_t& fields);
[[nodiscard]] static InputFieldList getInputFields(const peg::ast_node::children_t& fields);
void validateSchema();
void fixupOutputFieldList(OutputFieldList& fields,
const std::optional& interfaceFields,
const std::optional& accessor);
void fixupInputFieldList(InputFieldList& fields);
void reorderInputTypeDependencies();
void validateImplementedInterfaces() const;
[[nodiscard]] const InterfaceType& findInterfaceType(
std::string_view typeName, std::string_view interfaceName) const;
void validateInterfaceFields(std::string_view typeName, std::string_view interfaceName,
const OutputFieldList& typeFields) const;
void validateTransitiveInterfaces(
std::string_view typeName, const std::vector& interfaces) const;
[[nodiscard]] static std::string getJoinedCppName(
std::string_view prefix, std::string_view fieldName) noexcept;
static const std::string_view s_introspectionNamespace;
static const BuiltinTypeMap s_builtinTypes;
static const CppTypeMap s_builtinCppTypes;
static const std::string_view s_scalarCppType;
const SchemaOptions _schemaOptions;
const bool _isIntrospection;
std::string_view _schemaDescription;
std::string_view _schemaNamespace;
peg::ast _ast;
SchemaTypeMap _schemaTypes;
PositionMap _typePositions;
TypeNameMap _scalarNames;
ScalarTypeList _scalarTypes;
TypeNameMap _enumNames;
EnumTypeList _enumTypes;
TypeNameMap _inputNames;
InputTypeList _inputTypes;
TypeNameMap _unionNames;
UnionTypeList _unionTypes;
TypeNameMap _interfaceNames;
InterfaceTypeList _interfaceTypes;
TypeNameMap _objectNames;
ObjectTypeList _objectTypes;
DirectiveList _directives;
PositionMap _directivePositions;
OperationTypeList _operationTypes;
};
} // namespace graphql::generator
#endif // SCHEMALOADER_H