Skip to content

Commit e220447

Browse files
committed
Collect referenced enum and input types
1 parent 2b8505b commit e220447

File tree

2 files changed

+220
-31
lines changed

2 files changed

+220
-31
lines changed

include/RequestLoader.h

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,16 @@
1515

1616
namespace graphql::generator {
1717

18+
using RequestSchemaType = std::shared_ptr<const schema::BaseType>;
19+
using RequestSchemaTypeList = std::vector<RequestSchemaType>;
20+
1821
struct ResponseField;
1922

2023
using ResponseFieldList = std::vector<ResponseField>;
2124

2225
struct ResponseType
2326
{
24-
std::shared_ptr<const schema::BaseType> type;
27+
RequestSchemaType type;
2528
std::string_view cppType;
2629
ResponseFieldList fields;
2730
};
@@ -31,7 +34,7 @@ using ResponseFieldChildren = std::variant<ResponseFieldList, ResponseUnionOptio
3134

3235
struct ResponseField
3336
{
34-
std::shared_ptr<const schema::BaseType> type;
37+
RequestSchemaType type;
3538
TypeModifierStack modifiers;
3639
std::string_view name;
3740
std::string_view cppName;
@@ -41,7 +44,7 @@ struct ResponseField
4144

4245
struct RequestVariable
4346
{
44-
std::shared_ptr<const schema::BaseType> type;
47+
RequestSchemaType type;
4548
TypeModifierStack modifiers;
4649
std::string_view name;
4750
std::string_view cppName;
@@ -72,21 +75,30 @@ class RequestLoader
7275
std::string_view getOperationType() const noexcept;
7376
std::string_view getRequestText() const noexcept;
7477

75-
const RequestVariableList& getVariables() const noexcept;
7678
const ResponseType& getResponseType() const noexcept;
79+
const RequestVariableList& getVariables() const noexcept;
80+
81+
const RequestSchemaTypeList& getReferencedInputTypes() const noexcept;
82+
const RequestSchemaTypeList& getReferencedEnums() const noexcept;
83+
84+
std::string getInputCppType(const RequestVariable& variable) const noexcept;
85+
std::string getOutputCppType(const ResponseField& field) const noexcept;
7786

7887
private:
7988
void buildSchema();
8089
void addTypesToSchema();
81-
std::shared_ptr<const schema::BaseType> getSchemaType(
90+
RequestSchemaType getSchemaType(
8291
std::string_view type, const TypeModifierStack& modifiers) const noexcept;
8392
void validateRequest() const;
8493

8594
static std::string_view trimWhitespace(std::string_view content) noexcept;
8695

8796
void findOperation();
88-
void collectVariables() noexcept;
8997
void collectFragments() noexcept;
98+
void collectVariables() noexcept;
99+
void collectInputTypes(const RequestSchemaType& variableType) noexcept;
100+
void collectEnums(const RequestSchemaType& variableType) noexcept;
101+
void collectEnums(const ResponseField& responseField) noexcept;
90102

91103
using FragmentDefinitionMap = std::map<std::string_view, const peg::ast_node*>;
92104

@@ -95,9 +107,8 @@ class RequestLoader
95107
{
96108
public:
97109
explicit SelectionVisitor(const SchemaLoader& schemaLoader,
98-
const FragmentDefinitionMap& fragments,
99-
const std::shared_ptr<schema::Schema>& schema,
100-
const std::shared_ptr<const schema::BaseType>& type);
110+
const FragmentDefinitionMap& fragments, const std::shared_ptr<schema::Schema>& schema,
111+
const RequestSchemaType& type);
101112

102113
void visit(const peg::ast_node& selection);
103114

@@ -108,10 +119,12 @@ class RequestLoader
108119
void visitFragmentSpread(const peg::ast_node& fragmentSpread);
109120
void visitInlineFragment(const peg::ast_node& inlineFragment);
110121

122+
void mergeFragmentFields(ResponseFieldList&& fragmentFields) noexcept;
123+
111124
const SchemaLoader& _schemaLoader;
112125
const FragmentDefinitionMap& _fragments;
113126
const std::shared_ptr<schema::Schema>& _schema;
114-
const std::shared_ptr<const schema::BaseType>& _type;
127+
const RequestSchemaType& _type;
115128

116129
internal::string_view_set _names;
117130
ResponseFieldList _fields;
@@ -126,10 +139,13 @@ class RequestLoader
126139
const peg::ast_node* _operation = nullptr;
127140
std::string_view _operationName;
128141
std::string_view _operationType;
129-
RequestVariableList _variables;
130-
ResponseType _responseType;
131-
132142
FragmentDefinitionMap _fragments;
143+
ResponseType _responseType;
144+
RequestVariableList _variables;
145+
internal::string_view_set _inputTypeNames;
146+
RequestSchemaTypeList _referencedInputTypes;
147+
internal::string_view_set _enumNames;
148+
RequestSchemaTypeList _referencedEnums;
133149
};
134150

135151
} /* namespace graphql::generator */

src/RequestLoader.cpp

Lines changed: 191 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ RequestLoader::RequestLoader(RequestOptions&& requestOptions, const SchemaLoader
4141
validateRequest();
4242

4343
findOperation();
44-
collectVariables();
4544
collectFragments();
4645

4746
const peg::ast_node* selection = nullptr;
@@ -60,6 +59,19 @@ RequestLoader::RequestLoader(RequestOptions&& requestOptions, const SchemaLoader
6059

6160
visitor.visit(*selection);
6261
_responseType.fields = visitor.getFields();
62+
63+
collectVariables();
64+
65+
for (const auto& variable : _variables)
66+
{
67+
collectInputTypes(variable.type);
68+
collectEnums(variable.type);
69+
}
70+
71+
for (const auto& responseField : _responseType.fields)
72+
{
73+
collectEnums(responseField);
74+
}
6375
}
6476

6577
std::string_view RequestLoader::getRequestFilename() const noexcept
@@ -99,14 +111,63 @@ std::string_view RequestLoader::getRequestText() const noexcept
99111
return trimWhitespace(_requestText);
100112
}
101113

114+
const ResponseType& RequestLoader::getResponseType() const noexcept
115+
{
116+
return _responseType;
117+
}
118+
102119
const RequestVariableList& RequestLoader::getVariables() const noexcept
103120
{
104121
return _variables;
105122
}
106123

107-
const ResponseType& RequestLoader::getResponseType() const noexcept
124+
const RequestSchemaTypeList& RequestLoader::getReferencedInputTypes() const noexcept
108125
{
109-
return _responseType;
126+
return _referencedInputTypes;
127+
}
128+
129+
const RequestSchemaTypeList& RequestLoader::getReferencedEnums() const noexcept
130+
{
131+
return _referencedEnums;
132+
}
133+
134+
std::string RequestLoader::getInputCppType(const RequestVariable& variable) const noexcept
135+
{
136+
size_t templateCount = 0;
137+
std::ostringstream inputType;
138+
139+
for (auto modifier : variable.modifiers)
140+
{
141+
switch (modifier)
142+
{
143+
case service::TypeModifier::Nullable:
144+
inputType << R"cpp(std::optional<)cpp";
145+
++templateCount;
146+
break;
147+
148+
case service::TypeModifier::List:
149+
inputType << R"cpp(std::vector<)cpp";
150+
++templateCount;
151+
break;
152+
153+
default:
154+
break;
155+
}
156+
}
157+
158+
inputType << getCppType(field.type);
159+
160+
for (size_t i = 0; i < templateCount; ++i)
161+
{
162+
inputType << R"cpp(>)cpp";
163+
}
164+
165+
return inputType.str();
166+
}
167+
168+
std::string RequestLoader::getOutputCppType(const ResponseField& field) const noexcept
169+
{
170+
110171
}
111172

112173
void RequestLoader::buildSchema()
@@ -411,10 +472,10 @@ void RequestLoader::addTypesToSchema()
411472
}
412473
}
413474

414-
std::shared_ptr<const schema::BaseType> RequestLoader::getSchemaType(
475+
RequestSchemaType RequestLoader::getSchemaType(
415476
std::string_view type, const TypeModifierStack& modifiers) const noexcept
416477
{
417-
std::shared_ptr<const schema::BaseType> introspectionType = _schema->LookupType(type);
478+
RequestSchemaType introspectionType = _schema->LookupType(type);
418479

419480
if (introspectionType)
420481
{
@@ -577,6 +638,13 @@ void RequestLoader::findOperation()
577638
_operationName.empty() ? _responseType.type->name() : _operationName);
578639
}
579640

641+
void RequestLoader::collectFragments() noexcept
642+
{
643+
peg::for_each_child<peg::fragment_definition>(*_ast.root, [this](const peg::ast_node& child) {
644+
_fragments.emplace(child.children.front()->string_view(), &child);
645+
});
646+
}
647+
580648
void RequestLoader::collectVariables() noexcept
581649
{
582650
peg::for_each_child<peg::variable>(*_operation,
@@ -635,16 +703,80 @@ void RequestLoader::collectVariables() noexcept
635703
});
636704
}
637705

638-
void RequestLoader::collectFragments() noexcept
706+
void RequestLoader::collectInputTypes(const RequestSchemaType& variableType) noexcept
639707
{
640-
peg::for_each_child<peg::fragment_definition>(*_ast.root, [this](const peg::ast_node& child) {
641-
_fragments.emplace(child.children.front()->string_view(), &child);
642-
});
708+
// Input types may be referenced in any variable or field of a variable input type.
709+
if (variableType->kind() == introspection::TypeKind::INPUT_OBJECT
710+
&& _inputTypeNames.emplace(variableType->name()).second)
711+
{
712+
_referencedInputTypes.push_back(variableType);
713+
714+
for (const auto& inputField : variableType->inputFields())
715+
{
716+
collectInputTypes(inputField->type().lock());
717+
}
718+
}
719+
}
720+
721+
void RequestLoader::collectEnums(const RequestSchemaType& variableType) noexcept
722+
{
723+
// Enums may be referenced in any variable input type or in the response itself.
724+
if (variableType->kind() == introspection::TypeKind::ENUM
725+
&& _enumNames.emplace(variableType->name()).second)
726+
{
727+
_referencedEnums.push_back(variableType);
728+
}
729+
}
730+
731+
void RequestLoader::collectEnums(const ResponseField& responseField) noexcept
732+
{
733+
switch (responseField.type->kind())
734+
{
735+
case introspection::TypeKind::ENUM:
736+
{
737+
if (_enumNames.emplace(responseField.type->name()).second)
738+
{
739+
_referencedEnums.push_back(responseField.type);
740+
}
741+
742+
break;
743+
}
744+
745+
case introspection::TypeKind::OBJECT:
746+
case introspection::TypeKind::INTERFACE:
747+
{
748+
if (responseField.children)
749+
{
750+
for (const auto& field : std::get<ResponseFieldList>(*responseField.children))
751+
{
752+
collectEnums(field);
753+
}
754+
}
755+
756+
break;
757+
}
758+
759+
case introspection::TypeKind::UNION:
760+
{
761+
if (responseField.children)
762+
{
763+
for (const auto& responseType : std::get<ResponseUnionOptions>(*responseField.children))
764+
{
765+
for (const auto& field : responseType.fields)
766+
{
767+
collectEnums(field);
768+
}
769+
}
770+
}
771+
772+
break;
773+
}
774+
}
643775
}
644776

645777
RequestLoader::SelectionVisitor::SelectionVisitor(const SchemaLoader& schemaLoader,
646778
const FragmentDefinitionMap& fragments, const std::shared_ptr<schema::Schema>& schema,
647-
const std::shared_ptr<const schema::BaseType>& type)
779+
const RequestSchemaType& type)
648780
: _schemaLoader(schemaLoader)
649781
, _fragments(fragments)
650782
, _schema(schema)
@@ -742,6 +874,12 @@ void RequestLoader::SelectionVisitor::visitField(const peg::ast_node& field)
742874
return typeField->name() == name;
743875
});
744876

877+
if (itr == typeFields.end())
878+
{
879+
// Skip fields that are not found on the current type.
880+
return;
881+
}
882+
745883
responseField.type = (*itr)->type().lock();
746884
}
747885

@@ -869,20 +1007,55 @@ void RequestLoader::SelectionVisitor::visitFragmentSpread(const peg::ast_node& f
8691007
};
8701008
}
8711009

872-
for (const auto& selection : itr->second->children)
873-
{
874-
visit(*selection);
875-
}
1010+
const auto typeCondition = itr->second->children[1].get();
1011+
const auto fragmentType = _schema->LookupType(typeCondition->children.front()->string_view());
1012+
const auto& selection = *(itr->second->children.back());
1013+
SelectionVisitor selectionVisitor { _schemaLoader, _fragments, _schema, fragmentType };
1014+
1015+
selectionVisitor.visit(selection);
1016+
mergeFragmentFields(selectionVisitor.getFields());
8761017
}
8771018

8781019
void RequestLoader::SelectionVisitor::visitInlineFragment(const peg::ast_node& inlineFragment)
8791020
{
880-
peg::on_first_child<peg::selection_set>(inlineFragment, [this](const peg::ast_node& child) {
881-
for (const auto& selection : child.children)
1021+
const peg::ast_node* typeCondition = nullptr;
1022+
1023+
peg::on_first_child<peg::type_condition>(inlineFragment,
1024+
[&typeCondition](const peg::ast_node& child) {
1025+
typeCondition = &child;
1026+
});
1027+
1028+
peg::on_first_child<peg::selection_set>(inlineFragment,
1029+
[this, typeCondition](const peg::ast_node& child) {
1030+
const auto fragmentType = typeCondition
1031+
? _schema->LookupType(typeCondition->children.front()->string_view())
1032+
: _type;
1033+
const auto& selection = child;
1034+
SelectionVisitor selectionVisitor { _schemaLoader, _fragments, _schema, fragmentType };
1035+
1036+
selectionVisitor.visit(selection);
1037+
mergeFragmentFields(selectionVisitor.getFields());
1038+
});
1039+
}
1040+
1041+
void RequestLoader::SelectionVisitor::mergeFragmentFields(
1042+
ResponseFieldList&& fragmentFields) noexcept
1043+
{
1044+
fragmentFields.erase(std::remove_if(fragmentFields.begin(),
1045+
fragmentFields.end(),
1046+
[this](const ResponseField& fragmentField) noexcept {
1047+
return !_names.emplace(fragmentField.name).second;
1048+
}),
1049+
fragmentFields.cend());
1050+
1051+
if (!fragmentFields.empty())
1052+
{
1053+
_fields.reserve(_fields.size() + fragmentFields.size());
1054+
for (auto& fragmentField : fragmentFields)
8821055
{
883-
visit(*selection);
1056+
_fields.push_back(std::move(fragmentField));
8841057
}
885-
});
1058+
}
8861059
}
8871060

8881061
} /* namespace graphql::generator */

0 commit comments

Comments
 (0)