Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
24 changes: 10 additions & 14 deletions include/graphqlservice/GraphQLService.h
Original file line number Diff line number Diff line change
Expand Up @@ -662,18 +662,16 @@ struct ModifiedArgument
}
}

// Wrap require in a try/catch block.
// Return false only for an absent argument; supplied values must pass conversion.
[[nodiscard("unnecessary call")]] static std::pair<Type, bool> find(
const std::string& name, const response::Value& arguments) noexcept
const std::string& name, const response::Value& arguments)
{
try
{
return { require(name, arguments), true };
}
catch (const std::exception&)
if (arguments.find(name) == arguments.get<response::MapType>().cend())
{
return { Type {}, false };
}

return { require(name, arguments), true };
}

// Peel off the none modifier. If it's included, it should always be last in the list.
Expand Down Expand Up @@ -738,20 +736,18 @@ struct ModifiedArgument
return result;
}

// Wrap require with modifiers in a try/catch block.
// Preserve absence separately from nullable values and conversion errors.
template <TypeModifier Modifier = TypeModifier::None, TypeModifier... Other>
[[nodiscard("unnecessary call")]] static std::pair<
typename ArgumentTraits<Type, Modifier, Other...>::type, bool>
find(std::string_view name, const response::Value& arguments) noexcept
find(std::string_view name, const response::Value& arguments)
{
try
{
return { require<Modifier, Other...>(name, arguments), true };
}
catch (const std::exception&)
if (arguments.find(name) == arguments.get<response::MapType>().cend())
{
return { typename ArgumentTraits<Type, Modifier, Other...>::type {}, false };
}

return { require<Modifier, Other...>(name, arguments), true };
}

// Peel off the none modifier. If it's included, it should always be last in the list.
Expand Down
18 changes: 17 additions & 1 deletion samples/validation/schema/QueryObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ service::ResolverMap Query::getResolvers() const noexcept
{ R"gql(resource)gql"sv, [this](service::ResolverParams&& params) { return resolveResource(std::move(params)); } },
{ R"gql(arguments)gql"sv, [this](service::ResolverParams&& params) { return resolveArguments(std::move(params)); } },
{ R"gql(__typename)gql"sv, [this](service::ResolverParams&& params) { return resolve_typename(std::move(params)); } },
{ R"gql(booleanList)gql"sv, [this](service::ResolverParams&& params) { return resolveBooleanList(std::move(params)); } }
{ R"gql(booleanList)gql"sv, [this](service::ResolverParams&& params) { return resolveBooleanList(std::move(params)); } },
{ R"gql(inputDefaults)gql"sv, [this](service::ResolverParams&& params) { return resolveInputDefaults(std::move(params)); } }
};
}

Expand Down Expand Up @@ -153,6 +154,18 @@ service::AwaitableResolver Query::resolveBooleanList(service::ResolverParams&& p
return service::ModifiedResult<bool>::convert<service::TypeModifier::Nullable>(std::move(result), std::move(params));
}

service::AwaitableResolver Query::resolveInputDefaults(service::ResolverParams&& params) const
{
auto argInput = service::ModifiedArgument<DefaultInput>::require("input", params.arguments);
std::unique_lock resolverLock(_resolverMutex);
service::SelectionSetParams selectionSetParams { static_cast<const service::SelectionSetParams&>(params) };
auto directives = std::move(params.fieldDirectives);
auto result = _pimpl->getInputDefaults(service::FieldParams { std::move(selectionSetParams), std::move(directives) }, std::move(argInput));
resolverLock.unlock();

return service::ModifiedResult<std::string>::convert(std::move(result), std::move(params));
}

service::AwaitableResolver Query::resolve_typename(service::ResolverParams&& params) const
{
return service::Result<std::string>::convert(std::string{ R"gql(Query)gql" }, std::move(params));
Expand All @@ -174,6 +187,9 @@ void AddQueryDetails(const std::shared_ptr<schema::ObjectType>& typeQuery, const
}),
schema::Field::Make(R"gql(booleanList)gql"sv, R"md()md"sv, std::nullopt, schema->LookupType(R"gql(Boolean)gql"sv), {
schema::InputValue::Make(R"gql(booleanListArg)gql"sv, R"md()md"sv, schema->WrapType(introspection::TypeKind::LIST, schema->WrapType(introspection::TypeKind::NON_NULL, schema->LookupType(R"gql(Boolean)gql"sv))), R"gql()gql"sv)
}),
schema::Field::Make(R"gql(inputDefaults)gql"sv, R"md()md"sv, std::nullopt, schema->WrapType(introspection::TypeKind::NON_NULL, schema->LookupType(R"gql(String)gql"sv)), {
schema::InputValue::Make(R"gql(input)gql"sv, R"md()md"sv, schema->WrapType(introspection::TypeKind::NON_NULL, schema->LookupType(R"gql(DefaultInput)gql"sv)), R"gql()gql"sv)
})
});
}
Expand Down
30 changes: 30 additions & 0 deletions samples/validation/schema/QueryObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,18 @@ concept getBooleanList = requires (TImpl impl, std::optional<std::vector<bool>>
{ service::AwaitableScalar<std::optional<bool>> { impl.getBooleanList(std::move(booleanListArgArg)) } };
};

template <class TImpl>
concept getInputDefaultsWithParams = requires (TImpl impl, service::FieldParams params, DefaultInput inputArg)
{
{ service::AwaitableScalar<std::string> { impl.getInputDefaults(std::move(params), std::move(inputArg)) } };
};

template <class TImpl>
concept getInputDefaults = requires (TImpl impl, DefaultInput inputArg)
{
{ service::AwaitableScalar<std::string> { impl.getInputDefaults(std::move(inputArg)) } };
};

template <class TImpl>
concept beginSelectionSet = requires (TImpl impl, const service::SelectionSetParams params)
{
Expand All @@ -135,6 +147,7 @@ class [[nodiscard("unnecessary construction")]] Query final
[[nodiscard("unnecessary call")]] service::AwaitableResolver resolveResource(service::ResolverParams&& params) const;
[[nodiscard("unnecessary call")]] service::AwaitableResolver resolveFindDog(service::ResolverParams&& params) const;
[[nodiscard("unnecessary call")]] service::AwaitableResolver resolveBooleanList(service::ResolverParams&& params) const;
[[nodiscard("unnecessary call")]] service::AwaitableResolver resolveInputDefaults(service::ResolverParams&& params) const;

[[nodiscard("unnecessary call")]] service::AwaitableResolver resolve_typename(service::ResolverParams&& params) const;

Expand All @@ -153,6 +166,7 @@ class [[nodiscard("unnecessary construction")]] Query final
[[nodiscard("unnecessary call")]] virtual service::AwaitableObject<std::shared_ptr<Resource>> getResource(service::FieldParams&& params) const = 0;
[[nodiscard("unnecessary call")]] virtual service::AwaitableObject<std::shared_ptr<Dog>> getFindDog(service::FieldParams&& params, std::unique_ptr<ComplexInput>&& complexArg) const = 0;
[[nodiscard("unnecessary call")]] virtual service::AwaitableScalar<std::optional<bool>> getBooleanList(service::FieldParams&& params, std::optional<std::vector<bool>>&& booleanListArgArg) const = 0;
[[nodiscard("unnecessary call")]] virtual service::AwaitableScalar<std::string> getInputDefaults(service::FieldParams&& params, DefaultInput&& inputArg) const = 0;
};

template <class T>
Expand Down Expand Up @@ -292,6 +306,22 @@ class [[nodiscard("unnecessary construction")]] Query final
}
}

[[nodiscard("unnecessary call")]] service::AwaitableScalar<std::string> getInputDefaults(service::FieldParams&& params, DefaultInput&& inputArg) const override
{
if constexpr (methods::QueryHas::getInputDefaultsWithParams<T>)
{
return { _pimpl->getInputDefaults(std::move(params), std::move(inputArg)) };
}
else if constexpr (methods::QueryHas::getInputDefaults<T>)
{
return { _pimpl->getInputDefaults(std::move(inputArg)) };
}
else
{
throw service::unimplemented_method(R"ex(Query::getInputDefaults)ex");
}
}

void beginSelectionSet(const service::SelectionSetParams& params) const override
{
if constexpr (methods::QueryHas::beginSelectionSet<T>)
Expand Down
13 changes: 13 additions & 0 deletions samples/validation/schema/ValidationSchema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ void AddTypesToSchema(const std::shared_ptr<schema::Schema>& schema)
schema->AddType(R"gql(CatCommand)gql"sv, typeCatCommand);
auto typeComplexInput = schema::InputObjectType::Make(R"gql(ComplexInput)gql"sv, R"md()md"sv);
schema->AddType(R"gql(ComplexInput)gql"sv, typeComplexInput);
auto typeDefaultInput = schema::InputObjectType::Make(R"gql(DefaultInput)gql"sv, R"md()md"sv);
schema->AddType(R"gql(DefaultInput)gql"sv, typeDefaultInput);
auto typeNestedDefaultInput = schema::InputObjectType::Make(R"gql(NestedDefaultInput)gql"sv, R"md()md"sv);
schema->AddType(R"gql(NestedDefaultInput)gql"sv, typeNestedDefaultInput);
auto typeSentient = schema::InterfaceType::Make(R"gql(Sentient)gql"sv, R"md()md"sv);
schema->AddType(R"gql(Sentient)gql"sv, typeSentient);
auto typePet = schema::InterfaceType::Make(R"gql(Pet)gql"sv, R"md()md"sv);
Expand Down Expand Up @@ -94,6 +98,15 @@ void AddTypesToSchema(const std::shared_ptr<schema::Schema>& schema)
schema::InputValue::Make(R"gql(name)gql"sv, R"md()md"sv, schema->LookupType(R"gql(String)gql"sv), R"gql()gql"sv),
schema::InputValue::Make(R"gql(owner)gql"sv, R"md()md"sv, schema->LookupType(R"gql(String)gql"sv), R"gql()gql"sv)
});
typeDefaultInput->AddInputValues({
schema::InputValue::Make(R"gql(values)gql"sv, R"md()md"sv, schema->WrapType(introspection::TypeKind::LIST, schema->WrapType(introspection::TypeKind::NON_NULL, schema->LookupType(R"gql(Int)gql"sv))), R"gql([])gql"sv),
schema::InputValue::Make(R"gql(count)gql"sv, R"md()md"sv, schema->LookupType(R"gql(Int)gql"sv), R"gql(42)gql"sv),
schema::InputValue::Make(R"gql(child)gql"sv, R"md()md"sv, schema->LookupType(R"gql(NestedDefaultInput)gql"sv), R"gql({})gql"sv),
schema::InputValue::Make(R"gql(required)gql"sv, R"md()md"sv, schema->WrapType(introspection::TypeKind::NON_NULL, schema->LookupType(R"gql(Int)gql"sv)), R"gql(9)gql"sv)
});
typeNestedDefaultInput->AddInputValues({
schema::InputValue::Make(R"gql(count)gql"sv, R"md()md"sv, schema->LookupType(R"gql(Int)gql"sv), R"gql(7)gql"sv)
});

AddSentientDetails(typeSentient, schema);
AddPetDetails(typePet, schema);
Expand Down
187 changes: 187 additions & 0 deletions samples/validation/schema/ValidationSharedTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,87 @@ validation::ComplexInput Argument<validation::ComplexInput>::convert(const respo
};
}

template <>
validation::DefaultInput Argument<validation::DefaultInput>::convert(const response::Value& value)
{
const auto defaultValue = []()
{
response::Value values(response::Type::Map);
response::Value entry;

entry = []()
{
response::Value elements(response::Type::List);
response::Value entry;

return elements;
}();
values.emplace_back("values", std::move(entry));
entry = response::Value(static_cast<int>(42));
values.emplace_back("count", std::move(entry));
entry = []()
{
response::Value members(response::Type::Map);
response::Value entry;

return members;
}();
values.emplace_back("child", std::move(entry));
entry = response::Value(static_cast<int>(9));
values.emplace_back("required", std::move(entry));

return values;
}();

auto pairValues = service::ModifiedArgument<int>::find<service::TypeModifier::Nullable, service::TypeModifier::List>("values", value);
auto valueValues = (pairValues.second
? std::move(pairValues.first)
: service::ModifiedArgument<int>::require<service::TypeModifier::Nullable, service::TypeModifier::List>("values", defaultValue));
auto pairCount = service::ModifiedArgument<int>::find<service::TypeModifier::Nullable>("count", value);
auto valueCount = (pairCount.second
? std::move(pairCount.first)
: service::ModifiedArgument<int>::require<service::TypeModifier::Nullable>("count", defaultValue));
auto pairChild = service::ModifiedArgument<validation::NestedDefaultInput>::find<service::TypeModifier::Nullable>("child", value);
auto valueChild = (pairChild.second
? std::move(pairChild.first)
: service::ModifiedArgument<validation::NestedDefaultInput>::require<service::TypeModifier::Nullable>("child", defaultValue));
auto pairRequired = service::ModifiedArgument<int>::find("required", value);
auto valueRequired = (pairRequired.second
? pairRequired.first
: service::ModifiedArgument<int>::require("required", defaultValue));

return validation::DefaultInput {
std::move(valueValues),
std::move(valueCount),
std::move(valueChild),
valueRequired
};
}

template <>
validation::NestedDefaultInput Argument<validation::NestedDefaultInput>::convert(const response::Value& value)
{
const auto defaultValue = []()
{
response::Value values(response::Type::Map);
response::Value entry;

entry = response::Value(static_cast<int>(7));
values.emplace_back("count", std::move(entry));

return values;
}();

auto pairCount = service::ModifiedArgument<int>::find<service::TypeModifier::Nullable>("count", value);
auto valueCount = (pairCount.second
? std::move(pairCount.first)
: service::ModifiedArgument<int>::require<service::TypeModifier::Nullable>("count", defaultValue));

return validation::NestedDefaultInput {
std::move(valueCount)
};
}

} // namespace service

namespace validation {
Expand Down Expand Up @@ -204,5 +285,111 @@ ComplexInput& ComplexInput::operator=(ComplexInput&& other) noexcept
return *this;
}


DefaultInput::DefaultInput() noexcept
: values {}
, count {}
, child {}
, required {}
{
// Explicit definition to prevent ODR violations when LTO is enabled.
}

DefaultInput::DefaultInput(
std::optional<std::vector<int>> valuesArg,
std::optional<int> countArg,
std::unique_ptr<NestedDefaultInput> childArg,
int requiredArg) noexcept
: values { std::move(valuesArg) }
, count { std::move(countArg) }
, child { std::move(childArg) }
, required { std::move(requiredArg) }
{
}

DefaultInput::DefaultInput(const DefaultInput& other)
: values { service::ModifiedArgument<int>::duplicate<service::TypeModifier::Nullable, service::TypeModifier::List>(other.values) }
, count { service::ModifiedArgument<int>::duplicate<service::TypeModifier::Nullable>(other.count) }
, child { service::ModifiedArgument<NestedDefaultInput>::duplicate<service::TypeModifier::Nullable>(other.child) }
, required { service::ModifiedArgument<int>::duplicate(other.required) }
{
}

DefaultInput::DefaultInput(DefaultInput&& other) noexcept
: values { std::move(other.values) }
, count { std::move(other.count) }
, child { std::move(other.child) }
, required { std::move(other.required) }
{
}

DefaultInput::~DefaultInput()
{
// Explicit definition to prevent ODR violations when LTO is enabled.
}

DefaultInput& DefaultInput::operator=(const DefaultInput& other)
{
DefaultInput value { other };

std::swap(*this, value);

return *this;
}

DefaultInput& DefaultInput::operator=(DefaultInput&& other) noexcept
{
values = std::move(other.values);
count = std::move(other.count);
child = std::move(other.child);
required = std::move(other.required);

return *this;
}


NestedDefaultInput::NestedDefaultInput() noexcept
: count {}
{
// Explicit definition to prevent ODR violations when LTO is enabled.
}

NestedDefaultInput::NestedDefaultInput(
std::optional<int> countArg) noexcept
: count { std::move(countArg) }
{
}

NestedDefaultInput::NestedDefaultInput(const NestedDefaultInput& other)
: count { service::ModifiedArgument<int>::duplicate<service::TypeModifier::Nullable>(other.count) }
{
}

NestedDefaultInput::NestedDefaultInput(NestedDefaultInput&& other) noexcept
: count { std::move(other.count) }
{
}

NestedDefaultInput::~NestedDefaultInput()
{
// Explicit definition to prevent ODR violations when LTO is enabled.
}

NestedDefaultInput& NestedDefaultInput::operator=(const NestedDefaultInput& other)
{
NestedDefaultInput value { other };

std::swap(*this, value);

return *this;
}

NestedDefaultInput& NestedDefaultInput::operator=(NestedDefaultInput&& other) noexcept
{
count = std::move(other.count);

return *this;
}

} // namespace validation
} // namespace graphql
Loading