Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
177 changes: 105 additions & 72 deletions src/build_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::{
hasura_types::as_gql_field,
purescript_gen::{
purescript_argument::Argument,
purescript_gql_union::GqlUnion,
purescript_import::PurescriptImport,
purescript_instance::{derive_new_type_instance, DeriveInstance},
purescript_print_module::print_module,
Expand Down Expand Up @@ -58,6 +59,7 @@ pub async fn build_schema(
let mut types: Vec<PurescriptType> = vec![];
let mut imports: Vec<PurescriptImport> = vec![];
let mut variants: Vec<Variant> = vec![];
let mut unions: Vec<GqlUnion> = vec![];
let mut instances: Vec<DeriveInstance> = vec![];

// Add the purescript GraphQL client imports that are always used,
Expand Down Expand Up @@ -129,75 +131,75 @@ pub async fn build_schema(

// Process the schema types
for type_ in schema.types.iter() {
match type_ {
Type::Object(obj) => {
// There are a couple of `__` prefixed Hasura types that we can safely ignore
if obj.name.starts_with("__") {
continue;
}
let mut handle_obj = |obj: &cynic_introspection::ObjectType| {
// There are a builting of `__` prefixed graphql types that we can safely ignore
if obj.name.starts_with("__") {
return;
}

// Convert the hasura_type_name to a PurescriptTypeName
let name = pascal_case(&obj.name);
// Convert the gql_type_name to a PurescriptTypeName
let name = pascal_case(&obj.name);

// Creates a new record for the object
let mut record = PurescriptRecord::new("Ignored");
// Creates a new record for the object
let mut record = PurescriptRecord::new("Ignored");

// Add type fields to the record
for field in obj.fields.iter() {
// If the field has arguments then the purescript representation will be:
// field_name :: { | Arguments } -> ReturnType

// Build the arguments record:
let mut args = PurescriptRecord::new("Arguments");
for arg in &field.args {
let arg_type = wrap_type(
as_gql_field(
&field.name,
&arg.name,
&arg.ty.name,
&mut imports,
&postgres_types,
&outside_types,
),
&arg.ty.wrapping,
&mut imports,
);
let mut arg_field = Field::new(&arg.name);
arg_field.type_name = arg_type;
args.add_field(arg_field);
}
// Add type fields to the record
for field in obj.fields.iter() {
// If the field has arguments then the purescript representation will be:
// field_name :: { | Arguments } -> ReturnType

// Build the return type,
// potentially wrapping values in Array or Maybe
// and resolving any matched outside types
let return_type = return_type_wrapper(
// Build the arguments record:
let mut args = PurescriptRecord::new("Arguments");
for arg in &field.args {
let arg_type = wrap_type(
as_gql_field(
&obj.name,
&field.name,
&field.ty.name,
&arg.name,
&arg.ty.name,
&mut imports,
&postgres_types,
&outside_types,
),
&field.ty.wrapping,
&arg.ty.wrapping,
&mut imports,
);

// Add the function argument to the new record field
// and add it to the object record
let function_arg =
Argument::new_function(vec![Argument::new_record(args)], return_type);
let record_field = Field::new(&field.name).with_type_arg(function_arg);
record.add_field(record_field);
let mut arg_field = Field::new(&arg.name);
arg_field.type_name = arg_type;
args.add_field(arg_field);
}

// Create the newtype record for the object and append it to the schema module types
let mut query_type =
PurescriptType::new(&name, vec![], Argument::new_record(record));
query_type.set_newtype(true);
instances.push(derive_new_type_instance(&query_type.name));
types.push(query_type);
// Build the return type,
// potentially wrapping values in Array or Maybe
// and resolving any matched outside types
let return_type = return_type_wrapper(
as_gql_field(
&obj.name,
&field.name,
&field.ty.name,
&mut imports,
&postgres_types,
&outside_types,
),
&field.ty.wrapping,
&mut imports,
);

// Add the function argument to the new record field
// and add it to the object record
let function_arg =
Argument::new_function(vec![Argument::new_record(args)], return_type);
let record_field = Field::new(&field.name).with_type_arg(function_arg);
record.add_field(record_field);
}

// Create the newtype record for the object and append it to the schema module types
let mut query_type = PurescriptType::new(&name, vec![], Argument::new_record(record));
query_type.set_newtype(true);
instances.push(derive_new_type_instance(&query_type.name));
types.push(query_type);
};
match type_ {
Type::Object(obj) => handle_obj(obj),
Type::Scalar(scalar) => {
// Add imports for common scalar types if they are used.
// TODO maybe move these to config so they can be updated outside of rust
Expand All @@ -215,7 +217,7 @@ pub async fn build_schema(
}
}
Type::Enum(en) => {
// Ignore internal Hasura enums beginning with `__`
// Ignore internal graphql enums beginning with `__`
if en.name.starts_with("__") {
continue;
}
Expand All @@ -230,12 +232,12 @@ pub async fn build_schema(
}
}
Type::InputObject(obj) => {
// Ignore internal Hasura input objects beginning with `__`
// Ignore internal graphql input objects beginning with `__`
if obj.name.starts_with("__") {
continue;
}

// Convert the hasura_type_name to a PurescriptTypeName
// Convert the gql_type_name to a PurescriptTypeName
let name = pascal_case(&obj.name);

// Build a purescript record with all fields
Expand Down Expand Up @@ -267,13 +269,43 @@ pub async fn build_schema(
instances.push(derive_new_type_instance(&query_type.name));
types.push(query_type);
}
Type::Interface(InterfaceType { name, .. }) => {
// Currently ignored as we don't have any in our schemas
println!("Interface: {name}");
Type::Interface(InterfaceType {
name,
fields,
description,
..
}) => {
handle_obj(&cynic_introspection::ObjectType {
name: name.clone(),
fields: fields.clone(),
description: description.clone(),
interfaces: vec![],
});
}
Type::Union(UnionType { name, .. }) => {
Type::Union(UnionType {
name,
description: _,
possible_types,
..
}) => {
// Currently ignored as we don't have any in our schemas
println!("Union: {name}");
add_import(
"graphql-client",
"GraphQL.Client.Union",
"GqlUnion",
&mut imports,
);

let mut union = GqlUnion::new(&name);

union.with_values(
&possible_types
.iter()
.map(|t| (t.clone(), pascal_case(&t)))
.collect(),
);

unions.push(union);
}
}
}
Expand All @@ -297,18 +329,19 @@ pub async fn build_schema(

// Write the schema module to the file system
let schema_module_path = format!("{lib_path}/src/Schema/{role}.purs");
write(
&schema_module_path,
&print_module(
&role,
&mut types,
&mut records,
&mut imports,
&mut variants,
&mut instances,
),

let printed = print_module(
&role,
&mut types,
&mut records,
&mut imports,
&mut variants,
&mut unions,
&mut instances,
);

write(&schema_module_path, &printed);

// Write the directives module
let path_clone = lib_path.clone();
spawn_blocking(move || build_directives(path_clone, directive_role, schema.directives));
Expand Down
2 changes: 1 addition & 1 deletion src/config/parse_outside_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ fn write_types(outside_types: &OutsideTypes, workspace_config: &WorkspaceConfig)
let mock_outside_types = std::env::var("MOCK_OUTSIDE_TYPES");
if mock_outside_types.is_ok() {
let lib_path = workspace_config.shared_graphql_enums_dir.clone();
let postgres_enums_lib = pascal_case(&workspace_config.postgres_enums_lib);
let postgres_enums_lib = pascal_case(&(&workspace_config.postgres_enums_lib).clone().unwrap());
let gql_enums_lib = pascal_case(&workspace_config.shared_graphql_enums_lib);
for module in to_write.iter() {
let is_enum_mod = module.import.contains(&postgres_enums_lib)
Expand Down
17 changes: 9 additions & 8 deletions src/config/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ use yaml_rust2::{yaml, Yaml};
pub async fn parse_workspace() -> Result<WorkspaceConfig> {
let file_path: String = std::env::var("SPAGO_WORKSPACE_CONFIG_YAML")
.expect("SPAGO_WORKSPACE_CONFIG_YAML must be set");
let mut f = File::open(file_path)
let mut f = File::open(file_path.clone())
.await
.expect("Failed to locate or open spago workspace config yaml.");
.expect(format!("Failed to locate or open spago workspace config yaml at: {}", file_path).as_str());
let mut s = String::new();

f.read_to_string(&mut s)
Expand All @@ -32,8 +32,8 @@ pub async fn parse_workspace() -> Result<WorkspaceConfig> {

#[derive(Clone)]
pub struct WorkspaceConfig {
pub postgres_enums_lib: String,
pub postgres_enums_dir: String,
pub postgres_enums_lib: Option<String>,
pub postgres_enums_dir: Option<String>,
pub shared_graphql_enums_lib: String,
pub shared_graphql_enums_dir: String,
pub schema_libs_prefix: String,
Expand All @@ -55,12 +55,13 @@ impl WorkspaceConfig {
Some(Self {
postgres_enums_lib: postgres_enums_lib
.as_str()
.expect("Workspace yaml should contain postgres_enums_lib key.")
.to_string(),
.map(|s| s.to_string()),
postgres_enums_dir: postgres_enums_dir
.as_str()
.expect("Workspace yaml should contain postgres_enums_dir key.")
.to_string(),
.map(|s| s.to_string()),

// .expect("Workspace yaml should contain postgres_enums_dir key.")
// .to_string(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// .expect("Workspace yaml should contain postgres_enums_dir key.")
// .to_string(),

shared_graphql_enums_lib: shared_graphql_enums_lib
.as_str()
.expect("Workspace yaml should contain shared_graphql_enums_lib key.")
Expand Down
2 changes: 1 addition & 1 deletion src/enums/generate_enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ pub async fn generate_enum(
}
// Otherwise write schema-specific variant enums
} else {
Some(Variant::new(&name).with_values(&original_values))
Some(Variant::new(&name).with_values(&original_values).clone())
}
}

Expand Down
23 changes: 17 additions & 6 deletions src/enums/postgres_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,22 @@ pub async fn fetch_types(

// when no postgres enums are included, skip the enum generation
if db_env.is_err() {
println!("No DATABASE_URL specified. Skipping enum generation.");
return Ok(HashMap::new());
}

let Some(ref postgres_enums_lib) = workspace_config.postgres_enums_lib else {
println!("No postgres enums lib specified in workspace config. Skipping enum generation.");
return Ok(HashMap::new());
};


let Some(postgres_enums_dir) = &workspace_config.postgres_enums_dir else {
println!("No postgres enums dir specified in workspace config. Skipping enum generation.");
return Ok(HashMap::new());
};


let database_url =
db_env.expect("DATABASE_URL should not be required but for some reason is...");
let pool = PgPoolOptions::new()
Expand All @@ -35,12 +49,9 @@ pub async fn fetch_types(

let mut hash_map = HashMap::new();

let package_name = pascal_case(&workspace_config.postgres_enums_lib);
let lib_path = format!(
"{}{}",
&workspace_config.postgres_enums_dir, &workspace_config.postgres_enums_lib
);
let package = &workspace_config.postgres_enums_lib;
let package_name = pascal_case(&postgres_enums_lib);
let lib_path = format!("{}{}", &postgres_enums_dir, &postgres_enums_lib);
let package = postgres_enums_lib;

for enum_row in res.iter() {
let name = enum_row.enumtype.clone();
Expand Down
13 changes: 9 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,18 @@ async fn main() -> Result<()> {
// Trash existing schema
for path in vec![
workspace_config.postgres_enums_dir.clone(),
workspace_config.shared_graphql_enums_dir.clone(),
workspace_config.schema_libs_dir.clone(),
Some(workspace_config.shared_graphql_enums_dir.clone()),
Some(workspace_config.schema_libs_dir.clone()),
]
.iter()
{
remove_dir_all(path).ok();
match path {
Some(dir) => {
remove_dir_all(dir).ok();
}
None => (),
}
}

// Generate postgres enum types
let postgres_types = fetch_types(&workspace_config)
.await
Expand Down Expand Up @@ -69,6 +73,7 @@ async fn main() -> Result<()> {

// Run schema gen for each role concurrently
let mut tasks = Vec::with_capacity(num_roles);
println!("Generating schemas for roles: {:#?}", roles);
for role in roles.iter() {
tasks.push(spawn(build_schema(
role.clone(),
Expand Down
2 changes: 2 additions & 0 deletions src/purescript_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ pub mod purescript_print_module;
pub mod purescript_record;
pub mod purescript_type;
pub mod purescript_variant;
pub mod purescript_row;
pub mod purescript_gql_union;
Loading
Loading