Skip to content
This repository has been archived by the owner on May 3, 2024. It is now read-only.

Commit

Permalink
feat: Intra-subgraph logic of federated query graph creation (#84)
Browse files Browse the repository at this point in the history
This PR implements the intra-subgraph logic of federated query graph creation. That is, it implements the business logic that is performed for each individual subgraph when creating a federated query graph. The inter-subraph logic will be a separate PR, to keep PRs smaller. Tests will be added in follow-up PRs.

Some notes:
- Regarding porting, while the JS codebase creates a separate query graph for each subgraph and then copies its nodes/edges over to the federated one, the Rust code in this PR starts out with a single query graph (the federated one), and each builder adds its nodes/edges to that one. This allows us to avoid copying all the subgraph query graphs (and makes the porting/working with `petgraph` a bit easier).
- We introduce a few convenience functions for `FederationSchema`/positions in this PR. We use those in the `QueryGraph` logic, but I've also refactored `extract_subgraphs_from_supergraph()` logic to use them, which has simplified some functions/removed lifetimes (also some bugfixes).
- This PR is stacked on top of #80 .
  • Loading branch information
sachindshinde authored Nov 14, 2023
1 parent 387d4f8 commit 2ee4fe9
Show file tree
Hide file tree
Showing 8 changed files with 1,990 additions and 349 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ indexmap = "2.0.2"
thiserror = "1.0"
url = "2"
lazy_static = "1.4.0"
petgraph = "0.6.4"
strum = "0.25.0"
strum_macros = "0.25.2"

Expand Down
1 change: 0 additions & 1 deletion src/link/argument.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ pub(crate) fn directive_optional_fieldset_argument(
}
}

#[allow(dead_code)]
pub(crate) fn directive_required_fieldset_argument(
application: &Node<Directive>,
name: &Name,
Expand Down
107 changes: 106 additions & 1 deletion src/link/federation_spec_definition.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
use crate::error::{FederationError, SingleFederationError};
use crate::link::argument::{
directive_optional_boolean_argument, directive_required_fieldset_argument,
};
use crate::link::spec::{Identity, Url, Version};
use crate::link::spec_definition::{SpecDefinition, SpecDefinitions};
use crate::schema::FederationSchema;
use apollo_compiler::ast::Argument;
use apollo_compiler::schema::{Directive, DirectiveDefinition, Name, Value};
use apollo_compiler::schema::{
Directive, DirectiveDefinition, ExtendedType, Name, UnionType, Value,
};
use apollo_compiler::{name, Node, NodeStr};
use lazy_static::lazy_static;

pub(crate) const FEDERATION_ENTITY_TYPE_NAME_IN_SPEC: Name = name!("_Entity");
pub(crate) const FEDERATION_KEY_DIRECTIVE_NAME_IN_SPEC: Name = name!("key");
pub(crate) const FEDERATION_INTERFACEOBJECT_DIRECTIVE_NAME_IN_SPEC: Name = name!("interfaceObject");
pub(crate) const FEDERATION_EXTERNAL_DIRECTIVE_NAME_IN_SPEC: Name = name!("external");
Expand All @@ -20,6 +26,11 @@ pub(crate) const FEDERATION_RESOLVABLE_ARGUMENT_NAME: Name = name!("resolvable")
pub(crate) const FEDERATION_REASON_ARGUMENT_NAME: Name = name!("reason");
pub(crate) const FEDERATION_FROM_ARGUMENT_NAME: Name = name!("from");

pub(crate) struct KeyDirectiveArguments {
pub(crate) fields: NodeStr,
pub(crate) resolvable: bool,
}

pub(crate) struct FederationSpecDefinition {
url: Url,
}
Expand All @@ -34,6 +45,31 @@ impl FederationSpecDefinition {
}
}

pub(crate) fn entity_type_definition<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<Option<&'schema Node<UnionType>>, FederationError> {
// Note that the _Entity type is special in that:
// 1. Spec renaming doesn't take place for it (there's no prefixing or importing needed),
// in order to maintain backwards compatibility with Fed 1.
// 2. Its presence is optional; if absent, it means the subgraph has no resolvable keys.
match schema
.schema()
.types
.get(&FEDERATION_ENTITY_TYPE_NAME_IN_SPEC)
{
Some(ExtendedType::Union(type_)) => Ok(Some(type_)),
None => Ok(None),
_ => Err(SingleFederationError::Internal {
message: format!(
"Unexpectedly found non-union for federation spec's \"{}\" type definition",
FEDERATION_ENTITY_TYPE_NAME_IN_SPEC
),
}
.into()),
}
}

pub(crate) fn key_directive_definition<'schema>(
&self,
schema: &'schema FederationSchema,
Expand Down Expand Up @@ -76,6 +112,43 @@ impl FederationSpecDefinition {
})
}

pub(crate) fn key_directive_arguments(
&self,
application: &Node<Directive>,
) -> Result<KeyDirectiveArguments, FederationError> {
Ok(KeyDirectiveArguments {
fields: directive_required_fieldset_argument(
application,
&FEDERATION_FIELDS_ARGUMENT_NAME,
)?,
resolvable: directive_optional_boolean_argument(
application,
&FEDERATION_RESOLVABLE_ARGUMENT_NAME,
)?
.unwrap_or(false),
})
}

pub(crate) fn interface_object_directive_definition<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<Option<&'schema Node<DirectiveDefinition>>, FederationError> {
if *self.version() < (Version { major: 2, minor: 3 }) {
return Ok(None);
}
self.directive_definition(schema, &FEDERATION_INTERFACEOBJECT_DIRECTIVE_NAME_IN_SPEC)?
.ok_or_else(|| {
SingleFederationError::Internal {
message: format!(
"Unexpectedly could not find federation spec's \"@{}\" directive definition",
FEDERATION_INTERFACEOBJECT_DIRECTIVE_NAME_IN_SPEC
),
}
.into()
})
.map(Some)
}

pub(crate) fn interface_object_directive(
&self,
schema: &FederationSchema,
Expand All @@ -97,6 +170,22 @@ impl FederationSpecDefinition {
})
}

pub(crate) fn external_directive_definition<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<&'schema Node<DirectiveDefinition>, FederationError> {
self.directive_definition(schema, &FEDERATION_EXTERNAL_DIRECTIVE_NAME_IN_SPEC)?
.ok_or_else(|| {
SingleFederationError::Internal {
message: format!(
"Unexpectedly could not find federation spec's \"@{}\" directive definition",
FEDERATION_EXTERNAL_DIRECTIVE_NAME_IN_SPEC
),
}
.into()
})
}

pub(crate) fn external_directive(
&self,
schema: &FederationSchema,
Expand All @@ -120,6 +209,22 @@ impl FederationSpecDefinition {
})
}

pub(crate) fn requires_directive_definition<'schema>(
&self,
schema: &'schema FederationSchema,
) -> Result<&'schema Node<DirectiveDefinition>, FederationError> {
self.directive_definition(schema, &FEDERATION_REQUIRES_DIRECTIVE_NAME_IN_SPEC)?
.ok_or_else(|| {
SingleFederationError::Internal {
message: format!(
"Unexpectedly could not find federation spec's \"@{}\" directive definition",
FEDERATION_REQUIRES_DIRECTIVE_NAME_IN_SPEC
),
}
.into()
})
}

pub(crate) fn requires_directive(
&self,
schema: &FederationSchema,
Expand Down
Loading

0 comments on commit 2ee4fe9

Please sign in to comment.