// 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(BuiltinType::ID) + 1>; // 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