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
changelog
juniper_codegen
juniper_tests/src/codegen
|
@ -7,6 +7,11 @@
|
||||||
|
|
||||||
[#151](https://github.com/graphql-rust/juniper/pull/151)
|
[#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,
|
* Large integers (> signed 32bit) are now deserialized as floats. Previously,
|
||||||
they produced the "integer out of range" error. For languages that do not
|
they produced the "integer out of range" error. For languages that do not
|
||||||
have distinction between integer and floating point types (including
|
have distinction between integer and floating point types (including
|
||||||
|
@ -14,5 +19,4 @@
|
||||||
fractional part could not be decoded (because they are represented without
|
fractional part could not be decoded (because they are represented without
|
||||||
a decimal part `.0`).
|
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]
|
[dependencies]
|
||||||
syn = { version = "0.13.*", features = ["full", "extra-traits"] }
|
syn = { version = "0.13.*", features = ["full", "extra-traits"] }
|
||||||
quote = "0.5.*"
|
quote = "0.5.*"
|
||||||
|
regex = "0.2.10"
|
||||||
|
lazy_static = "1.0.0"
|
||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
travis-ci = { repository = "graphql-rust/juniper" }
|
travis-ci = { repository = "graphql-rust/juniper" }
|
||||||
|
|
|
@ -32,8 +32,13 @@ impl EnumAttrs {
|
||||||
if let Some(items) = get_graphl_attr(&input.attrs) {
|
if let Some(items) = get_graphl_attr(&input.attrs) {
|
||||||
for item in items {
|
for item in items {
|
||||||
if let Some(val) = keyed_item_value(&item, "name", true) {
|
if let Some(val) = keyed_item_value(&item, "name", true) {
|
||||||
res.name = Some(val);
|
if is_valid_name(&*val) {
|
||||||
continue;
|
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) {
|
if let Some(val) = keyed_item_value(&item, "description", true) {
|
||||||
res.description = Some(val);
|
res.description = Some(val);
|
||||||
|
@ -73,8 +78,13 @@ impl EnumVariantAttrs {
|
||||||
if let Some(items) = get_graphl_attr(&variant.attrs) {
|
if let Some(items) = get_graphl_attr(&variant.attrs) {
|
||||||
for item in items {
|
for item in items {
|
||||||
if let Some(val) = keyed_item_value(&item, "name", true) {
|
if let Some(val) = keyed_item_value(&item, "name", true) {
|
||||||
res.name = Some(val);
|
if is_valid_name(&*val) {
|
||||||
continue;
|
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) {
|
if let Some(val) = keyed_item_value(&item, "description", true) {
|
||||||
res.description = Some(val);
|
res.description = Some(val);
|
||||||
|
|
|
@ -29,8 +29,13 @@ impl ObjAttrs {
|
||||||
if let Some(items) = get_graphl_attr(&input.attrs) {
|
if let Some(items) = get_graphl_attr(&input.attrs) {
|
||||||
for item in items {
|
for item in items {
|
||||||
if let Some(val) = keyed_item_value(&item, "name", true) {
|
if let Some(val) = keyed_item_value(&item, "name", true) {
|
||||||
res.name = Some(val);
|
if is_valid_name(&*val) {
|
||||||
continue;
|
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) {
|
if let Some(val) = keyed_item_value(&item, "description", true) {
|
||||||
res.description = Some(val);
|
res.description = Some(val);
|
||||||
|
@ -71,8 +76,13 @@ impl ObjFieldAttrs {
|
||||||
if let Some(items) = get_graphl_attr(&variant.attrs) {
|
if let Some(items) = get_graphl_attr(&variant.attrs) {
|
||||||
for item in items {
|
for item in items {
|
||||||
if let Some(val) = keyed_item_value(&item, "name", true) {
|
if let Some(val) = keyed_item_value(&item, "name", true) {
|
||||||
res.name = Some(val);
|
if is_valid_name(&*val) {
|
||||||
continue;
|
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) {
|
if let Some(val) = keyed_item_value(&item, "description", true) {
|
||||||
res.description = Some(val);
|
res.description = Some(val);
|
||||||
|
|
|
@ -23,8 +23,13 @@ impl ObjAttrs {
|
||||||
if let Some(items) = get_graphl_attr(&input.attrs) {
|
if let Some(items) = get_graphl_attr(&input.attrs) {
|
||||||
for item in items {
|
for item in items {
|
||||||
if let Some(val) = keyed_item_value(&item, "name", true) {
|
if let Some(val) = keyed_item_value(&item, "name", true) {
|
||||||
res.name = Some(val);
|
if is_valid_name(&*val) {
|
||||||
continue;
|
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) {
|
if let Some(val) = keyed_item_value(&item, "description", true) {
|
||||||
res.description = Some(val);
|
res.description = Some(val);
|
||||||
|
@ -55,8 +60,13 @@ impl ObjFieldAttrs {
|
||||||
if let Some(items) = get_graphl_attr(&variant.attrs) {
|
if let Some(items) = get_graphl_attr(&variant.attrs) {
|
||||||
for item in items {
|
for item in items {
|
||||||
if let Some(val) = keyed_item_value(&item, "name", true) {
|
if let Some(val) = keyed_item_value(&item, "name", true) {
|
||||||
res.name = Some(val);
|
if is_valid_name(&*val) {
|
||||||
continue;
|
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) {
|
if let Some(val) = keyed_item_value(&item, "description", true) {
|
||||||
res.description = Some(val);
|
res.description = Some(val);
|
||||||
|
|
|
@ -10,6 +10,9 @@ extern crate proc_macro;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate quote;
|
extern crate quote;
|
||||||
extern crate syn;
|
extern crate syn;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate lazy_static;
|
||||||
|
extern crate regex;
|
||||||
|
|
||||||
mod util;
|
mod util;
|
||||||
mod derive_enum;
|
mod derive_enum;
|
||||||
|
|
|
@ -4,6 +4,7 @@ use syn::{
|
||||||
NestedMeta,
|
NestedMeta,
|
||||||
Lit,
|
Lit,
|
||||||
};
|
};
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
// Get the nested items of a a #[graphql(...)] attribute.
|
// Get the nested items of a a #[graphql(...)] attribute.
|
||||||
pub fn get_graphl_attr(attrs: &Vec<Attribute>) -> Option<Vec<NestedMeta>> {
|
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("someINpuT"), "SOME_INPU_T");
|
||||||
assert_eq!(to_upper_snake_case("some_INpuT"), "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")]
|
#[graphql(name = "Some", description = "enum descr")]
|
||||||
enum SomeEnum {
|
enum SomeEnum {
|
||||||
Regular,
|
Regular,
|
||||||
|
#[graphql(name = "FULL", description = "field descr", deprecated = "depr")] Full,
|
||||||
#[graphql(name = "full", description = "field descr", deprecated = "depr")] Full,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -37,10 +36,10 @@ fn test_derived_enum() {
|
||||||
// Test FULL variant.
|
// Test FULL variant.
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
SomeEnum::Full.to_input_value(),
|
SomeEnum::Full.to_input_value(),
|
||||||
InputValue::String("full".into())
|
InputValue::String("FULL".into())
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
FromInputValue::from_input_value(&InputValue::String("full".into())),
|
FromInputValue::from_input_value(&InputValue::String("FULL".into())),
|
||||||
Some(SomeEnum::Full)
|
Some(SomeEnum::Full)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue