Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add magic link field #282

Merged
merged 8 commits into from
Dec 28, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions crates/graphql_artifact_generation/src/eager_reader_artifact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,18 +156,18 @@ pub(crate) fn generate_eager_reader_condition_artifact(
reader_imports_to_import_statement(&reader_imports, file_extensions);

let reader_param_type = "{ data: any, parameters: Record<PropertyKey, never> }";
let reader_output_type = "boolean";
let reader_output_type = "Link | null";

let reader_content = format!(
"import type {{ EagerReaderArtifact, ReaderAst }} from '@isograph/react';\n\
"import type {{ EagerReaderArtifact, ReaderAst, Link }} from '@isograph/react';\n\
{reader_import_statement}\n\
const readerAst: ReaderAst<{reader_param_type}> = {reader_ast};\n\n\
const artifact: EagerReaderArtifact<\n\
{}{reader_param_type},\n\
{}{reader_output_type}\n\
> = {{\n\
{}kind: \"EagerReaderArtifact\",\n\
{}resolver: ({{ data }}) => data.__typename === \"{concrete_type}\",\n\
{}resolver: ({{ data }}) => data.__typename === \"{concrete_type}\" ? data.link : null,\n\
{}readerAst,\n\
}};\n\n\
export default artifact;\n",
Expand Down Expand Up @@ -196,19 +196,27 @@ pub(crate) fn generate_eager_reader_param_type_artifact(

let mut param_type_imports = BTreeSet::new();
let mut loadable_fields = BTreeSet::new();
let mut link_fields = false;
let client_field_parameter_type = generate_client_field_parameter_type(
schema,
client_field.selection_set_for_parent_query(),
parent_type,
&mut param_type_imports,
&mut loadable_fields,
1,
&mut link_fields,
);

let param_type_import_statement =
param_type_imports_to_import_statement(&param_type_imports, file_extensions);
let reader_param_type = format!("{}__{}__param", parent_type.name, client_field.name);

let link_field_imports = if link_fields {
"import type { Link } from '@isograph/react';\n".to_string()
} else {
"".to_string()
};

let loadable_field_imports = if !loadable_fields.is_empty() {
let param_imports =
param_type_imports_to_import_param_statement(&loadable_fields, file_extensions);
Expand All @@ -234,6 +242,7 @@ pub(crate) fn generate_eager_reader_param_type_artifact(
let indent = " ";
let param_type_content = format!(
"{param_type_import_statement}\
{link_field_imports}\
{loadable_field_imports}\
{parameters_import}\n\
export type {reader_param_type} = {{\n\
Expand Down
143 changes: 86 additions & 57 deletions crates/graphql_artifact_generation/src/generate_artifacts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use crate::{
generate_entrypoint_artifacts_with_client_field_traversal_result,
},
format_parameter_type::format_parameter_type,
import_statements::ParamTypeImports,
import_statements::{LinkImports, ParamTypeImports},
iso_overload_file::build_iso_overload_artifact,
refetch_reader_artifact::{
generate_refetch_output_type_artifact, generate_refetch_reader_artifact,
Expand Down Expand Up @@ -131,8 +131,9 @@ pub fn get_artifact_path_and_content(
}
FieldType::ClientField(encountered_client_field_id) => {
let encountered_client_field = schema.client_field(*encountered_client_field_id);
// Generate reader ASTs for all encountered client fields, which may be reader or refetch reader

match &encountered_client_field.variant {
ClientFieldVariant::Link => (),
PatrykWalach marked this conversation as resolved.
Show resolved Hide resolved
ClientFieldVariant::UserWritten(info) => {
path_and_contents.extend(generate_eager_reader_artifacts(
schema,
Expand Down Expand Up @@ -248,6 +249,7 @@ pub fn get_artifact_path_and_content(

for user_written_client_field in schema.client_fields.iter().flat_map(|field| match field {
ClientType::ClientField(field) => match field.variant {
ClientFieldVariant::Link => None,
ClientFieldVariant::UserWritten(_) => Some(field),
ClientFieldVariant::ImperativelyLoadedField(_) => None,
},
Expand Down Expand Up @@ -283,20 +285,25 @@ pub fn get_artifact_path_and_content(

for output_type_id in encountered_output_types {
let client_field = schema.client_field(output_type_id);
let path_and_content = match client_field.variant {
ClientFieldVariant::UserWritten(info) => generate_eager_reader_output_type_artifact(
schema,
client_field,
project_root,
artifact_directory,
info,
file_extensions,
),
let artifact_path_and_content = match client_field.variant {
ClientFieldVariant::Link => None,
ClientFieldVariant::UserWritten(info) => {
Some(generate_eager_reader_output_type_artifact(
schema,
client_field,
project_root,
artifact_directory,
info,
file_extensions,
))
}
ClientFieldVariant::ImperativelyLoadedField(_) => {
generate_refetch_output_type_artifact(schema, client_field)
Some(generate_refetch_output_type_artifact(schema, client_field))
}
};
path_and_contents.push(path_and_content);
if let Some(path_and_content) = artifact_path_and_content {
path_and_contents.push(path_and_content);
};
}

path_and_contents.push(build_iso_overload_artifact(
Expand Down Expand Up @@ -403,6 +410,7 @@ pub(crate) fn get_serialized_field_arguments(
pub(crate) fn generate_output_type(client_field: &ValidatedClientField) -> ClientFieldOutputType {
let variant = &client_field.variant;
match variant {
ClientFieldVariant::Link => ClientFieldOutputType("Link".to_string()),
ClientFieldVariant::UserWritten(info) => match info.user_written_component_variant {
UserWrittenComponentVariant::Eager => {
ClientFieldOutputType("ReturnType<typeof resolver>".to_string())
Expand Down Expand Up @@ -438,6 +446,7 @@ pub(crate) fn generate_client_field_parameter_type(
nested_client_field_imports: &mut ParamTypeImports,
loadable_fields: &mut ParamTypeImports,
indentation_level: u8,
link_fields: &mut LinkImports,
) -> ClientFieldParameterType {
// TODO use unwraps
let mut client_field_parameter_type = "{\n".to_string();
Expand All @@ -450,13 +459,15 @@ pub(crate) fn generate_client_field_parameter_type(
nested_client_field_imports,
loadable_fields,
indentation_level + 1,
link_fields,
);
}
client_field_parameter_type.push_str(&format!("{}}}", " ".repeat(indentation_level as usize)));

ClientFieldParameterType(client_field_parameter_type)
}

#[allow(clippy::too_many_arguments)]
fn write_param_type_from_selection(
schema: &ValidatedSchema,
query_type_declaration: &mut String,
Expand All @@ -465,6 +476,7 @@ fn write_param_type_from_selection(
nested_client_field_imports: &mut ParamTypeImports,
loadable_fields: &mut ParamTypeImports,
indentation_level: u8,
link_fields: &mut LinkImports,
) {
match &selection.item {
ServerFieldSelection::ScalarField(scalar_field_selection) => {
Expand Down Expand Up @@ -516,55 +528,71 @@ fn write_param_type_from_selection(
query_type_declaration
.push_str(&" ".repeat(indentation_level as usize).to_string());

nested_client_field_imports.insert(client_field.type_and_field);
let inner_output_type = format!(
"{}__output_type",
client_field.type_and_field.underscore_separated()
);

let output_type = match scalar_field_selection.associated_data.selection_variant
{
ValidatedIsographSelectionVariant::Regular => inner_output_type,
ValidatedIsographSelectionVariant::Loadable(_) => {
loadable_fields.insert(client_field.type_and_field);
let provided_arguments = get_provided_arguments(
client_field.variable_definitions.iter().map(|x| &x.item),
&scalar_field_selection.arguments,
match client_field.variant {
ClientFieldVariant::Link => {
*link_fields = true;
let output_type = "Link";
query_type_declaration.push_str(
&(format!(
"readonly {}: {},\n",
scalar_field_selection.name_or_alias().item,
output_type
)),
);
}
ClientFieldVariant::UserWritten(_)
| ClientFieldVariant::ImperativelyLoadedField(_) => {
nested_client_field_imports.insert(client_field.type_and_field);
let inner_output_type = format!(
"{}__output_type",
client_field.type_and_field.underscore_separated()
);
let output_type = match scalar_field_selection
.associated_data
.selection_variant
{
ValidatedIsographSelectionVariant::Regular => inner_output_type,
ValidatedIsographSelectionVariant::Loadable(_) => {
loadable_fields.insert(client_field.type_and_field);
let provided_arguments = get_provided_arguments(
client_field.variable_definitions.iter().map(|x| &x.item),
&scalar_field_selection.arguments,
);

let indent = " ".repeat((indentation_level + 1) as usize);
let provided_args_type = if provided_arguments.is_empty() {
"".to_string()
} else {
format!(
",\n{indent}Omit<ExtractParameters<{}__param>, keyof {}>",
client_field.type_and_field.underscore_separated(),
get_loadable_field_type_from_arguments(
schema,
provided_arguments
let indent = " ".repeat((indentation_level + 1) as usize);
let provided_args_type = if provided_arguments.is_empty() {
"".to_string()
} else {
format!(
",\n{indent}Omit<ExtractParameters<{}__param>, keyof {}>",
client_field.type_and_field.underscore_separated(),
get_loadable_field_type_from_arguments(
schema,
provided_arguments
)
)
};

format!(
"LoadableField<\n\
{indent}{}__param,\n\
{indent}{inner_output_type}\
{provided_args_type}\n\
{}>",
client_field.type_and_field.underscore_separated(),
" ".repeat(indentation_level as usize),
)
)
}
};

format!(
"LoadableField<\n\
{indent}{}__param,\n\
{indent}{inner_output_type}\
{provided_args_type}\n\
{}>",
client_field.type_and_field.underscore_separated(),
" ".repeat(indentation_level as usize),
)
query_type_declaration.push_str(
&(format!(
"readonly {}: {},\n",
scalar_field_selection.name_or_alias().item,
output_type
)),
);
}
};

query_type_declaration.push_str(
&(format!(
"readonly {}: {},\n",
scalar_field_selection.name_or_alias().item,
output_type
)),
);
}
}
}
}
Expand Down Expand Up @@ -603,6 +631,7 @@ fn write_param_type_from_selection(
nested_client_field_imports,
loadable_fields,
indentation_level,
link_fields,
)
}),
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ impl ImportedFileCategory {

pub(crate) type ReaderImports = BTreeSet<(ObjectTypeAndFieldName, ImportedFileCategory)>;
pub(crate) type ParamTypeImports = BTreeSet<ObjectTypeAndFieldName>;
pub(crate) type LinkImports = bool;

pub(crate) fn reader_imports_to_import_statement(
reader_imports: &ReaderImports,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ fn user_written_fields(
.iter()
.filter_map(|client_field| match client_field {
ClientType::ClientField(client_field) => match client_field.variant {
ClientFieldVariant::Link => None,
ClientFieldVariant::UserWritten(info) => {
Some((client_field, info.user_written_component_variant))
}
Expand Down
51 changes: 37 additions & 14 deletions crates/graphql_artifact_generation/src/reader_ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ use isograph_lang_types::{
LoadableDirectiveParameters, RefetchQueryIndex, SelectionType, ServerFieldSelection,
};
use isograph_schema::{
categorize_field_loadability, transform_arguments_with_child_context, FieldType, Loadability,
NameAndArguments, NormalizationKey, ObjectTypeAndFieldName, PathToRefetchField,
RefetchedPathsMap, SchemaServerFieldVariant, ValidatedClientField,
categorize_field_loadability, transform_arguments_with_child_context, ClientFieldVariant,
FieldType, Loadability, NameAndArguments, NormalizationKey, ObjectTypeAndFieldName,
PathToRefetchField, RefetchedPathsMap, SchemaServerFieldVariant, ValidatedClientField,
ValidatedIsographSelectionVariant, ValidatedLinkedFieldSelection,
ValidatedScalarFieldSelection, ValidatedSchema, ValidatedSelection, VariableContext,
};
Expand Down Expand Up @@ -204,20 +204,43 @@ fn scalar_client_defined_field_ast_node(
indentation_level,
scalar_field_selection,
),
None => user_written_variant_ast_node(
scalar_field_selection,
indentation_level,
client_field,
schema,
path,
root_refetched_paths,
reader_imports,
&client_field_variable_context,
parent_variable_context,
),
None => match client_field.variant {
ClientFieldVariant::Link => {
link_variant_ast_node(scalar_field_selection, indentation_level)
}
ClientFieldVariant::UserWritten(_) | ClientFieldVariant::ImperativelyLoadedField(_) => {
user_written_variant_ast_node(
scalar_field_selection,
indentation_level,
client_field,
schema,
path,
root_refetched_paths,
reader_imports,
&client_field_variable_context,
parent_variable_context,
)
}
},
}
}

fn link_variant_ast_node(
scalar_field_selection: &ValidatedScalarFieldSelection,
indentation_level: u8,
) -> String {
let alias = scalar_field_selection.name_or_alias().item;
let indent_1 = " ".repeat(indentation_level as usize);
let indent_2 = " ".repeat((indentation_level + 1) as usize);

format!(
"{indent_1}{{\n\
{indent_2}kind: \"Link\",\n\
{indent_2}alias: \"{alias}\",\n\
{indent_1}}},\n",
)
}

#[allow(clippy::too_many_arguments)]
fn user_written_variant_ast_node(
scalar_field_selection: &ValidatedScalarFieldSelection,
Expand Down
1 change: 1 addition & 0 deletions crates/isograph_compiler/src/source_files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ impl SourceFiles {
process_iso_literals(schema, self.contains_iso)?;
process_exposed_fields(schema)?;
schema.add_fields_to_subtypes(&outcome.type_refinement_maps.supertype_to_subtype_map)?;
schema.add_link_fields()?;
schema
.add_pointers_to_supertypes(&outcome.type_refinement_maps.subtype_to_supertype_map)?;
add_refetch_fields_to_objects(schema)?;
Expand Down
Loading
Loading