Add Compile Time Check For "Invalid" Names (#170)

This commit is contained in:
piperRyan 2018-05-23 01:25:20 -06:00 committed by Christian Legnitto
parent 69db4c247b
commit 9080448da2
8 changed files with 78 additions and 18 deletions

View file

@ -7,6 +7,11 @@
[#151](https://github.com/graphql-rust/juniper/pull/151)
* The `GraphQLObject`, `GraphQLInputObject`, and `GraphQLEnum` custom derives will reject
invalid [names](http://facebook.github.io/graphql/October2016/#Name) at compile time.
[#170](https://github.com/graphql-rust/juniper/pull/170)
* Large integers (> signed 32bit) are now deserialized as floats. Previously,
they produced the "integer out of range" error. For languages that do not
have distinction between integer and floating point types (including
@ -14,5 +19,4 @@
fractional part could not be decoded (because they are represented without
a decimal part `.0`).
[#179](https://github.com/graphql-rust/juniper/pull/179)
[#179](https://github.com/graphql-rust/juniper/pull/179)

View file

@ -16,6 +16,8 @@ proc-macro = true
[dependencies]
syn = { version = "0.13.*", features = ["full", "extra-traits"] }
quote = "0.5.*"
regex = "0.2.10"
lazy_static = "1.0.0"
[badges]
travis-ci = { repository = "graphql-rust/juniper" }

View file

@ -32,8 +32,13 @@ impl EnumAttrs {
if let Some(items) = get_graphl_attr(&input.attrs) {
for item in items {
if let Some(val) = keyed_item_value(&item, "name", true) {
res.name = Some(val);
continue;
if is_valid_name(&*val) {
res.name = Some(val);
continue;
} else {
panic!("Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but \"{}\" does not",
&*val);
}
}
if let Some(val) = keyed_item_value(&item, "description", true) {
res.description = Some(val);
@ -73,8 +78,13 @@ impl EnumVariantAttrs {
if let Some(items) = get_graphl_attr(&variant.attrs) {
for item in items {
if let Some(val) = keyed_item_value(&item, "name", true) {
res.name = Some(val);
continue;
if is_valid_name(&*val) {
res.name = Some(val);
continue;
} else {
panic!("Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but \"{}\" does not",
&*val);
}
}
if let Some(val) = keyed_item_value(&item, "description", true) {
res.description = Some(val);

View file

@ -29,8 +29,13 @@ impl ObjAttrs {
if let Some(items) = get_graphl_attr(&input.attrs) {
for item in items {
if let Some(val) = keyed_item_value(&item, "name", true) {
res.name = Some(val);
continue;
if is_valid_name(&*val) {
res.name = Some(val);
continue;
} else {
panic!("Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but \"{}\" does not",
&*val);
}
}
if let Some(val) = keyed_item_value(&item, "description", true) {
res.description = Some(val);
@ -71,8 +76,13 @@ impl ObjFieldAttrs {
if let Some(items) = get_graphl_attr(&variant.attrs) {
for item in items {
if let Some(val) = keyed_item_value(&item, "name", true) {
res.name = Some(val);
continue;
if is_valid_name(&*val) {
res.name = Some(val);
continue;
} else {
panic!("Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but \"{}\" does not",
&*val);
}
}
if let Some(val) = keyed_item_value(&item, "description", true) {
res.description = Some(val);

View file

@ -23,8 +23,13 @@ impl ObjAttrs {
if let Some(items) = get_graphl_attr(&input.attrs) {
for item in items {
if let Some(val) = keyed_item_value(&item, "name", true) {
res.name = Some(val);
continue;
if is_valid_name(&*val) {
res.name = Some(val);
continue;
} else {
panic!("Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but \"{}\" does not",
&*val);
}
}
if let Some(val) = keyed_item_value(&item, "description", true) {
res.description = Some(val);
@ -55,8 +60,13 @@ impl ObjFieldAttrs {
if let Some(items) = get_graphl_attr(&variant.attrs) {
for item in items {
if let Some(val) = keyed_item_value(&item, "name", true) {
res.name = Some(val);
continue;
if is_valid_name(&*val) {
res.name = Some(val);
continue;
} else {
panic!("Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but \"{}\" does not",
&*val);
}
}
if let Some(val) = keyed_item_value(&item, "description", true) {
res.description = Some(val);

View file

@ -10,6 +10,9 @@ extern crate proc_macro;
#[macro_use]
extern crate quote;
extern crate syn;
#[macro_use]
extern crate lazy_static;
extern crate regex;
mod util;
mod derive_enum;

View file

@ -4,6 +4,7 @@ use syn::{
NestedMeta,
Lit,
};
use regex::Regex;
// Get the nested items of a a #[graphql(...)] attribute.
pub fn get_graphl_attr(attrs: &Vec<Attribute>) -> Option<Vec<NestedMeta>> {
@ -111,3 +112,24 @@ fn test_to_upper_snake_case() {
assert_eq!(to_upper_snake_case("someINpuT"), "SOME_INPU_T");
assert_eq!(to_upper_snake_case("some_INpuT"), "SOME_INPU_T");
}
#[doc(hidden)]
pub fn is_valid_name(field_name: &str) -> bool {
lazy_static!{
static ref CAMELCASE: Regex = Regex::new("^[_A-Za-z][_0-9A-Za-z]*$").unwrap();
}
CAMELCASE.is_match(field_name)
}
#[test]
fn test_is_valid_name(){
assert_eq!(is_valid_name("yesItIs"), true);
assert_eq!(is_valid_name("NoitIsnt"), true);
assert_eq!(is_valid_name("iso6301"), true);
assert_eq!(is_valid_name("thisIsATest"), true);
assert_eq!(is_valid_name("i6Op"), true);
assert_eq!(is_valid_name("i!"), false);
assert_eq!(is_valid_name(""), false);
assert_eq!(is_valid_name("aTest"), true);
assert_eq!(is_valid_name("__Atest90"), true);
}

View file

@ -8,8 +8,7 @@ use juniper::{self, FromInputValue, GraphQLType, InputValue, ToInputValue};
#[graphql(name = "Some", description = "enum descr")]
enum SomeEnum {
Regular,
#[graphql(name = "full", description = "field descr", deprecated = "depr")] Full,
#[graphql(name = "FULL", description = "field descr", deprecated = "depr")] Full,
}
#[test]
@ -37,10 +36,10 @@ fn test_derived_enum() {
// Test FULL variant.
assert_eq!(
SomeEnum::Full.to_input_value(),
InputValue::String("full".into())
InputValue::String("FULL".into())
);
assert_eq!(
FromInputValue::from_input_value(&InputValue::String("full".into())),
FromInputValue::from_input_value(&InputValue::String("FULL".into())),
Some(SomeEnum::Full)
);
}