Skip to content

Commit b41c977

Browse files
authored
Merge pull request #235 from wravery/input-type-references
Input type references
2 parents 2c1a677 + 5ce439b commit b41c977

28 files changed

+1089
-92
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/SchemaLoader.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ struct InputType
106106
InputFieldList fields;
107107
std::string_view description;
108108
std::unordered_set<std::string_view> dependencies {};
109+
std::vector<std::string_view> declarations {};
109110
};
110111

111112
using InputTypeList = std::vector<InputType>;

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() noexcept
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: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -591,20 +591,35 @@ enum class TypeModifier
591591
List,
592592
};
593593

594+
// Specialized to return true for all INPUT_OBJECT types.
595+
template <typename Type>
596+
constexpr bool isInputType() noexcept
597+
{
598+
return false;
599+
}
600+
594601
// Extract individual arguments with chained type modifiers which add nullable or list wrappers.
595602
// If the argument is not optional, use require and let it throw a schema_exception when the
596603
// argument is missing or not the correct type. If it's optional, use find and check the second
597604
// element in the pair to see if it was found or if you just got the default value for that type.
598605
template <typename Type>
599606
struct ModifiedArgument
600607
{
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+
601615
// Peel off modifiers until we get to the underlying type.
602616
template <typename U, TypeModifier Modifier = TypeModifier::None, TypeModifier... Other>
603617
struct ArgumentTraits
604618
{
605619
// Peel off modifiers until we get to the underlying type.
606620
using type = typename std::conditional_t<TypeModifier::Nullable == Modifier,
607-
std::optional<typename ArgumentTraits<U, Other...>::type>,
621+
typename std::conditional_t<isInputType<U>() && onlyNoneModifiers<Other...>(),
622+
std::unique_ptr<U>, std::optional<typename ArgumentTraits<U, Other...>::type>>,
608623
typename std::conditional_t<TypeModifier::List == Modifier,
609624
std::vector<typename ArgumentTraits<U, Other...>::type>, U>>;
610625
};
@@ -678,12 +693,19 @@ struct ModifiedArgument
678693
if (valueItr == arguments.get<response::MapType>().cend()
679694
|| valueItr->second.type() == response::Type::Null)
680695
{
681-
return std::nullopt;
696+
return {};
682697
}
683698

684699
auto result = require<Other...>(name, arguments);
685700

686-
return std::make_optional<decltype(result)>(std::move(result));
701+
if constexpr (isInputType<Type>() && onlyNoneModifiers<Other...>())
702+
{
703+
return std::make_unique<decltype(result)>(std::move(result));
704+
}
705+
else
706+
{
707+
return std::make_optional<decltype(result)>(std::move(result));
708+
}
687709
}
688710

689711
// Peel off list modifiers.

samples/client/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ cmake_minimum_required(VERSION 3.15)
66
add_subdirectory(query)
77
add_subdirectory(mutate)
88
add_subdirectory(subscribe)
9+
add_subdirectory(nestedinput)
910

1011
add_subdirectory(benchmark)
1112

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
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
cmake_minimum_required(VERSION 3.15)
5+
6+
# Normally this would be handled by find_package(cppgraphqlgen CONFIG).
7+
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/cppgraphqlgen-functions.cmake)
8+
9+
if(GRAPHQL_UPDATE_SAMPLES AND GRAPHQL_BUILD_CLIENTGEN)
10+
update_graphql_client_files(nestedinput schema.graphql query.graphql NestedInput nestedinput)
11+
endif()
12+
13+
add_graphql_client_target(nestedinput)

0 commit comments

Comments
 (0)