Skip to content

Commit

Permalink
update command should not override the config file in some cases
Browse files Browse the repository at this point in the history
  • Loading branch information
Gil Mizrahi committed Mar 14, 2024
1 parent ebb7b53 commit 076ec88
Show file tree
Hide file tree
Showing 12 changed files with 74 additions and 37 deletions.
87 changes: 62 additions & 25 deletions crates/cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ use tokio::fs;
use ndc_postgres_configuration as configuration;
use ndc_postgres_configuration::environment::Environment;

const UPDATE_ATTEMPTS: u8 = 3;

/// The various contextual bits and bobs we need to run.
pub struct Context<Env: Environment> {
pub context_path: PathBuf,
Expand Down Expand Up @@ -128,30 +130,65 @@ async fn initialize(with_metadata: bool, context: Context<impl Environment>) ->
///
/// This expects a configuration with a valid connection URI.
async fn update(context: Context<impl Environment>) -> anyhow::Result<()> {
let configuration_file_path = context
.context_path
.join(configuration::CONFIGURATION_FILENAME);
let input: configuration::RawConfiguration = {
let configuration_file_contents = fs::read_to_string(&configuration_file_path)
.await
.map_err(|err| {
if err.kind() == std::io::ErrorKind::NotFound {
anyhow::anyhow!(
"{}: No such file or directory. Perhaps you meant to 'initialize' first?",
configuration_file_path.display()
)
} else {
anyhow::anyhow!(err)
}
})?;
serde_json::from_str(&configuration_file_contents)?
};
let output = configuration::introspect(input, &context.environment).await?;
// It is possible to change the file in the middle of introspection.
// We want to detect these scenario and try again, or fail if we are unable to.
// We do that with a few attempts.
for _attempt in 1..UPDATE_ATTEMPTS {
let configuration_file_path = context
.context_path
.join(configuration::CONFIGURATION_FILENAME);
let input: configuration::RawConfiguration = {
let configuration_file_contents =
read_config_file_contents(&configuration_file_path).await?;
serde_json::from_str(&configuration_file_contents)?
};
let output = configuration::introspect(input.clone(), &context.environment).await?;

fs::write(
&configuration_file_path,
serde_json::to_string_pretty(&output)?,
)
.await?;
Ok(())
// Check that the input file did not change since we started introspecting.
let input_again_before_write: configuration::RawConfiguration = {
let configuration_file_contents =
read_config_file_contents(&configuration_file_path).await?;
serde_json::from_str(&configuration_file_contents)?
};

if input_again_before_write != input {
println!("Input file changed before write, trying again.");
// next attempt
continue;
}

// If the introspection result is different than the current config,
// change it. Otherwise, continue.
if input != output {
fs::write(
&configuration_file_path,
serde_json::to_string_pretty(&output)?,
)
.await?;
} else {
println!("The configuration is up-to-date. Nothing to do.");
}

return Ok(());
}

// We ran out of attempts.
Err(anyhow::anyhow!(
"Cannot override configuration: input changed before write."
))
}

async fn read_config_file_contents(configuration_file_path: &PathBuf) -> anyhow::Result<String> {
fs::read_to_string(configuration_file_path)
.await
.map_err(|err| {
if err.kind() == std::io::ErrorKind::NotFound {
anyhow::anyhow!(
"{}: No such file or directory. Perhaps you meant to 'initialize' first?",
configuration_file_path.display()
)
} else {
anyhow::anyhow!(err)
}
})
}
2 changes: 1 addition & 1 deletion crates/configuration/src/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub const CONFIGURATION_FILENAME: &str = "configuration.json";
pub const CONFIGURATION_JSONSCHEMA_FILENAME: &str = "schema.json";

/// The parsed connector configuration.
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize, JsonSchema)]
#[serde(tag = "version")]
pub enum RawConfiguration {
#[serde(rename = "3")]
Expand Down
2 changes: 1 addition & 1 deletion crates/configuration/src/values/isolation_level.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

/// The isolation level of the transaction in which a query is executed.
#[derive(Debug, Clone, Copy, Default, Deserialize, Serialize, JsonSchema)]
#[derive(Debug, PartialEq, Eq, Clone, Copy, Default, Deserialize, Serialize, JsonSchema)]
pub enum IsolationLevel {
/// Prevents reading data from another uncommitted transaction.
#[default]
Expand Down
2 changes: 1 addition & 1 deletion crates/configuration/src/values/pool_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

/// Settings for the PostgreSQL connection pool
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct PoolSettings {
/// maximum number of pool connections
Expand Down
2 changes: 1 addition & 1 deletion crates/configuration/src/version3/comparison.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

/// Define the names that comparison operators will be exposed as by the automatic introspection.
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct ComparisonOperatorMapping {
/// The name of the operator as defined by the database
Expand Down
2 changes: 1 addition & 1 deletion crates/configuration/src/version3/connection_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
pub const DEFAULT_CONNECTION_URI_VARIABLE: &str = "CONNECTION_URI";

/// Database connection settings.
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct DatabaseConnectionSettings {
/// Connection string for a Postgres-compatible database.
Expand Down
2 changes: 1 addition & 1 deletion crates/configuration/src/version3/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const CONFIGURATION_QUERY: &str = include_str!("version3.sql");

/// Initial configuration, just enough to connect to a database and elaborate a full
/// 'Configuration'.
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct RawConfiguration {
/// Jsonschema of the configuration format.
Expand Down
2 changes: 1 addition & 1 deletion crates/configuration/src/version3/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
use super::comparison::ComparisonOperatorMapping;

/// Options which only influence how the configuration is updated.
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct IntrospectionOptions {
/// Schemas which are excluded from introspection. The default setting will exclude the
Expand Down
4 changes: 2 additions & 2 deletions crates/query-engine/metadata/src/metadata/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, BTreeSet};

/// Map of all known composite types.
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema)]
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct CompositeTypes(pub BTreeMap<String, CompositeType>);

Expand Down Expand Up @@ -79,7 +79,7 @@ fn default_true() -> bool {
}

/// Mapping from a "table" name to its information.
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema)]
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct TablesInfo(pub BTreeMap<String, TableInfo>);

Expand Down
2 changes: 1 addition & 1 deletion crates/query-engine/metadata/src/metadata/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

/// Metadata information.
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct Metadata {
#[serde(default)]
Expand Down
2 changes: 1 addition & 1 deletion crates/query-engine/metadata/src/metadata/mutations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

/// Which version of the generated mutations will be included in the schema
#[derive(Debug, Clone, Copy, Deserialize, Serialize, JsonSchema)]
#[derive(Debug, PartialEq, Eq, Clone, Copy, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub enum MutationsVersion {
V1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::collections::BTreeMap;
// Types

/// Metadata information of native queries.
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema)]
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct NativeQueries(pub BTreeMap<String, NativeQueryInfo>);

Expand Down

0 comments on commit 076ec88

Please sign in to comment.