-
Notifications
You must be signed in to change notification settings - Fork 5
Implement native query builder in the CLI plugin #511
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
Merged
Merged
Changes from all commits
Commits
Show all changes
31 commits
Select commit
Hold shift + click to select a range
cc71fdc
--wip-- [skip ci]
i-am-tom 56c99a3
Delete schema.json
i-am-tom b9f9bd8
Updates
i-am-tom e8f9b82
format
b33c5a6
Merge remote-tracking branch 'origin/main' into gjm-tjh/native-query-cli
d84048e
add some comments
b3cf917
remove the test.sql entry in the config
ffb7ea7
implement oid lookup and move native operations code to different file
9fb5b4b
undo changes to schema.json
452a490
use native operations parser
1492204
remove regex dep
080fb86
fix warnings
b14d25d
Add `postgresql_16` as a Nix build-time dependency.
SamirTalwar c6da54c
Add libclang too.
SamirTalwar 5cd7e1c
sqlx turned out to be adequate
plcplc 496f7a6
Remove leftovers
plcplc 73186bc
linting
plcplc 459fccc
Move things around
e3d88ab
machete
eb53b3a
lint
739c77a
use one kind
98c0542
rename and move native operation commands
c44b4fd
Add list command
5f292a4
handle nullability of columns
3736972
add a test
4d866fb
comment
53066aa
comments and lints
2d85944
pass a connection string
9e72090
lint
bf8d2ea
Merge branch 'main' into gjm-tjh/native-query-cli
199fbee
Merge branch 'main' into gjm-tjh/native-query-cli
i-am-tom File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,229 @@ | ||
//! Handle the creation of Native Operations. | ||
|
||
use std::path::PathBuf; | ||
|
||
use super::{update, Context}; | ||
use ndc_postgres_configuration as configuration; | ||
use ndc_postgres_configuration::environment::Environment; | ||
|
||
pub use configuration::version4::native_operations::Kind; | ||
|
||
/// Commands on Native Operations. | ||
#[derive(Debug, Clone, clap::Subcommand)] | ||
pub enum Command { | ||
/// List the existing Native Operations. | ||
List, | ||
/// Create a new Native Operation from a SQL file. | ||
Create { | ||
/// Relative path to the SQL file inside the connector configuration directory. | ||
#[arg(long)] | ||
operation_path: PathBuf, | ||
|
||
/// Operation kind. | ||
#[arg(long)] | ||
kind: Kind, | ||
|
||
/// Override the Native Operation definition if it exists. | ||
#[arg(long)] | ||
r#override: bool, | ||
}, | ||
/// Delete an existing Native Operation from the configuration. | ||
Delete { | ||
/// The name of the Native Operation. | ||
#[arg(long)] | ||
name: String, | ||
|
||
/// Operation kind. | ||
#[arg(long)] | ||
kind: Kind, | ||
}, | ||
} | ||
|
||
/// Run a command in a given directory. | ||
pub async fn run(command: Command, context: Context<impl Environment>) -> anyhow::Result<()> { | ||
match command { | ||
Command::List => list(context).await?, | ||
Command::Create { | ||
operation_path, | ||
kind, | ||
r#override, | ||
} => { | ||
create( | ||
context, | ||
operation_path, | ||
kind, | ||
if r#override { | ||
Override::Yes | ||
} else { | ||
Override::No | ||
}, | ||
) | ||
.await?; | ||
} | ||
Command::Delete { name, kind } => { | ||
delete(context, name, kind).await?; | ||
} | ||
}; | ||
Ok(()) | ||
} | ||
|
||
/// List all native operations. | ||
async fn list(context: Context<impl Environment>) -> anyhow::Result<()> { | ||
// Read the configuration. | ||
let mut configuration = | ||
configuration::parse_configuration(context.context_path.clone()).await?; | ||
|
||
match configuration { | ||
configuration::ParsedConfiguration::Version3(_) => Err(anyhow::anyhow!( | ||
"To use the native operations commands, please upgrade to the latest version." | ||
))?, | ||
configuration::ParsedConfiguration::Version4(ref mut configuration) => { | ||
let operations = &configuration.metadata.native_queries.0; | ||
println!("Native Queries:"); | ||
for native_operation in operations.iter().filter(|op| !op.1.is_procedure) { | ||
println!("- {}", native_operation.0); | ||
} | ||
println!("Native Mutations:"); | ||
for native_operation in operations.iter().filter(|op| op.1.is_procedure) { | ||
println!("- {}", native_operation.0); | ||
} | ||
} | ||
}; | ||
Ok(()) | ||
} | ||
|
||
/// Override Native Operation definition if exists? | ||
#[derive(Debug, Clone, clap::ValueEnum)] | ||
enum Override { | ||
Yes, | ||
No, | ||
} | ||
|
||
/// Take a SQL file containing a Native Operation, check against the database that it is valid, | ||
/// and add it to the configuration if it is. | ||
async fn create( | ||
context: Context<impl Environment>, | ||
operation_path: PathBuf, | ||
kind: Kind, | ||
override_entry: Override, | ||
) -> anyhow::Result<()> { | ||
// Read the configuration. | ||
let mut configuration = | ||
configuration::parse_configuration(context.context_path.clone()).await?; | ||
|
||
// Prepare the Native Operation SQL so it can be checked against the db. | ||
let name = operation_path | ||
.file_stem() | ||
.ok_or(anyhow::anyhow!("SQL file not found"))? | ||
.to_str() | ||
.ok_or(anyhow::anyhow!("Could not convert SQL file name to string"))? | ||
.to_string(); | ||
|
||
// Read the SQL file. | ||
let file_contents = match std::fs::read_to_string(context.context_path.join(&operation_path)) { | ||
Ok(ok) => ok, | ||
Err(err) => anyhow::bail!("{}: {}", operation_path.display(), err), | ||
}; | ||
|
||
match configuration { | ||
configuration::ParsedConfiguration::Version3(_) => Err(anyhow::anyhow!( | ||
"To use the native operations commands, please upgrade to the latest version." | ||
))?, | ||
configuration::ParsedConfiguration::Version4(ref mut configuration) => { | ||
let connection_string = configuration.get_connection_uri()?; | ||
|
||
let new_native_operation = configuration::version4::native_operations::create( | ||
configuration, | ||
&connection_string, | ||
&operation_path, | ||
&file_contents, | ||
kind, | ||
) | ||
.await?; | ||
|
||
// Add the new native operation to the configuration. | ||
match override_entry { | ||
Override::Yes => { | ||
configuration | ||
.metadata | ||
.native_queries | ||
.0 | ||
.insert(name, new_native_operation); | ||
} | ||
Override::No => { | ||
// Only insert if vacant. | ||
if let std::collections::btree_map::Entry::Vacant(entry) = | ||
configuration.metadata.native_queries.0.entry(name.clone()) | ||
{ | ||
entry.insert(new_native_operation); | ||
} else { | ||
anyhow::bail!("A Native Operation with the name '{}' already exists. To override, use the --override flag.", name); | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
|
||
// We write the configuration including the new Native Operation to file. | ||
configuration::write_parsed_configuration(configuration, context.context_path.clone()).await?; | ||
|
||
// We update the configuration as well so that the introspection will add missing scalar type entries if necessary. | ||
update(context).await | ||
} | ||
|
||
/// Delete a Native Operation by name. | ||
async fn delete( | ||
context: Context<impl Environment>, | ||
name: String, | ||
kind: Kind, | ||
) -> anyhow::Result<()> { | ||
// Read the configuration. | ||
let mut configuration = | ||
configuration::parse_configuration(context.context_path.clone()).await?; | ||
|
||
let error_message_not_exist = format!( | ||
"A Native {} with the name '{}' does not exists.", | ||
match kind { | ||
Kind::Mutation => "Mutation", | ||
Kind::Query => "Query", | ||
}, | ||
name | ||
); | ||
|
||
match configuration { | ||
configuration::ParsedConfiguration::Version3(_) => Err(anyhow::anyhow!( | ||
"To use the native operations commands, please upgrade to the latest version." | ||
))?, | ||
configuration::ParsedConfiguration::Version4(ref mut configuration) => { | ||
// Delete if exists and is of the same type, error if not. | ||
match configuration.metadata.native_queries.0.entry(name.clone()) { | ||
std::collections::btree_map::Entry::Occupied(entry) => { | ||
let value = entry.get(); | ||
if value.is_procedure { | ||
match kind { | ||
Kind::Mutation => { | ||
entry.remove_entry(); | ||
} | ||
Kind::Query => { | ||
anyhow::bail!(format!("{error_message_not_exist}\n Did you mean the Native Mutation with the same name?")); | ||
} | ||
} | ||
} else { | ||
match kind { | ||
Kind::Mutation => { | ||
anyhow::bail!(format!("{error_message_not_exist}\n Did you mean the Native Query with the same name?")); | ||
} | ||
Kind::Query => { | ||
entry.remove_entry(); | ||
} | ||
} | ||
} | ||
} | ||
std::collections::btree_map::Entry::Vacant(_) => { | ||
anyhow::bail!(error_message_not_exist); | ||
} | ||
} | ||
} | ||
} | ||
Ok(()) | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We introduce a method to prettyprint a native query.