Add Compile Time Check For "Invalid" Names (#170)
This commit is contained in:
parent
69db4c247b
commit
9080448da2
8 changed files with 78 additions and 18 deletions
|
@ -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)
|
|
@ -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" }
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue