Skip to content

Commit 3047fc9

Browse files
committed
Fix #225 for clientgen
1 parent 07ebed1 commit 3047fc9

File tree

12 files changed

+217
-131
lines changed

12 files changed

+217
-131
lines changed

include/RequestLoader.h

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#include "graphqlservice/internal/Grammar.h"
1515
#include "graphqlservice/internal/Schema.h"
1616

17+
#include <unordered_set>
18+
1719
namespace graphql::generator {
1820

1921
using RequestSchemaType = std::shared_ptr<const schema::BaseType>;
@@ -40,9 +42,18 @@ struct ResponseField
4042
ResponseFieldList children;
4143
};
4244

43-
struct RequestVariable
45+
struct RequestInputType
4446
{
4547
RequestSchemaType type;
48+
std::unordered_set<std::string_view> dependencies {};
49+
std::vector<std::string_view> declarations {};
50+
};
51+
52+
using RequestInputTypeList = std::vector<RequestInputType>;
53+
54+
struct RequestVariable
55+
{
56+
RequestInputType inputType;
4657
TypeModifierStack modifiers;
4758
std::string_view name;
4859
std::string_view cppName;
@@ -76,10 +87,12 @@ class RequestLoader
7687
const ResponseType& getResponseType() const noexcept;
7788
const RequestVariableList& getVariables() const noexcept;
7889

79-
const RequestSchemaTypeList& getReferencedInputTypes() const noexcept;
90+
const RequestInputTypeList& getReferencedInputTypes() const noexcept;
8091
const RequestSchemaTypeList& getReferencedEnums() const noexcept;
8192

8293
std::string getInputCppType(const RequestSchemaType& wrappedInputType) const noexcept;
94+
std::string getInputCppType(const RequestSchemaType& inputType,
95+
const TypeModifierStack& modifiers) const noexcept;
8396
static std::string getOutputCppType(
8497
std::string_view outputCppType, const TypeModifierStack& modifiers) noexcept;
8598

@@ -99,7 +112,7 @@ class RequestLoader
99112
void collectFragments() noexcept;
100113
void collectVariables() noexcept;
101114
void collectInputTypes(const RequestSchemaType& variableType) noexcept;
102-
void reorderInputTypeDependencies() noexcept;
115+
void reorderInputTypeDependencies();
103116
void collectEnums(const RequestSchemaType& variableType) noexcept;
104117
void collectEnums(const ResponseField& responseField) noexcept;
105118

@@ -146,7 +159,7 @@ class RequestLoader
146159
ResponseType _responseType;
147160
RequestVariableList _variables;
148161
internal::string_view_set _inputTypeNames;
149-
RequestSchemaTypeList _referencedInputTypes;
162+
RequestInputTypeList _referencedInputTypes;
150163
internal::string_view_set _enumNames;
151164
RequestSchemaTypeList _referencedEnums;
152165
};

include/graphqlservice/GraphQLClient.h

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,17 +71,32 @@ enum class TypeModifier
7171
List,
7272
};
7373

74+
// Specialized to return true for all INPUT_OBJECT types.
75+
template <typename Type>
76+
constexpr bool isInputType()
77+
{
78+
return false;
79+
}
80+
7481
// Serialize variable input values with chained type modifiers which add nullable or list wrappers.
7582
template <typename Type>
7683
struct ModifiedVariable
7784
{
85+
// Special-case an innermost nullable INPUT_OBJECT type.
86+
template <TypeModifier... Other>
87+
static constexpr bool onlyNoneModifiers() noexcept
88+
{
89+
return (... && (Other == TypeModifier::None));
90+
}
91+
7892
// Peel off modifiers until we get to the underlying type.
7993
template <typename U, TypeModifier Modifier = TypeModifier::None, TypeModifier... Other>
8094
struct VariableTraits
8195
{
8296
// Peel off modifiers until we get to the underlying type.
8397
using type = typename std::conditional_t<TypeModifier::Nullable == Modifier,
84-
std::optional<typename VariableTraits<U, Other...>::type>,
98+
typename std::conditional_t<isInputType<U>() && onlyNoneModifiers<Other...>(),
99+
std::unique_ptr<U>, std::optional<typename VariableTraits<U, Other...>::type>>,
85100
typename std::conditional_t<TypeModifier::List == Modifier,
86101
std::vector<typename VariableTraits<U, Other...>::type>, U>>;
87102
};
@@ -115,7 +130,7 @@ struct ModifiedVariable
115130
if (nullableValue)
116131
{
117132
result = serialize<Other...>(std::move(*nullableValue));
118-
nullableValue = std::nullopt;
133+
nullableValue.reset();
119134
}
120135

121136
return result;

include/graphqlservice/GraphQLService.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -591,13 +591,6 @@ enum class TypeModifier
591591
List,
592592
};
593593

594-
// Used by ModifiedArgument to special-case an innermost nullable INPUT_OBJECT type.
595-
template <TypeModifier... Other>
596-
constexpr bool onlyNoneModifiers() noexcept
597-
{
598-
return (... && (Other == TypeModifier::None));
599-
}
600-
601594
// Specialized to return true for all INPUT_OBJECT types.
602595
template <typename Type>
603596
constexpr bool isInputType()
@@ -612,6 +605,13 @@ constexpr bool isInputType()
612605
template <typename Type>
613606
struct ModifiedArgument
614607
{
608+
// Special-case an innermost nullable INPUT_OBJECT type.
609+
template <TypeModifier... Other>
610+
static constexpr bool onlyNoneModifiers() noexcept
611+
{
612+
return (... && (Other == TypeModifier::None));
613+
}
614+
615615
// Peel off modifiers until we get to the underlying type.
616616
template <typename U, TypeModifier Modifier = TypeModifier::None, TypeModifier... Other>
617617
struct ArgumentTraits

samples/client/mutate/MutateClient.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ static const std::array<std::string_view, 4> s_namesTaskState = {
2424
"Unassigned"sv,
2525
};
2626

27+
template <>
28+
constexpr bool isInputType<Variables::CompleteTaskInput>() noexcept
29+
{
30+
return true;
31+
}
32+
2733
template <>
2834
response::Value ModifiedVariable<TaskState>::serialize(TaskState&& value)
2935
{
@@ -132,7 +138,7 @@ const std::string& GetRequestText() noexcept
132138
# Copyright (c) Microsoft Corporation. All rights reserved.
133139
# Licensed under the MIT License.
134140
135-
mutation CompleteTaskMutation($input: CompleteTaskInput! = {id: "ZmFrZVRhc2tJZA==", isComplete: true, clientMutationId: "Hi There!"}, $skipClientMutationId: Boolean!) {
141+
mutation CompleteTaskMutation($input: CompleteTaskInput = {id: "ZmFrZVRhc2tJZA==", isComplete: true, clientMutationId: "Hi There!"}, $skipClientMutationId: Boolean!) {
136142
completedTask: completeTask(input: $input) {
137143
completedTask: task {
138144
completedTaskId: id
@@ -165,7 +171,7 @@ response::Value serializeVariables(Variables&& variables)
165171
{
166172
response::Value result { response::Type::Map };
167173

168-
result.emplace_back(R"js(input)js"s, ModifiedVariable<Variables::CompleteTaskInput>::serialize(std::move(variables.input)));
174+
result.emplace_back(R"js(input)js"s, ModifiedVariable<Variables::CompleteTaskInput>::serialize<TypeModifier::Nullable>(std::move(variables.input)));
169175
result.emplace_back(R"js(skipClientMutationId)js"s, ModifiedVariable<bool>::serialize(std::move(variables.skipClientMutationId)));
170176

171177
return result;

samples/client/mutate/MutateClient.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ static_assert(graphql::internal::MinorVersion == 2, "regenerate with clientgen:
2929
/// # Copyright (c) Microsoft Corporation. All rights reserved.
3030
/// # Licensed under the MIT License.
3131
///
32-
/// mutation CompleteTaskMutation($input: CompleteTaskInput! = {id: "ZmFrZVRhc2tJZA==", isComplete: true, clientMutationId: "Hi There!"}, $skipClientMutationId: Boolean!) {
32+
/// mutation CompleteTaskMutation($input: CompleteTaskInput = {id: "ZmFrZVRhc2tJZA==", isComplete: true, clientMutationId: "Hi There!"}, $skipClientMutationId: Boolean!) {
3333
/// completedTask: completeTask(input: $input) {
3434
/// completedTask: task {
3535
/// completedTaskId: id
@@ -66,7 +66,7 @@ struct Variables
6666
std::optional<std::string> clientMutationId {};
6767
};
6868

69-
CompleteTaskInput input {};
69+
std::unique_ptr<CompleteTaskInput> input {};
7070
bool skipClientMutationId {};
7171
};
7272

samples/client/mutate/mutate.today.graphql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Copyright (c) Microsoft Corporation. All rights reserved.
22
# Licensed under the MIT License.
33

4-
mutation CompleteTaskMutation($input: CompleteTaskInput! = {id: "ZmFrZVRhc2tJZA==", isComplete: true, clientMutationId: "Hi There!"}, $skipClientMutationId: Boolean!) {
4+
mutation CompleteTaskMutation($input: CompleteTaskInput = {id: "ZmFrZVRhc2tJZA==", isComplete: true, clientMutationId: "Hi There!"}, $skipClientMutationId: Boolean!) {
55
completedTask: completeTask(input: $input) {
66
completedTask: task {
77
completedTaskId: id

src/ClientGenerator.cpp

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -233,17 +233,35 @@ static_assert(graphql::internal::MinorVersion == )cpp"
233233
{
234234
)cpp";
235235

236+
std::unordered_set<std::string_view> forwardDeclared;
237+
236238
// Define all of the input object structs referenced in variables.
237239
for (const auto& inputType : _requestLoader.getReferencedInputTypes())
238240
{
239241
pendingSeparator.reset();
240242

241-
headerFile << R"cpp( struct )cpp" << _schemaLoader.getCppType(inputType->name())
243+
if (!inputType.declarations.empty())
244+
{
245+
// Forward declare nullable dependencies
246+
for (auto declaration : inputType.declarations)
247+
{
248+
if (forwardDeclared.insert(declaration).second)
249+
{
250+
headerFile << R"cpp(struct )cpp" << declaration << R"cpp(;
251+
)cpp";
252+
pendingSeparator.add();
253+
}
254+
}
255+
256+
pendingSeparator.reset();
257+
}
258+
259+
headerFile << R"cpp( struct )cpp" << _schemaLoader.getCppType(inputType.type->name())
242260
<< R"cpp(
243261
{
244262
)cpp";
245263

246-
for (const auto& inputField : inputType->inputFields())
264+
for (const auto& inputField : inputType.type->inputFields())
247265
{
248266

249267
headerFile << R"cpp( )cpp"
@@ -263,7 +281,9 @@ static_assert(graphql::internal::MinorVersion == )cpp"
263281

264282
for (const auto& variable : variables)
265283
{
266-
headerFile << R"cpp( )cpp" << _schemaLoader.getCppType(variable.type->name())
284+
headerFile << R"cpp( )cpp"
285+
<< _requestLoader.getInputCppType(variable.inputType.type,
286+
variable.modifiers)
267287
<< R"cpp( )cpp" << variable.cppName << R"cpp( {};
268288
)cpp";
269289
}
@@ -477,6 +497,21 @@ using namespace )cpp"
477497
pendingSeparator.add();
478498
}
479499

500+
for (const auto& inputType : _requestLoader.getReferencedInputTypes())
501+
{
502+
pendingSeparator.reset();
503+
504+
sourceFile << R"cpp(template <>
505+
constexpr bool isInputType<Variables::)cpp"
506+
<< _schemaLoader.getCppType(inputType.type->name()) << R"cpp(>() noexcept
507+
{
508+
return true;
509+
}
510+
)cpp";
511+
512+
pendingSeparator.add();
513+
}
514+
480515
if (!variables.empty())
481516
{
482517
for (const auto& enumType : _requestLoader.getReferencedEnums())
@@ -508,7 +543,7 @@ response::Value ModifiedVariable<)cpp"
508543
{
509544
pendingSeparator.reset();
510545

511-
const auto cppType = _schemaLoader.getCppType(inputType->name());
546+
const auto cppType = _schemaLoader.getCppType(inputType.type->name());
512547

513548
sourceFile << R"cpp(template <>
514549
response::Value ModifiedVariable<Variables::)cpp"
@@ -518,7 +553,7 @@ response::Value ModifiedVariable<Variables::)cpp"
518553
response::Value result { response::Type::Map };
519554
520555
)cpp";
521-
for (const auto& inputField : inputType->inputFields())
556+
for (const auto& inputField : inputType.type->inputFields())
522557
{
523558
const auto [type, modifiers] =
524559
RequestLoader::unwrapSchemaType(inputField->type().lock());
@@ -610,14 +645,15 @@ response::Value serializeVariables(Variables&& variables)
610645

611646
const auto& builtinTypes = _schemaLoader.getBuiltinTypes();
612647

613-
if (builtinTypes.find(variable.type->name()) == builtinTypes.cend()
614-
&& _schemaLoader.getSchemaType(variable.type->name()) != SchemaType::Scalar)
648+
if (builtinTypes.find(variable.inputType.type->name()) == builtinTypes.cend()
649+
&& _schemaLoader.getSchemaType(variable.inputType.type->name())
650+
!= SchemaType::Scalar)
615651
{
616652
sourceFile << R"cpp(Variables::)cpp";
617653
}
618654

619-
sourceFile << _schemaLoader.getCppType(variable.type->name()) << R"cpp(>::serialize)cpp"
620-
<< getTypeModifierList(variable.modifiers)
655+
sourceFile << _schemaLoader.getCppType(variable.inputType.type->name())
656+
<< R"cpp(>::serialize)cpp" << getTypeModifierList(variable.modifiers)
621657
<< R"cpp((std::move(variables.)cpp" << variable.cppName << R"cpp()));
622658
)cpp";
623659
}

0 commit comments

Comments
 (0)