Skip to content

Commit

Permalink
implement RescriptRelay dedicated directive for allowing unsafe enums
Browse files Browse the repository at this point in the history
  • Loading branch information
zth committed May 8, 2022
1 parent 49618af commit 416be1b
Show file tree
Hide file tree
Showing 8 changed files with 241 additions and 26 deletions.
9 changes: 8 additions & 1 deletion compiler/crates/relay-schema/src/relay-extensions.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -263,4 +263,11 @@ directive @alias(as: String) on FRAGMENT_SPREAD | INLINE_FRAGMENT
Tells the RescriptRelay CLI that it's fine to ignore unused fields in this fragment.
"""
directive @rescriptRelayIgnoreUnused on FRAGMENT_DEFINITION
directive @rescriptRelayIgnoreUnused on FRAGMENT_DEFINITION

"""
(RescriptRelay only)
Turns off safety features if this is an enum.
"""
directive @rescriptRelayAllowUnsafeEnum on FIELD
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* LICENSE file in the root directory of this source tree.
*/

use std::sync::Arc;

use lazy_static::lazy_static;

use graphql_ir::{
Expand All @@ -22,6 +24,8 @@ pub fn rescript_relay_remove_custom_directives(program: &Program) -> Program {

lazy_static! {
static ref FRAGMENT_DIRECTIVE_IGNORE_UNUSED: StringKey = "rescriptRelayIgnoreUnused".intern();
static ref FIELD_DIRECTIVE_ALLOW_UNSAFE_ENUM: StringKey =
"rescriptRelayAllowUnsafeEnum".intern();
}

#[allow(dead_code)]
Expand Down Expand Up @@ -76,8 +80,21 @@ impl<'s> Transformer for RescriptRelayRemoveCustomDirectivesTransform<'s> {
Transformed::Keep
}

fn transform_scalar_field(&mut self, _field: &ScalarField) -> Transformed<Selection> {
Transformed::Keep
fn transform_scalar_field(&mut self, field: &ScalarField) -> Transformed<Selection> {
Transformed::Replace(Selection::ScalarField(Arc::new(ScalarField {
directives: field
.directives
.iter()
.filter_map(|directive| {
if directive.name.item == *FIELD_DIRECTIVE_ALLOW_UNSAFE_ENUM {
None
} else {
Some(directive.to_owned())
}
})
.collect::<Vec<Directive>>(),
..field.clone()
})))
}

fn transform_fragment_spread(&mut self, _spread: &FragmentSpread) -> Transformed<Selection> {
Expand Down
79 changes: 59 additions & 20 deletions compiler/crates/relay-typegen/src/rescript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -727,28 +727,41 @@ fn get_object_prop_type_as_string(
prop_value: &PropType,
context: &Context,
indentation: usize,
field_path_name: &Vec<String>,
) -> String {
match &prop_value {
&PropType::DataId => String::from("RescriptRelay.dataId"),
&PropType::Enum(enum_name) => match &context {
Context::Variables | Context::RawResponse | Context::RootObject(_) => {
match state
.enums
.iter()
.find(|full_enum| full_enum.name == enum_name.to_string())
{
None => {
warn!("Did not find enum");
String::from("invalid_enum")
&PropType::Enum(enum_name) => {
let has_allow_unsafe_enum_directive = state
.operation_meta_data
.field_directives
.iter()
.find(|field_directive| {
field_directive.at_object_path.join("__") == field_path_name.join("__")
})
.is_some();

match (has_allow_unsafe_enum_directive, &context) {
(true, _)
| (false, Context::Variables | Context::RawResponse | Context::RootObject(_)) => {
match state
.enums
.iter()
.find(|full_enum| full_enum.name == enum_name.to_string())
{
None => {
warn!("Did not find enum");
String::from("invalid_enum")
}
Some(full_enum) => format!(
"{}",
get_enum_definition_body(full_enum, indentation, false)
),
}
Some(full_enum) => format!(
"{}",
get_enum_definition_body(full_enum, indentation, false)
),
}
_ => format!("enum_{}", enum_name),
}
_ => format!("enum_{}", enum_name),
},
}
&PropType::StringLiteral(literal) => format!("[ | #{}]", literal),
&PropType::InputObjectReference(input_object_name) => input_object_name.to_string(),
&PropType::RecordReference(record_name) => record_name.to_string(),
Expand Down Expand Up @@ -780,7 +793,8 @@ fn get_object_prop_type_as_string(
state,
inner_list_type.as_ref(),
&context,
indentation
indentation,
field_path_name
),
)
.unwrap();
Expand Down Expand Up @@ -859,6 +873,9 @@ fn write_object_maker_as_external(
let mut has_nullable = false;

definition.values.iter().for_each(|prop_value| {
let mut field_path_name = definition.at_path.clone();
field_path_name.push(prop_value.key.to_owned());

write_indentation(str, indentation + 1).unwrap();
write!(
str,
Expand All @@ -869,6 +886,7 @@ fn write_object_maker_as_external(
&prop_value.prop_type,
&Context::Variables,
indentation,
&field_path_name
)
)
.unwrap();
Expand Down Expand Up @@ -1645,6 +1663,9 @@ fn write_object_definition(
has_printed_keys.insert(&prop.key);
}

let mut field_path_name = object.at_path.clone();
field_path_name.push(prop.key.to_owned());

write_indentation(str, in_object_indentation).unwrap();
writeln!(
str,
Expand Down Expand Up @@ -1680,15 +1701,33 @@ fn write_object_definition(
match (prop.nullable, is_refetch_var) {
(true, true) => format!(
"option<option<{}>>",
get_object_prop_type_as_string(state, &prop.prop_type, &context, indentation)
get_object_prop_type_as_string(
state,
&prop.prop_type,
&context,
indentation,
&field_path_name
)
),
(true, false) | (false, true) => format!(
"option<{}>",
get_object_prop_type_as_string(state, &prop.prop_type, &context, indentation)
get_object_prop_type_as_string(
state,
&prop.prop_type,
&context,
indentation,
&field_path_name
)
),
(false, false) => format!(
"{}",
get_object_prop_type_as_string(state, &prop.prop_type, &context, indentation)
get_object_prop_type_as_string(
state,
&prop.prop_type,
&context,
indentation,
&field_path_name
)
),
}
)
Expand Down
26 changes: 26 additions & 0 deletions compiler/crates/relay-typegen/src/rescript_relay_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,24 @@ pub enum RescriptRelayFragmentDirective {
IgnoreUnused,
}

#[derive(Debug, PartialEq, Eq)]
pub enum RescriptRelayFieldDirective {
AllowUnsafeEnum,
}

#[derive(Debug, PartialEq, Eq)]
pub struct FieldDirectiveContainer {
pub at_object_path: Vec<String>,
pub directive: RescriptRelayFieldDirective,
}

#[derive(Debug)]
pub struct RescriptRelayOperationMetaData {
pub connection_config: Option<RescriptRelayConnectionConfig>,
pub variables_with_connection_data_ids: Vec<String>,
pub custom_scalars: IndexMap<StringKey, StringKey, FnvBuildHasher>,
pub fragment_directives: Vec<RescriptRelayFragmentDirective>,
pub field_directives: Vec<FieldDirectiveContainer>,
}

pub struct RescriptRelayVisitor<'a> {
Expand Down Expand Up @@ -62,6 +74,8 @@ lazy_static! {
static ref PREPEND_EDGE: StringKey = "prependEdge".intern();
static ref PREPEND_NODE: StringKey = "prependNode".intern();
static ref FRAGMENT_DIRECTIVE_IGNORE_UNUSED: StringKey = "rescriptRelayIgnoreUnused".intern();
static ref FIELD_DIRECTIVE_ALLOW_UNSAFE_ENUM: StringKey =
"rescriptRelayAllowUnsafeEnum".intern();
}

fn find_connections_arguments(directive: Option<&Directive>) -> Vec<String> {
Expand Down Expand Up @@ -124,6 +138,18 @@ impl<'a> Visitor for RescriptRelayVisitor<'a> {
.push(variable_name.to_string())
});

field.directives.iter().for_each(|directive| {
if directive.name.item == *FIELD_DIRECTIVE_ALLOW_UNSAFE_ENUM {
let mut at_object_path = self.current_path.clone();
at_object_path.push(field.alias_or_name(self.schema).to_string());

self.state.field_directives.push(FieldDirectiveContainer {
directive: RescriptRelayFieldDirective::AllowUnsafeEnum,
at_object_path,
})
}
});

self.default_visit_scalar_field(field)
}

Expand Down
1 change: 1 addition & 0 deletions compiler/crates/relay-typegen/src/rescript_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ pub fn get_rescript_relay_meta_data(
variables_with_connection_data_ids: vec![],
custom_scalars: typegen_config.custom_scalar_types.clone(),
fragment_directives: vec![],
field_directives: vec![],
};

match &typegen_definition {
Expand Down
9 changes: 9 additions & 0 deletions compiler/test-project-res/src/Test_fragment.res
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module Query = %relay(`
...TestFragment_user
...TestFragment_inline
...TestFragment_ignoreUnused
...TestFragment_allowUnsafeEnum
}
users {
edges {
Expand Down Expand Up @@ -98,3 +99,11 @@ module IgnoreUnusedFragment = %relay(`
onlineStatus
}
`)

module AllowUnsafeEnumFragment = %relay(`
fragment TestFragment_allowUnsafeEnum on User {
firstName
lastName
onlineStatus @rescriptRelayAllowUnsafeEnum
}
`)

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 416be1b

Please sign in to comment.