这是indexloc提供的服务,不要输入任何密码
Skip to content

ignore existing configuration when regenerating native query #119

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
Show file tree
Hide file tree
Changes from all 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
34 changes: 20 additions & 14 deletions crates/cli/src/native_query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use configuration::schema::ObjectField;
use configuration::{
native_query::NativeQueryRepresentation::Collection, serialized::NativeQuery, Configuration,
};
use configuration::{read_directory, WithName};
use configuration::{read_directory_with_ignored_configs, WithName};
use mongodb_support::aggregate::Pipeline;
use ndc_models::CollectionName;
use tokio::fs;
Expand Down Expand Up @@ -57,7 +57,25 @@ pub async fn run(context: &Context, command: Command) -> anyhow::Result<()> {
force,
pipeline_path,
} => {
let configuration = match read_directory(&context.path).await {
let native_query_path = {
let path = get_native_query_path(context, &name);
if !force && fs::try_exists(&path).await? {
eprintln!(
"A native query named {name} already exists at {}.",
path.to_string_lossy()
);
eprintln!("Re-run with --force to overwrite.");
exit(ExitCode::RefusedToOverwrite.into())
}
path
};

let configuration = match read_directory_with_ignored_configs(
&context.path,
&[native_query_path.clone()],
)
.await
{
Ok(c) => c,
Err(err) => {
eprintln!("Could not read connector configuration - configuration must be initialized before creating native queries.\n\n{err:#}");
Expand All @@ -76,18 +94,6 @@ pub async fn run(context: &Context, command: Command) -> anyhow::Result<()> {
exit(ExitCode::CouldNotReadAggregationPipeline.into())
}
};
let native_query_path = {
let path = get_native_query_path(context, &name);
if !force && fs::try_exists(&path).await? {
eprintln!(
"A native query named {name} already exists at {}.",
path.to_string_lossy()
);
eprintln!("Re-run with --force to overwrite.");
exit(ExitCode::RefusedToOverwrite.into())
}
path
};
let native_query =
match native_query_from_pipeline(&configuration, &name, collection, pipeline) {
Ok(q) => WithName::named(name, q),
Expand Down
4 changes: 3 additions & 1 deletion crates/cli/src/native_query/type_solver/simplify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,11 @@ fn simplify_constraint_pair(
b: TypeConstraint,
) -> Simplified<TypeConstraint> {
match (a, b) {
(C::ExtendedJSON, _) | (_, C::ExtendedJSON) => Ok(C::ExtendedJSON),
(C::ExtendedJSON, _) | (_, C::ExtendedJSON) => Ok(C::ExtendedJSON), // TODO: Do we want this in contravariant case?
(C::Scalar(a), C::Scalar(b)) => solve_scalar(variance, a, b),

// TODO: We need to make sure we aren't putting multiple layers of Nullable on constraints
// - if a and b have mismatched levels of Nullable they won't unify
(C::Nullable(a), C::Nullable(b)) => {
simplify_constraint_pair(configuration, object_type_constraints, variance, *a, *b)
.map(|constraint| C::Nullable(Box::new(constraint)))
Expand Down
107 changes: 97 additions & 10 deletions crates/configuration/src/directory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,25 +41,35 @@ const YAML: FileFormat = FileFormat::Yaml;
/// Read configuration from a directory
pub async fn read_directory(
configuration_dir: impl AsRef<Path> + Send,
) -> anyhow::Result<Configuration> {
read_directory_with_ignored_configs(configuration_dir, &[]).await
}

/// Read configuration from a directory
pub async fn read_directory_with_ignored_configs(
configuration_dir: impl AsRef<Path> + Send,
ignored_configs: &[PathBuf],
) -> anyhow::Result<Configuration> {
let dir = configuration_dir.as_ref();

let schemas = read_subdir_configs::<String, Schema>(&dir.join(SCHEMA_DIRNAME))
let schemas = read_subdir_configs::<String, Schema>(&dir.join(SCHEMA_DIRNAME), ignored_configs)
.await?
.unwrap_or_default();
let schema = schemas.into_values().fold(Schema::default(), Schema::merge);

// Deprecated see message above at NATIVE_PROCEDURES_DIRNAME
let native_procedures = read_subdir_configs(&dir.join(NATIVE_PROCEDURES_DIRNAME))
.await?
.unwrap_or_default();
let native_procedures =
read_subdir_configs(&dir.join(NATIVE_PROCEDURES_DIRNAME), ignored_configs)
.await?
.unwrap_or_default();

// TODO: Once we fully remove `native_procedures` after a deprecation period we can remove `mut`
let mut native_mutations = read_subdir_configs(&dir.join(NATIVE_MUTATIONS_DIRNAME))
.await?
.unwrap_or_default();
let mut native_mutations =
read_subdir_configs(&dir.join(NATIVE_MUTATIONS_DIRNAME), ignored_configs)
.await?
.unwrap_or_default();

let native_queries = read_subdir_configs(&dir.join(NATIVE_QUERIES_DIRNAME))
let native_queries = read_subdir_configs(&dir.join(NATIVE_QUERIES_DIRNAME), ignored_configs)
.await?
.unwrap_or_default();

Expand All @@ -75,7 +85,10 @@ pub async fn read_directory(
/// json and yaml files in the given directory should be parsed as native mutation configurations.
///
/// Assumes that every configuration file has a `name` field.
async fn read_subdir_configs<N, T>(subdir: &Path) -> anyhow::Result<Option<BTreeMap<N, T>>>
async fn read_subdir_configs<N, T>(
subdir: &Path,
ignored_configs: &[PathBuf],
) -> anyhow::Result<Option<BTreeMap<N, T>>>
where
for<'a> T: Deserialize<'a>,
for<'a> N: Ord + ToString + Deserialize<'a>,
Expand All @@ -97,6 +110,13 @@ where
let path = dir_entry.path();
let extension = path.extension().and_then(|ext| ext.to_str());

if ignored_configs
.iter()
.any(|ignored| path.ends_with(ignored))
{
return Ok(None);
}

let format_option = extension
.and_then(|ext| {
CONFIGURATION_EXTENSIONS
Expand Down Expand Up @@ -240,7 +260,7 @@ pub async fn list_existing_schemas(
let dir = configuration_dir.as_ref();

// TODO: we don't really need to read and parse all the schema files here, just get their names.
let schemas = read_subdir_configs::<_, Schema>(&dir.join(SCHEMA_DIRNAME))
let schemas = read_subdir_configs::<_, Schema>(&dir.join(SCHEMA_DIRNAME), &[])
.await?
.unwrap_or_default();

Expand Down Expand Up @@ -290,11 +310,22 @@ pub async fn get_config_file_changed(dir: impl AsRef<Path>) -> anyhow::Result<bo

#[cfg(test)]
mod tests {
use std::path::PathBuf;

use async_tempfile::TempDir;
use googletest::prelude::*;
use mongodb_support::BsonScalarType;
use ndc_models::FunctionName;
use serde_json::json;
use tokio::fs;

use crate::{
native_query::NativeQuery,
read_directory_with_ignored_configs,
schema::{ObjectField, ObjectType, Type},
serialized, WithName, NATIVE_QUERIES_DIRNAME,
};

use super::{read_directory, CONFIGURATION_OPTIONS_BASENAME};

#[googletest::test]
Expand Down Expand Up @@ -328,4 +359,60 @@ mod tests {

Ok(())
}

#[googletest::test]
#[tokio::test]
async fn ignores_specified_config_files() -> anyhow::Result<()> {
let native_query = WithName {
name: "hello".to_string(),
value: serialized::NativeQuery {
representation: crate::native_query::NativeQueryRepresentation::Function,
input_collection: None,
arguments: Default::default(),
result_document_type: "Hello".into(),
object_types: [(
"Hello".into(),
ObjectType {
fields: [(
"__value".into(),
ObjectField {
r#type: Type::Scalar(BsonScalarType::String),
description: None,
},
)]
.into(),
description: None,
},
)]
.into(),
pipeline: [].into(),
description: None,
},
};

let config_dir = TempDir::new().await?;
tokio::fs::create_dir(config_dir.join(NATIVE_QUERIES_DIRNAME)).await?;
let native_query_path = PathBuf::from(NATIVE_QUERIES_DIRNAME).join("hello.json");
fs::write(
config_dir.join(&native_query_path),
serde_json::to_vec(&native_query)?,
)
.await?;

let parsed_config = read_directory(&config_dir).await?;
let parsed_config_ignoring_native_query =
read_directory_with_ignored_configs(config_dir, &[native_query_path]).await?;

expect_that!(
parsed_config.native_queries,
unordered_elements_are!(eq((
&FunctionName::from("hello"),
&NativeQuery::from_serialized(&Default::default(), native_query.value)?
))),
);

expect_that!(parsed_config_ignoring_native_query.native_queries, empty());

Ok(())
}
}
2 changes: 1 addition & 1 deletion crates/configuration/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ pub use crate::configuration::Configuration;
pub use crate::directory::get_config_file_changed;
pub use crate::directory::list_existing_schemas;
pub use crate::directory::parse_configuration_options_file;
pub use crate::directory::read_directory;
pub use crate::directory::write_schema_directory;
pub use crate::directory::{read_directory, read_directory_with_ignored_configs};
pub use crate::directory::{
CONFIGURATION_OPTIONS_BASENAME, CONFIGURATION_OPTIONS_METADATA, NATIVE_MUTATIONS_DIRNAME,
NATIVE_QUERIES_DIRNAME, SCHEMA_DIRNAME,
Expand Down
2 changes: 1 addition & 1 deletion crates/configuration/src/native_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::serialized;
/// Note: this type excludes `name` and `object_types` from the serialized type. Object types are
/// intended to be merged into one big map so should not be accessed through values of this type.
/// Native query values are stored in maps so names should be taken from map keys.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
pub struct NativeQuery {
pub representation: NativeQueryRepresentation,
pub input_collection: Option<ndc::CollectionName>,
Expand Down
1 change: 1 addition & 0 deletions crates/configuration/src/serialized/native_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pub struct NativeQuery {

/// Use `input_collection` when you want to start an aggregation pipeline off of the specified
/// `input_collection` db.<input_collection>.aggregate.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub input_collection: Option<ndc_models::CollectionName>,

/// Arguments to be supplied for each query invocation. These will be available to the given
Expand Down
Loading