Skip to content

Commit 970c94a

Browse files
Alex Danofffacebook-github-bot
Alex Danoff
authored andcommitted
clean up client_edges transform code
Summary: While trying to understand this code I found `transform_linked_field_impl` to be too complex to follow easily. This diff aims to simplify it by splitting up the function into some named helper functions which are each more focused. No functionality should be changed by this diff. : Refactoring code in this file to make it easier to follow. Reviewed By: alunyov Differential Revision: D49473484 fbshipit-source-id: c334c00ec4b648d689749802234f2a4bd5a12a48
1 parent a5e0ba3 commit 970c94a

File tree

1 file changed

+164
-120
lines changed

1 file changed

+164
-120
lines changed

compiler/crates/relay-transforms/src/client_edges.rs

Lines changed: 164 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,124 @@ impl<'program, 'sc, 'flag> ClientEdgesTransform<'program, 'sc, 'flag> {
308308
};
309309
}
310310

311+
fn verify_directives_or_push_errors(&mut self, directives: &[Directive]) {
312+
let allowed_directive_names = [
313+
*CLIENT_EDGE_WATERFALL_DIRECTIVE_NAME,
314+
*REQUIRED_DIRECTIVE_NAME,
315+
*CHILDREN_CAN_BUBBLE_METADATA_KEY,
316+
RequiredMetadataDirective::directive_name(),
317+
];
318+
319+
let other_directives = directives
320+
.iter()
321+
.filter(|directive| {
322+
!allowed_directive_names
323+
.iter()
324+
.any(|item| directive.name.item == *item)
325+
})
326+
.collect::<Vec<_>>();
327+
328+
for directive in other_directives {
329+
self.errors.push(Diagnostic::error(
330+
ValidationMessage::ClientEdgeUnsupportedDirective {
331+
directive_name: directive.name.item,
332+
},
333+
directive.name.location,
334+
));
335+
}
336+
}
337+
338+
fn get_edge_to_client_object_metadata_directive(
339+
&mut self,
340+
field: &LinkedField,
341+
edge_to_type: Type,
342+
waterfall_directive: Option<&Directive>,
343+
resolver_directive: Option<&DirectiveValue>,
344+
) -> Option<ClientEdgeMetadataDirective> {
345+
// We assume edges to client objects will be resolved on the client
346+
// and thus not incur a waterfall. This will change in the future
347+
// for @live Resolvers that can trigger suspense.
348+
if let Some(directive) = waterfall_directive {
349+
self.errors.push(Diagnostic::error_with_data(
350+
ValidationMessageWithData::RelayResolversUnexpectedWaterfall,
351+
directive.name.location,
352+
));
353+
}
354+
355+
match edge_to_type {
356+
Type::Interface(interface_id) => {
357+
let interface = self.program.schema.interface(interface_id);
358+
let implementing_objects =
359+
interface.recursively_implementing_objects(Arc::as_ref(&self.program.schema));
360+
if implementing_objects.is_empty() {
361+
self.errors.push(Diagnostic::error(
362+
ValidationMessage::RelayResolverClientInterfaceMustBeImplemented {
363+
interface_name: interface.name.item,
364+
},
365+
interface.name.location,
366+
));
367+
}
368+
if !self
369+
.relay_resolver_enable_interface_output_type
370+
.is_fully_enabled()
371+
&& !has_output_type(resolver_directive)
372+
{
373+
self.errors.push(Diagnostic::error(
374+
ValidationMessage::ClientEdgeToClientInterface,
375+
field.alias_or_name_location(),
376+
));
377+
}
378+
Some(ClientEdgeMetadataDirective::ClientObject {
379+
type_name: None,
380+
unique_id: self.get_key(),
381+
})
382+
}
383+
Type::Union(_) => {
384+
self.errors.push(Diagnostic::error(
385+
ValidationMessage::ClientEdgeToClientUnion,
386+
field.alias_or_name_location(),
387+
));
388+
None
389+
}
390+
Type::Object(object_id) => Some(ClientEdgeMetadataDirective::ClientObject {
391+
type_name: Some(self.program.schema.object(object_id).name.item),
392+
unique_id: self.get_key(),
393+
}),
394+
_ => {
395+
panic!("Expected a linked field to reference either an Object, Interface, or Union")
396+
}
397+
}
398+
}
399+
400+
fn get_edge_to_server_object_metadata_directive(
401+
&mut self,
402+
field_type: &schema::Field,
403+
field_location: Location,
404+
waterfall_directive: Option<&Directive>,
405+
selections: Vec<Selection>,
406+
) -> ClientEdgeMetadataDirective {
407+
// Client Edges to server objects must be annotated with @waterfall
408+
if waterfall_directive.is_none() {
409+
self.errors.push(Diagnostic::error_with_data(
410+
ValidationMessageWithData::RelayResolversMissingWaterfall {
411+
field_name: field_type.name.item,
412+
},
413+
field_location,
414+
));
415+
}
416+
let client_edge_query_name = self.generate_query_name();
417+
418+
self.generate_client_edge_query(
419+
client_edge_query_name,
420+
field_type.type_.inner(),
421+
selections,
422+
);
423+
ClientEdgeMetadataDirective::ServerObject {
424+
query_name: client_edge_query_name,
425+
unique_id: self.get_key(),
426+
}
427+
}
428+
311429
fn transform_linked_field_impl(&mut self, field: &LinkedField) -> Transformed<Selection> {
312430
let schema = &self.program.schema;
313431
let field_type = schema.field(field.definition.item);
@@ -334,31 +452,7 @@ impl<'program, 'sc, 'flag> ClientEdgesTransform<'program, 'sc, 'flag> {
334452
return self.default_transform_linked_field(field);
335453
}
336454

337-
let allowed_directive_names = [
338-
*CLIENT_EDGE_WATERFALL_DIRECTIVE_NAME,
339-
*REQUIRED_DIRECTIVE_NAME,
340-
*CHILDREN_CAN_BUBBLE_METADATA_KEY,
341-
RequiredMetadataDirective::directive_name(),
342-
];
343-
344-
let other_directives = field
345-
.directives
346-
.iter()
347-
.filter(|directive| {
348-
!allowed_directive_names
349-
.iter()
350-
.any(|item| directive.name.item == *item)
351-
})
352-
.collect::<Vec<_>>();
353-
354-
for directive in other_directives {
355-
self.errors.push(Diagnostic::error(
356-
ValidationMessage::ClientEdgeUnsupportedDirective {
357-
directive_name: directive.name.item,
358-
},
359-
directive.name.location,
360-
));
361-
}
455+
self.verify_directives_or_push_errors(&field.directives);
362456

363457
let edge_to_type = field_type.type_.inner();
364458

@@ -369,106 +463,26 @@ impl<'program, 'sc, 'flag> ClientEdgesTransform<'program, 'sc, 'flag> {
369463
.replace_or_else(|| field.selections.clone());
370464

371465
let metadata_directive = if is_edge_to_client_object {
372-
// We assume edges to client objects will be resolved on the client
373-
// and thus not incur a waterfall. This will change in the future
374-
// for @live Resolvers that can trigger suspense.
375-
if let Some(directive) = waterfall_directive {
376-
self.errors.push(Diagnostic::error_with_data(
377-
ValidationMessageWithData::RelayResolversUnexpectedWaterfall,
378-
directive.name.location,
379-
));
380-
}
381-
382-
match edge_to_type {
383-
Type::Interface(interface_id) => {
384-
let interface = schema.interface(interface_id);
385-
let implementing_objects =
386-
interface.recursively_implementing_objects(Arc::as_ref(schema));
387-
if implementing_objects.is_empty() {
388-
self.errors.push(Diagnostic::error(
389-
ValidationMessage::RelayResolverClientInterfaceMustBeImplemented {
390-
interface_name: interface.name.item,
391-
},
392-
interface.name.location,
393-
));
394-
}
395-
if !self
396-
.relay_resolver_enable_interface_output_type
397-
.is_fully_enabled()
398-
&& !has_output_type(resolver_directive)
399-
{
400-
self.errors.push(Diagnostic::error(
401-
ValidationMessage::ClientEdgeToClientInterface,
402-
field.alias_or_name_location(),
403-
));
404-
}
405-
ClientEdgeMetadataDirective::ClientObject {
406-
type_name: None,
407-
unique_id: self.get_key(),
408-
}
409-
}
410-
Type::Union(_) => {
411-
self.errors.push(Diagnostic::error(
412-
ValidationMessage::ClientEdgeToClientUnion,
413-
field.alias_or_name_location(),
414-
));
415-
return Transformed::Keep;
416-
}
417-
Type::Object(object_id) => ClientEdgeMetadataDirective::ClientObject {
418-
type_name: Some(schema.object(object_id).name.item),
419-
unique_id: self.get_key(),
420-
},
421-
_ => {
422-
panic!(
423-
"Expected a linked field to reference either an Object, Interface, or Union"
424-
)
425-
}
466+
match self.get_edge_to_client_object_metadata_directive(
467+
field,
468+
edge_to_type,
469+
waterfall_directive,
470+
resolver_directive,
471+
) {
472+
Some(directive) => directive,
473+
None => return Transformed::Keep,
426474
}
427475
} else {
428-
// Client Edges to server objects must be annotated with @waterfall
429-
if waterfall_directive.is_none() {
430-
self.errors.push(Diagnostic::error_with_data(
431-
ValidationMessageWithData::RelayResolversMissingWaterfall {
432-
field_name: field_type.name.item,
433-
},
434-
field.definition.location,
435-
));
436-
}
437-
let client_edge_query_name = self.generate_query_name();
438-
439-
self.generate_client_edge_query(
440-
client_edge_query_name,
441-
field_type.type_.inner(),
476+
self.get_edge_to_server_object_metadata_directive(
477+
field_type,
478+
field.definition.location,
479+
waterfall_directive,
442480
new_selections.clone(),
443-
);
444-
ClientEdgeMetadataDirective::ServerObject {
445-
query_name: client_edge_query_name,
446-
unique_id: self.get_key(),
447-
}
481+
)
448482
};
449-
let mut inline_fragment_directives: Vec<Directive> = vec![metadata_directive.into()];
450-
if let Some(required_directive_metadata) = field
451-
.directives
452-
.named(RequiredMetadataDirective::directive_name())
453-
.cloned()
454-
{
455-
inline_fragment_directives.push(required_directive_metadata);
456-
}
457483

458-
let transformed_field = Arc::new(LinkedField {
459-
selections: new_selections,
460-
..field.clone()
461-
});
462-
463-
let inline_fragment = InlineFragment {
464-
type_condition: None,
465-
directives: inline_fragment_directives,
466-
selections: vec![
467-
Selection::LinkedField(transformed_field.clone()),
468-
Selection::LinkedField(transformed_field),
469-
],
470-
spread_location: Location::generated(),
471-
};
484+
let inline_fragment =
485+
create_inline_fragment_for_client_edge(field, new_selections, metadata_directive);
472486

473487
Transformed::Replace(Selection::InlineFragment(Arc::new(inline_fragment)))
474488
}
@@ -480,6 +494,36 @@ impl<'program, 'sc, 'flag> ClientEdgesTransform<'program, 'sc, 'flag> {
480494
}
481495
}
482496

497+
fn create_inline_fragment_for_client_edge(
498+
field: &LinkedField,
499+
selections: Vec<Selection>,
500+
metadata_directive: ClientEdgeMetadataDirective,
501+
) -> InlineFragment {
502+
let mut inline_fragment_directives: Vec<Directive> = vec![metadata_directive.into()];
503+
if let Some(required_directive_metadata) = field
504+
.directives
505+
.named(RequiredMetadataDirective::directive_name())
506+
.cloned()
507+
{
508+
inline_fragment_directives.push(required_directive_metadata);
509+
}
510+
511+
let transformed_field = Arc::new(LinkedField {
512+
selections,
513+
..field.clone()
514+
});
515+
516+
InlineFragment {
517+
type_condition: None,
518+
directives: inline_fragment_directives,
519+
selections: vec![
520+
Selection::LinkedField(transformed_field.clone()),
521+
Selection::LinkedField(transformed_field),
522+
],
523+
spread_location: Location::generated(),
524+
}
525+
}
526+
483527
impl Transformer for ClientEdgesTransform<'_, '_, '_> {
484528
const NAME: &'static str = "ClientEdgesTransform";
485529
const VISIT_ARGUMENTS: bool = false;

0 commit comments

Comments
 (0)