Skip to content

Commit

Permalink
Merge pull request #88 from wravery/master
Browse files Browse the repository at this point in the history
Implement the Validation section of the spec
  • Loading branch information
wravery authored Mar 8, 2020
2 parents f77f95b + d79d6b6 commit 3ca273a
Show file tree
Hide file tree
Showing 74 changed files with 8,961 additions and 2,367 deletions.
10 changes: 8 additions & 2 deletions include/SchemaGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
#ifndef SCHEMAGENERATOR_H
#define SCHEMAGENERATOR_H

#include <graphqlservice/GraphQLService.h>
#include <graphqlservice/GraphQLGrammar.h>
#include "graphqlservice/GraphQLService.h"
#include "graphqlservice/GraphQLGrammar.h"

#include <array>
#include <cstdio>
Expand Down Expand Up @@ -291,6 +291,12 @@ class Generator
std::vector<std::string> Build() const noexcept;

private:
std::string getHeaderDir() const noexcept;
std::string getSourceDir() const noexcept;
std::string getHeaderPath() const noexcept;
std::string getObjectHeaderPath() const noexcept;
std::string getSourcePath() const noexcept;

void visitDefinition(const peg::ast_node& definition);

void visitSchemaDefinition(const peg::ast_node& schemaDefinition);
Expand Down
257 changes: 257 additions & 0 deletions include/Validation.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#pragma once

#ifndef VALIDATION_H
#define VALIDATION_H

#include "graphqlservice/GraphQLService.h"
#include "graphqlservice/IntrospectionSchema.h"

namespace graphql::service {

using ValidateType = response::Value;

struct ValidateArgument
{
bool defaultValue = false;
bool nonNullDefaultValue = false;
ValidateType type;
};

using ValidateTypeFieldArguments = std::map<std::string, ValidateArgument>;

struct ValidateTypeField
{
ValidateType returnType;
ValidateTypeFieldArguments arguments;
};

using ValidateDirectiveArguments = std::map<std::string, ValidateArgument>;

struct ValidateDirective
{
std::set<introspection::DirectiveLocation> locations;
ValidateDirectiveArguments arguments;
};

struct ValidateArgumentVariable
{
bool operator==(const ValidateArgumentVariable& other) const;

std::string name;
};

struct ValidateArgumentEnumValue
{
bool operator==(const ValidateArgumentEnumValue& other) const;

std::string value;
};

struct ValidateArgumentValue;

struct ValidateArgumentValuePtr
{
bool operator==(const ValidateArgumentValuePtr& other) const;

std::unique_ptr<ValidateArgumentValue> value;
schema_location position;
};

struct ValidateArgumentList
{
bool operator==(const ValidateArgumentList& other) const;

std::vector<ValidateArgumentValuePtr> values;
};

struct ValidateArgumentMap
{
bool operator==(const ValidateArgumentMap& other) const;

std::map<std::string, ValidateArgumentValuePtr> values;
};

using ValidateArgumentVariant = std::variant<
ValidateArgumentVariable,
response::IntType,
response::FloatType,
response::StringType,
response::BooleanType,
ValidateArgumentEnumValue,
ValidateArgumentList,
ValidateArgumentMap>;

struct ValidateArgumentValue
{
ValidateArgumentValue(ValidateArgumentVariable&& value);
ValidateArgumentValue(response::IntType value);
ValidateArgumentValue(response::FloatType value);
ValidateArgumentValue(response::StringType&& value);
ValidateArgumentValue(response::BooleanType value);
ValidateArgumentValue(ValidateArgumentEnumValue&& value);
ValidateArgumentValue(ValidateArgumentList&& value);
ValidateArgumentValue(ValidateArgumentMap&& value);

ValidateArgumentVariant data;
};

// ValidateArgumentValueVisitor visits the AST and builds a record of a field return type and map
// of the arguments for comparison to see if 2 fields with the same result name can be merged.
class ValidateArgumentValueVisitor
{
public:
ValidateArgumentValueVisitor(std::vector<schema_error>& errors);

void visit(const peg::ast_node& value);

ValidateArgumentValuePtr getArgumentValue();

private:
void visitVariable(const peg::ast_node& variable);
void visitIntValue(const peg::ast_node& intValue);
void visitFloatValue(const peg::ast_node& floatValue);
void visitStringValue(const peg::ast_node& stringValue);
void visitBooleanValue(const peg::ast_node& booleanValue);
void visitNullValue(const peg::ast_node& nullValue);
void visitEnumValue(const peg::ast_node& enumValue);
void visitListValue(const peg::ast_node& listValue);
void visitObjectValue(const peg::ast_node& objectValue);

ValidateArgumentValuePtr _argumentValue;
std::vector<schema_error>& _errors;
};

using ValidateFieldArguments = std::map<std::string, ValidateArgumentValuePtr>;

struct ValidateField
{
ValidateField(std::string&& returnType, std::optional<std::string>&& objectType, const std::string& fieldName, ValidateFieldArguments&& arguments);

bool operator==(const ValidateField& other) const;

std::string returnType;
std::optional<std::string> objectType;
std::string fieldName;
ValidateFieldArguments arguments;
};

using ValidateTypeKinds = std::map<std::string, introspection::TypeKind>;

// ValidateVariableTypeVisitor visits the AST and builds a ValidateType structure representing
// a variable type in an operation definition as if it came from an Introspection query.
class ValidateVariableTypeVisitor
{
public:
ValidateVariableTypeVisitor(const ValidateTypeKinds& typeKinds);

void visit(const peg::ast_node& typeName);

bool isInputType() const;
ValidateType getType();

private:
void visitNamedType(const peg::ast_node& namedType);
void visitListType(const peg::ast_node& listType);
void visitNonNullType(const peg::ast_node& nonNullType);

const ValidateTypeKinds& _typeKinds;

bool _isInputType = false;
ValidateType _variableType;
};

// ValidateExecutableVisitor visits the AST and validates that it is executable against the service schema.
class ValidateExecutableVisitor
{
public:
ValidateExecutableVisitor(const Request& service);

void visit(const peg::ast_node& root);

std::vector<schema_error> getStructuredErrors();

private:
response::Value executeQuery(std::string_view query) const;

static ValidateTypeFieldArguments getArguments(response::ListType&& argumentsMember);

using FieldTypes = std::map<std::string, ValidateTypeField>;
using TypeFields = std::map<std::string, FieldTypes>;
using InputFieldTypes = ValidateTypeFieldArguments;
using InputTypeFields = std::map<std::string, InputFieldTypes>;
using EnumValues = std::map<std::string, std::set<std::string>>;

std::optional<introspection::TypeKind> getTypeKind(const std::string& name) const;
std::optional<introspection::TypeKind> getScopedTypeKind() const;
constexpr bool isScalarType(introspection::TypeKind kind);

bool matchesScopedType(const std::string& name) const;

TypeFields::const_iterator getScopedTypeFields();
InputTypeFields::const_iterator getInputTypeFields(const std::string& name);
static const ValidateType& getValidateFieldType(const FieldTypes::mapped_type& value);
static const ValidateType& getValidateFieldType(const InputFieldTypes::mapped_type& value);
template <class _FieldTypes>
static std::string getFieldType(const _FieldTypes& fields, const std::string& name);
template <class _FieldTypes>
static std::string getWrappedFieldType(const _FieldTypes& fields, const std::string& name);
static std::string getWrappedFieldType(const ValidateType& returnType);

void visitFragmentDefinition(const peg::ast_node& fragmentDefinition);
void visitOperationDefinition(const peg::ast_node& operationDefinition);

void visitSelection(const peg::ast_node& selection);

void visitField(const peg::ast_node& field);
void visitFragmentSpread(const peg::ast_node& fragmentSpread);
void visitInlineFragment(const peg::ast_node& inlineFragment);

void visitDirectives(introspection::DirectiveLocation location, const peg::ast_node& directives);

bool validateInputValue(bool hasNonNullDefaultValue, const ValidateArgumentValuePtr& argument, const ValidateType& type);
bool validateVariableType(bool isNonNull, const ValidateType& variableType, const schema_location& position, const ValidateType& inputType);


const Request& _service;
std::vector<schema_error> _errors;

using OperationTypes = std::map<std::string_view, std::string>;
using Directives = std::map<std::string, ValidateDirective>;
using ExecutableNodes = std::map<std::string, const peg::ast_node&>;
using FragmentSet = std::unordered_set<std::string>;
using MatchingTypes = std::map<std::string, std::set<std::string>>;
using ScalarTypes = std::set<std::string>;
using VariableDefinitions = std::map<std::string, const peg::ast_node&>;
using VariableTypes = std::map<std::string, ValidateArgument>;
using OperationVariables = std::optional<VariableTypes>;
using VariableSet = std::set<std::string>;

OperationTypes _operationTypes;
ValidateTypeKinds _typeKinds;
MatchingTypes _matchingTypes;
Directives _directives;
EnumValues _enumValues;
ScalarTypes _scalarTypes;

ExecutableNodes _fragmentDefinitions;
ExecutableNodes _operationDefinitions;

OperationVariables _operationVariables;
VariableDefinitions _variableDefinitions;
VariableSet _referencedVariables;
FragmentSet _referencedFragments;
FragmentSet _fragmentStack;
FragmentSet _fragmentCycles;
size_t _fieldCount = 0;
TypeFields _typeFields;
InputTypeFields _inputTypeFields;
std::string _scopedType;
std::map<std::string, ValidateField> _selectionFields;
};

} /* namespace graphql::service */

#endif // VALIDATION_H
2 changes: 1 addition & 1 deletion include/graphqlservice/GraphQLGrammar.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#ifndef GRAPHQLGRAMMAR_H
#define GRAPHQLGRAMMAR_H

#include <graphqlservice/GraphQLTree.h>
#include "graphqlservice/GraphQLTree.h"

#define TAO_PEGTL_NAMESPACE tao::graphqlpeg

Expand Down
1 change: 1 addition & 0 deletions include/graphqlservice/GraphQLParse.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ struct ast
{
std::shared_ptr<ast_input> input;
std::shared_ptr<ast_node> root;
bool validated = false;
};

ast parseString(std::string_view input);
Expand Down
8 changes: 4 additions & 4 deletions include/graphqlservice/GraphQLResponse.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,24 +46,24 @@ struct ValueTypeTraits
// Set by r-value reference, get by const reference, and release by value. The only types
// that actually support all 3 methods are StringType and ScalarType, everything else
// overrides some subset of these types with a template specialization.
using set_type = ValueType &&;
using get_type = const ValueType &;
using set_type = ValueType&&;
using get_type = const ValueType&;
using release_type = ValueType;
};

template <>
struct ValueTypeTraits<MapType>
{
// Get by const reference and release by value.
using get_type = const MapType &;
using get_type = const MapType&;
using release_type = MapType;
};

template <>
struct ValueTypeTraits<ListType>
{
// Get by const reference and release by value.
using get_type = const ListType &;
using get_type = const ListType&;
using release_type = ListType;
};

Expand Down
Loading

0 comments on commit 3ca273a

Please sign in to comment.