Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
ab0ec7f
fix: INPUT_OBJECT is not a scalar type
barbieri Nov 30, 2020
f074ae6
ValidateExecutableVisitor use a single, shared, introspection query
barbieri Nov 30, 2020
eb93083
ValidateExecutableVisitor::getScopedTypeFields() use read only data
barbieri Nov 30, 2020
d97f014
ValidateExecutableVisitor::getInputTypeFields() use read only data
barbieri Nov 30, 2020
76e3ba6
refactor ValidateExecutableVisitor, split field get x add
barbieri Nov 30, 2020
61738bc
refactor ValidateExecutableVisitor to handle scalar/enum introspection
barbieri Nov 30, 2020
2f1a802
refactor ValidateExecutableVisitor, split non-scalar introspection
barbieri Nov 30, 2020
6b44541
refactor ValidateExecutableVisitor, split directives handling
barbieri Dec 7, 2020
633565e
ValidateExecutableVisitor process fields and input fields in one go
barbieri Nov 30, 2020
369c3a9
ValidateExecutableVisitor doesn't hold service anymore
barbieri Dec 1, 2020
3e5610d
split ValidationContext into a shared class
barbieri Dec 1, 2020
e85f360
ValidationContext change operationTypes to a struct
barbieri Dec 1, 2020
4c6cc75
add specific ValidateType
barbieri Dec 1, 2020
b76dc2c
simplify variable processing
barbieri Dec 8, 2020
2c8954a
enum validation now uses string_view
barbieri Dec 8, 2020
20089d5
field validation now uses string_view
barbieri Dec 8, 2020
8a7bff8
directives validation now uses string_view
barbieri Dec 8, 2020
faa53cc
simplify operation validation, use string_view
barbieri Dec 8, 2020
1675497
graphql::service::Request now accepts an introspection query results
barbieri Dec 1, 2020
e7d158b
create GraphQLError.h
barbieri Dec 4, 2020
e90ab5a
add operator== to basic GraphQLError types
barbieri Dec 5, 2020
372710b
refactor Response TypedData
barbieri Dec 9, 2020
7b79024
response now uses copy-on-write for complex types
barbieri Dec 9, 2020
d06d2b7
optimize Request::findOperationDefinition()
barbieri Dec 9, 2020
c677d45
Validation: replace queue with list
barbieri Dec 9, 2020
7d746b5
Result conversions should all be launched as deferred
barbieri Dec 9, 2020
b72fe3c
add response::Type::Result
barbieri Dec 7, 2020
a0af0e1
optimize SelectionVisitor::visitField()
barbieri Dec 9, 2020
d8fdda7
optimize list result conversion
barbieri Dec 9, 2020
cb52a63
optimize converters for FieldResult
barbieri Dec 9, 2020
6cbf8c8
do not use ResultType unless really needed
barbieri Dec 9, 2020
89448cd
split ValidationContext into a public file
barbieri Dec 9, 2020
f1033e4
Split ValidationContext base class and IntrospectionValidationContext
barbieri Dec 9, 2020
c98f446
Prepare Request to receive a ValidationContext
barbieri Dec 9, 2020
499a0d3
Validation is now fully string_view
barbieri Dec 10, 2020
801ac3e
schemagen now outputs the ValidationContext
barbieri Dec 10, 2020
5dcd12e
schemagen allow introspection to be disabled
barbieri Dec 10, 2020
25afd86
change field_path to list and make it a const-ref in SelectionVisitor
barbieri Dec 10, 2020
7eb32e4
BREAKING SelectionSetParams keeps a reference to the parent
barbieri Dec 10, 2020
ec460f5
Provide SelectionSetParams constructor with parent + ownErrorPath
barbieri Dec 10, 2020
78ca2c9
BREAKING ResolverParams now uses string view
barbieri Dec 10, 2020
a142c6c
ownErrorPath is now a const path_segment
barbieri Dec 10, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ settings.json
/test/pegtl_tests
/test/response_tests
/test/today_tests
/test/nointrospection_tests
build/
install/
isenseconfig/
84 changes: 83 additions & 1 deletion include/SchemaGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ class Generator
// Run the generator and return a list of filenames that were output.
std::vector<std::string> Build() const noexcept;

private:
protected:
std::string getHeaderDir() const noexcept;
std::string getSourceDir() const noexcept;
std::string getHeaderPath() const noexcept;
Expand Down Expand Up @@ -381,6 +381,42 @@ class Generator
std::string getResolverDeclaration(const OutputField& outputField) const noexcept;

bool outputSource() const noexcept;

static void outputValidationScalarsList(
std::ostream& sourceFile, const ScalarTypeList& scalars);
static void outputValidationEnumsList(std::ostream& sourceFile, const EnumTypeList& enums);
static void outputValidationInputTypeList(
std::ostream& sourceFile, const InputTypeList& inputTypes);
static void outputValidationInputTypeListSetFields(
std::ostream& sourceFile, const InputTypeList& inputTypes);
static void outputValidationUnionTypeList(
std::ostream& sourceFile, const UnionTypeList& unionTypes);
static void outputValidationUnionTypeListSetFieldsAndPossibleTypes(
std::ostream& sourceFile, const UnionTypeList& unionTypes);
static void outputValidationInterfaceTypeList(
std::ostream& sourceFile, const InterfaceTypeList& interfaceTypes);
static void outputValidationInterfaceTypeListSetFieldsAndPossibleTypes(std::ostream& sourceFile,
const InterfaceTypeList& interfaceTypes,
const std::unordered_map<std::string, std::vector<std::string>>& interfacePossibleTypes);
static void outputValidationObjectTypeList(std::ostream& sourceFile,
const ObjectTypeList& objectTypes,
std::unordered_map<std::string, std::vector<std::string>>& interfacePossibleTypes);
static void outputValidationObjectTypeListSetFields(
std::ostream& sourceFile, const ObjectTypeList& objectTypes, const std::string& queryType);
static void outputValidationDirectiveList(
std::ostream& sourceFile, const DirectiveList& directives);

static void outputValidationInputField(std::ostream& sourceFile, const InputField& inputField);
static void outputValidationInputFieldListArrayBody(std::ostream& sourceFile,
const InputFieldList& list, const std::string indent, const std::string separator);
static void outputValidationOutputField(
std::ostream& sourceFile, const OutputField& outputField);
static void outputValidationSetFields(
std::ostream& sourceFile, const std::string& cppType, const OutputFieldList& list);
static void outputValidationSetPossibleTypes(std::ostream& sourceFile,
const std::string& cppType, const std::vector<std::string>& options);

void outputValidationContext(std::ostream& sourceFile) const;
void outputObjectImplementation(
std::ostream& sourceFile, const ObjectType& objectType, bool isQueryType) const;
void outputObjectIntrospection(std::ostream& sourceFile, const ObjectType& objectType) const;
Expand All @@ -393,6 +429,8 @@ class Generator
std::string getTypeModifiers(const TypeModifierStack& modifiers) const noexcept;
std::string getIntrospectionType(
const std::string& type, const TypeModifierStack& modifiers) const noexcept;
static std::string getValidationType(
const std::string& type, const TypeModifierStack& modifiers) noexcept;

std::vector<std::string> outputSeparateFiles() const noexcept;

Expand Down Expand Up @@ -430,6 +468,50 @@ class Generator
OperationTypeList _operationTypes;
};

class IntrospectionValidationContextGenerator : Generator
{
public:
IntrospectionValidationContextGenerator()
: Generator({ std::nullopt, std::nullopt, false, false, false })
{
}

const ScalarTypeList& GetScalarTypes() const
{
return _scalarTypes;
}

const EnumTypeList& GetEnumTypes() const
{
return _enumTypes;
}

const InputTypeList& GetInputTypes() const
{
return _inputTypes;
}

const UnionTypeList& GetUnionTypes() const
{
return _unionTypes;
}

const InterfaceTypeList& GetInterfaceTypes() const
{
return _interfaceTypes;
}

const ObjectTypeList& GetObjectTypes() const
{
return _objectTypes;
}

const DirectiveList& GetDirectives() const
{
return _directives;
}
};

} /* namespace graphql::schema */

#endif // SCHEMAGENERATOR_H
121 changes: 32 additions & 89 deletions include/Validation.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,48 +6,27 @@
#ifndef VALIDATION_H
#define VALIDATION_H

#include "graphqlservice/GraphQLService.h"
#include "graphqlservice/IntrospectionSchema.h"
#include "graphqlservice/GraphQLValidation.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
struct VariableDefinition : public ValidateArgument
{
std::set<introspection::DirectiveLocation> locations;
ValidateDirectiveArguments arguments;
schema_location position;
};

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

std::string name;
const std::string_view name;
};

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

std::string value;
const std::string_view value;
};

struct ValidateArgumentValue;
Expand All @@ -71,7 +50,7 @@ struct ValidateArgumentMap
{
bool operator==(const ValidateArgumentMap& other) const;

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

using ValidateArgumentVariant = std::variant<ValidateArgumentVariable, response::IntType,
Expand Down Expand Up @@ -118,83 +97,59 @@ class ValidateArgumentValueVisitor
std::vector<schema_error>& _errors;
};

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

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

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

std::string returnType;
std::optional<std::string> objectType;
std::string fieldName;
std::shared_ptr<const ValidateType> returnType;
std::shared_ptr<const ValidateType> objectType;
std::string_view fieldName;
ValidateFieldArguments arguments;
};

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

// 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);
ValidateVariableTypeVisitor(const ValidationContext& validationContext);

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

bool isInputType() const;
ValidateType getType();
std::shared_ptr<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;
const ValidationContext& _validationContext;

bool _isInputType = false;
ValidateType _variableType;
std::shared_ptr<ValidateType> _variableType;
};

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

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);
bool matchesScopedType(const ValidateType& name) const;

void visitFragmentDefinition(const peg::ast_node& fragmentDefinition);
void visitOperationDefinition(const peg::ast_node& operationDefinition);
Expand All @@ -213,45 +168,33 @@ class ValidateExecutableVisitor
bool validateVariableType(bool isNonNull, const ValidateType& variableType,
const schema_location& position, const ValidateType& inputType);

const Request& _service;
const ValidationContext& _validationContext;

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 ExecutableNodes = std::unordered_map<std::string_view, const peg::ast_node&>;
using FragmentSet = std::unordered_set<std::string_view>;
using VariableTypes = std::unordered_map<std::string_view, VariableDefinition>;
using OperationVariables = std::optional<VariableTypes>;
using VariableSet = std::set<std::string>;

// These members store Introspection schema information which does not change between queries.
OperationTypes _operationTypes;
ValidateTypeKinds _typeKinds;
MatchingTypes _matchingTypes;
Directives _directives;
EnumValues _enumValues;
ScalarTypes _scalarTypes;
using VariableSet = std::unordered_set<const VariableDefinition*>;

// These members store information that's specific to a single query and changes every time we
// visit a new one. They must be reset in between queries.
ExecutableNodes _fragmentDefinitions;
ExecutableNodes _operationDefinitions;
FragmentSet _referencedFragments;
FragmentSet _fragmentCycles;

// These members store state for the visitor. They implicitly reset each time we call visit.
OperationVariables _operationVariables;
VariableDefinitions _variableDefinitions;
VariableSet _referencedVariables;
FragmentSet _fragmentStack;
size_t _fieldCount = 0;
TypeFields _typeFields;
InputTypeFields _inputTypeFields;
std::string _scopedType;
std::map<std::string, ValidateField> _selectionFields;
std::shared_ptr<const ValidateType> _scopedType;
std::unordered_map<std::string_view, ValidateField> _selectionFields;
struct
{
std::shared_ptr<ValidateType> nonNullString;
} commonTypes;
};

} /* namespace graphql::service */
Expand Down
Loading