Skip to content

Commit ecadebb

Browse files
committed
Flag string values from JSON as potential enum values
1 parent 8fd4dd8 commit ecadebb

File tree

7 files changed

+74
-7
lines changed

7 files changed

+74
-7
lines changed

GraphQLResponse.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,14 @@ Value::Value(Value&& other) noexcept
8080
, _map(std::move(other._map))
8181
, _list(std::move(other._list))
8282
, _string(std::move(other._string))
83+
, _from_json(other._from_json)
8384
, _scalar(std::move(other._scalar))
8485
, _boolean(other._boolean)
8586
, _int(other._int)
8687
, _float(other._float)
8788
{
8889
const_cast<Type&>(other._type) = Type::Null;
90+
other._from_json = false;
8991
other._boolean = false;
9092
other._int = 0;
9193
other._float = 0.0;
@@ -131,6 +133,8 @@ Value& Value::operator=(Value&& rhs) noexcept
131133
_map = std::move(rhs._map);
132134
_list = std::move(rhs._list);
133135
_string = std::move(rhs._string);
136+
_from_json = rhs._from_json;
137+
rhs._from_json = false;
134138
_scalar = std::move(rhs._scalar);
135139
_boolean = rhs._boolean;
136140
rhs._boolean = false;
@@ -186,11 +190,24 @@ bool Value::operator!=(const Value& rhs) const noexcept
186190
return !(*this == rhs);
187191
}
188192

189-
Type Value::type() const
193+
Type Value::type() const noexcept
190194
{
191195
return _type;
192196
}
193197

198+
Value&& Value::from_json() noexcept
199+
{
200+
_from_json = true;
201+
202+
return std::move(*this);
203+
}
204+
205+
bool Value::maybe_enum() const noexcept
206+
{
207+
return _type == Type::EnumValue
208+
|| (_from_json && _type == Type::String);
209+
}
210+
194211
void Value::reserve(size_t count)
195212
{
196213
switch (_type)

JSONResponse.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ struct ResponseHandler
185185

186186
bool String(const Ch* str, rapidjson::SizeType /*length*/, bool /*copy*/)
187187
{
188-
setValue(Value(std::string(str)));
188+
setValue(Value(std::string(str)).from_json());
189189
return true;
190190
}
191191

SchemaGenerator.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1623,7 +1623,7 @@ template <>
16231623
sourceFile << R"cpp(
16241624
};
16251625
1626-
if (value.type() != response::Type::EnumValue)
1626+
if (!value.maybe_enum())
16271627
{
16281628
throw service::schema_exception({ "not a valid )cpp" << enumType.type << R"cpp( value" });
16291629
}

include/graphqlservice/GraphQLResponse.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,12 @@ struct Value
6161
bool operator!=(const Value& rhs) const noexcept;
6262

6363
// Check the Type
64-
Type type() const;
64+
Type type() const noexcept;
65+
66+
// JSON doesn't distinguish between Type::String and Type::EnumValue, so if this value comes
67+
// from JSON and it's a string we need to track the fact that it can be interpreted as either.
68+
Value&& from_json() noexcept;
69+
bool maybe_enum() const noexcept;
6570

6671
// Valid for Type::Map or Type::List
6772
void reserve(size_t count);
@@ -102,6 +107,7 @@ struct Value
102107

103108
// Type::String or Type::EnumValue
104109
std::unique_ptr<StringType> _string;
110+
bool _from_json = false;
105111

106112
// Type::Boolean
107113
BooleanType _boolean = false;

samples/IntrospectionSchema.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ introspection::__TypeKind ModifiedArgument<introspection::__TypeKind>::convert(c
2727
{ "NON_NULL", introspection::__TypeKind::NON_NULL }
2828
};
2929

30-
if (value.type() != response::Type::EnumValue)
30+
if (!value.maybe_enum())
3131
{
3232
throw service::schema_exception({ "not a valid __TypeKind value" });
3333
}
@@ -89,7 +89,7 @@ introspection::__DirectiveLocation ModifiedArgument<introspection::__DirectiveLo
8989
{ "INPUT_FIELD_DEFINITION", introspection::__DirectiveLocation::INPUT_FIELD_DEFINITION }
9090
};
9191

92-
if (value.type() != response::Type::EnumValue)
92+
if (!value.maybe_enum())
9393
{
9494
throw service::schema_exception({ "not a valid __DirectiveLocation value" });
9595
}

samples/TodaySchema.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ today::TaskState ModifiedArgument<today::TaskState>::convert(const response::Val
2525
{ "Unassigned", today::TaskState::Unassigned }
2626
};
2727

28-
if (value.type() != response::Type::EnumValue)
28+
if (!value.maybe_enum())
2929
{
3030
throw service::schema_exception({ "not a valid TaskState value" });
3131
}

tests.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1242,6 +1242,50 @@ TEST(ArgumentsCase, TaskStateEnum)
12421242
EXPECT_EQ(today::TaskState::Started, actual) << "should parse the enum";
12431243
}
12441244

1245+
TEST(ArgumentsCase, TaskStateEnumFromString)
1246+
{
1247+
response::Value response(response::Type::Map);
1248+
response::Value status("Started");
1249+
response.emplace_back("status", std::move(status));
1250+
today::TaskState actual = static_cast<today::TaskState>(-1);
1251+
bool caughtException = false;
1252+
std::string exceptionWhat;
1253+
1254+
try
1255+
{
1256+
actual = service::ModifiedArgument<today::TaskState>::require("status", response);
1257+
}
1258+
catch (const service::schema_exception& ex)
1259+
{
1260+
caughtException = true;
1261+
exceptionWhat = response::toJSON(response::Value(ex.getErrors()));
1262+
}
1263+
1264+
EXPECT_NE(today::TaskState::Started, actual) << "should not parse the enum from a known string value";
1265+
ASSERT_TRUE(caughtException);
1266+
EXPECT_EQ(R"js([{"message":"Invalid argument: status message: not a valid TaskState value"}])js", exceptionWhat) << "exception should match";
1267+
}
1268+
1269+
TEST(ArgumentsCase, TaskStateEnumFromJSONString)
1270+
{
1271+
response::Value response(response::Type::Map);
1272+
response::Value status("Started");
1273+
response.emplace_back("status", status.from_json());
1274+
today::TaskState actual = static_cast<today::TaskState>(-1);
1275+
1276+
1277+
try
1278+
{
1279+
actual = service::ModifiedArgument<today::TaskState>::require("status", response);
1280+
}
1281+
catch (const service::schema_exception& ex)
1282+
{
1283+
FAIL() << response::toJSON(response::Value(ex.getErrors()));
1284+
}
1285+
1286+
EXPECT_EQ(today::TaskState::Started, actual) << "should parse the enum";
1287+
}
1288+
12451289
TEST(PegtlCase, ParseKitchenSinkQuery)
12461290
{
12471291
memory_input<> input(R"gql(

0 commit comments

Comments
 (0)