From 0600685eb775c74153684c4632de568c5da5cbdb Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 3 Jun 2026 08:03:28 +0000 Subject: [PATCH 01/15] feat(supertoml): import config from TOML/JSON via the export endpoints POSTing a TOML/JSON body to /config/toml or /config/json now imports that config into the workspace (GET, or POST with an empty body, still exports). The body is parsed and fully validated into a DetailedConfig, then persisted in a single transaction: dimensions and default-configs first, then the contexts that reference them. Behaviour is controlled via request headers: - x-import-mode: merge (default, upsert) | replace (mirror, deletes entities absent from the file) - x-import-overwrite: skip existing entities when false - x-import-on-error: abort (default) | continue (per-entity errors via savepoints, applies the rest) - x-import-dry-run: validate + summarise without writing (rolls back) - x-import-value-merge: deep-merge object-valued default configs Returns a JSON summary of created/updated/skipped/deleted counts (and the new config version). Also raises the app-wide raw payload limit to 10MB so large workspaces can be imported, and adds docs plus unit tests for the option parsing and value-merge logic. --- crates/context_aware_config/src/api/config.rs | 1 + .../src/api/config/handlers.rs | 42 +- .../src/api/config/import.rs | 724 ++++++++++++++++++ crates/superposition/src/main.rs | 5 +- .../import-export.md | 115 +++ docs/docs/superposition-config-file/intro.md | 1 + 6 files changed, 885 insertions(+), 3 deletions(-) create mode 100644 crates/context_aware_config/src/api/config/import.rs create mode 100644 docs/docs/superposition-config-file/import-export.md diff --git a/crates/context_aware_config/src/api/config.rs b/crates/context_aware_config/src/api/config.rs index 62a998ddb..fc07c1737 100644 --- a/crates/context_aware_config/src/api/config.rs +++ b/crates/context_aware_config/src/api/config.rs @@ -1,3 +1,4 @@ mod handlers; pub use handlers::endpoints; pub mod helpers; +pub mod import; diff --git a/crates/context_aware_config/src/api/config/handlers.rs b/crates/context_aware_config/src/api/config/handlers.rs index bd8f73488..4dbe8027f 100644 --- a/crates/context_aware_config/src/api/config/handlers.rs +++ b/crates/context_aware_config/src/api/config/handlers.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use actix_web::{ HttpRequest, HttpResponse, Scope, get, put, routes, - web::{Data, Header, Json, Path, Query}, + web::{Bytes, Data, Header, Json, Path, Query}, }; use chrono::{DateTime, Utc}; use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl, SelectableHelper}; @@ -11,7 +11,9 @@ use serde_json::{Map, Value, json}; use service_utils::{ helpers::{fetch_dimensions_info_map, is_not_modified}, redis::{CONFIG_KEY_SUFFIX, LAST_MODIFIED_KEY_SUFFIX, read_through_cache}, - service::types::{AppHeader, AppState, DbConnection, WorkspaceContext}, + service::types::{ + AppHeader, AppState, CustomHeaders, DbConnection, WorkspaceContext, + }, }; use superposition_core::{ ConfigFormat, JsonFormat, TomlFormat, @@ -573,6 +575,9 @@ async fn get_handler( #[post("/toml")] async fn get_toml_handler( req: HttpRequest, + body: Bytes, + user: User, + custom_headers: CustomHeaders, db_conn: DbConnection, workspace_context: WorkspaceContext, state: Data, @@ -580,6 +585,21 @@ async fn get_toml_handler( let DbConnection(mut conn) = db_conn; let is_smithy = matches!(req.method(), &actix_web::http::Method::POST); + // A non-empty body on POST means "import this config"; the export path + // (GET, or POST with an empty body) is left unchanged. + if is_smithy && !body.is_empty() { + return super::import::handle_import::( + &body, + &req, + custom_headers, + &user, + &workspace_context, + &state, + &mut conn, + ) + .await; + } + let max_created_at = read_through_cache::>( format!( "{}{LAST_MODIFIED_KEY_SUFFIX}", @@ -620,6 +640,9 @@ async fn get_toml_handler( #[post("/json")] async fn get_json_handler( req: HttpRequest, + body: Bytes, + user: User, + custom_headers: CustomHeaders, db_conn: DbConnection, workspace_context: WorkspaceContext, state: Data, @@ -627,6 +650,21 @@ async fn get_json_handler( let DbConnection(mut conn) = db_conn; let is_smithy = matches!(req.method(), &actix_web::http::Method::POST); + // A non-empty body on POST means "import this config"; the export path + // (GET, or POST with an empty body) is left unchanged. + if is_smithy && !body.is_empty() { + return super::import::handle_import::( + &body, + &req, + custom_headers, + &user, + &workspace_context, + &state, + &mut conn, + ) + .await; + } + let max_created_at = read_through_cache::>( format!( "{}{LAST_MODIFIED_KEY_SUFFIX}", diff --git a/crates/context_aware_config/src/api/config/import.rs b/crates/context_aware_config/src/api/config/import.rs new file mode 100644 index 000000000..d7957c6fc --- /dev/null +++ b/crates/context_aware_config/src/api/config/import.rs @@ -0,0 +1,724 @@ +//! Import support for SuperTOML. +//! +//! This module powers the "import" side of the TOML/JSON config endpoints. +//! When a body is POSTed to `/config/toml` or `/config/json`, it is parsed +//! (and fully validated) into a [`DetailedConfig`] via the format's +//! [`ConfigFormat::parse_into_detailed`] and then persisted to the workspace. +//! +//! The behaviour is controlled through request headers: +//! - `x-import-mode`: `merge` (default) upserts the entities in the file and +//! leaves everything else untouched; `replace` additionally deletes any +//! dimension/default-config/context that is absent from the file (mirror). +//! - `x-import-overwrite`: `true` (default) updates entities that already +//! exist; `false` skips them (only new entities are created). +//! - `x-import-on-error`: `abort` (default) fails the whole import on the +//! first error; `continue` records per-entity errors and applies the rest. +//! - `x-import-dry-run`: `true` parses, validates and computes the summary +//! without persisting anything (the transaction is rolled back). +//! - `x-import-value-merge`: `true` deep-merges object-valued default configs +//! with their existing value instead of replacing them wholesale. + +use std::collections::HashSet; + +use actix_web::{web::Data, HttpRequest, HttpResponse}; +use chrono::Utc; +use diesel::{ + Connection, ExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl, +}; +use serde::Serialize; +use serde_json::{Map, Value}; +use service_utils::{ + helpers::{execute_webhook_call, parse_config_tags, WebhookData}, + service::types::{AppState, CustomHeaders, SchemaName, WorkspaceContext}, +}; +use superposition_core::{helpers::calculate_context_weight, ConfigFormat}; +use superposition_macros::{bad_argument, db_error}; +use superposition_types::{ + api::webhook::Action, + database::models::{ + cac::{Context as DbContext, DefaultConfig, Dimension, Position}, + others::WebhookEvent, + ChangeReason, Description, + }, + database::schema::{ + contexts::dsl as ctx_dsl, default_configs::dsl as dc_dsl, + dimensions::dsl as dim_dsl, + }, + result as superposition, Context as ConfigContext, DBConnection, DefaultConfigInfo, + DimensionInfo, ExtendedMap, Overrides, Resource, User, +}; + +use crate::{ + api::context::operations, + helpers::{add_config_version, put_config_in_redis}, +}; + +// --------------------------------------------------------------------------- +// Options +// --------------------------------------------------------------------------- + +#[derive(Clone, Copy, PartialEq, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum ImportMode { + Merge, + Replace, +} + +#[derive(Clone, Copy, PartialEq)] +pub enum OnError { + Abort, + Continue, +} + +#[derive(Clone, Copy)] +pub struct ImportOptions { + pub mode: ImportMode, + pub overwrite: bool, + pub on_error: OnError, + pub dry_run: bool, + pub value_merge: bool, +} + +fn header_bool(req: &HttpRequest, name: &str, default: bool) -> bool { + req.headers() + .get(name) + .and_then(|v| v.to_str().ok()) + .and_then(|s| s.parse::().ok()) + .unwrap_or(default) +} + +impl ImportOptions { + pub fn from_request(req: &HttpRequest) -> superposition::Result { + let mode = match req + .headers() + .get("x-import-mode") + .and_then(|v| v.to_str().ok()) + { + None => ImportMode::Merge, + Some(s) if s.eq_ignore_ascii_case("merge") => ImportMode::Merge, + Some(s) if s.eq_ignore_ascii_case("replace") => ImportMode::Replace, + Some(s) => { + return Err(bad_argument!( + "Invalid x-import-mode '{}', expected 'merge' or 'replace'", + s + )) + } + }; + let on_error = match req + .headers() + .get("x-import-on-error") + .and_then(|v| v.to_str().ok()) + { + None => OnError::Abort, + Some(s) if s.eq_ignore_ascii_case("abort") => OnError::Abort, + Some(s) if s.eq_ignore_ascii_case("continue") => OnError::Continue, + Some(s) => { + return Err(bad_argument!( + "Invalid x-import-on-error '{}', expected 'abort' or 'continue'", + s + )) + } + }; + Ok(Self { + mode, + overwrite: header_bool(req, "x-import-overwrite", true), + on_error, + dry_run: header_bool(req, "x-import-dry-run", false), + value_merge: header_bool(req, "x-import-value-merge", false), + }) + } +} + +// --------------------------------------------------------------------------- +// Summary +// --------------------------------------------------------------------------- + +#[derive(Serialize)] +pub struct ImportError { + pub id: String, + pub error: String, +} + +#[derive(Default, Serialize)] +pub struct EntityReport { + pub created: usize, + pub updated: usize, + pub skipped: usize, + pub deleted: usize, + #[serde(skip_serializing_if = "Vec::is_empty")] + pub errors: Vec, +} + +impl EntityReport { + fn record(&mut self, outcome: Outcome) { + match outcome { + Outcome::Created => self.created += 1, + Outcome::Updated => self.updated += 1, + Outcome::Skipped => self.skipped += 1, + Outcome::Deleted => self.deleted += 1, + } + } +} + +#[derive(Serialize)] +pub struct ImportSummary { + pub mode: ImportMode, + pub dry_run: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub config_version: Option, + pub dimensions: EntityReport, + pub default_configs: EntityReport, + pub contexts: EntityReport, +} + +impl ImportSummary { + fn new(opts: &ImportOptions) -> Self { + Self { + mode: opts.mode, + dry_run: opts.dry_run, + config_version: None, + dimensions: EntityReport::default(), + default_configs: EntityReport::default(), + contexts: EntityReport::default(), + } + } +} + +#[derive(Clone, Copy)] +enum Outcome { + Created, + Updated, + Skipped, + Deleted, +} + +// --------------------------------------------------------------------------- +// Transaction error plumbing +// --------------------------------------------------------------------------- + +/// Error type used inside the import transaction. `DryRun` carries the summary +/// out of the (deliberately rolled-back) transaction so it can still be +/// returned to the caller. +enum TxError { + App(superposition::AppError), + DryRun(ImportSummary), +} + +impl From for TxError { + fn from(e: superposition::AppError) -> Self { + TxError::App(e) + } +} + +impl From for TxError { + fn from(e: diesel::result::Error) -> Self { + TxError::App(db_error!(e)) + } +} + +/// Run `f` within a SAVEPOINT so that a failure rolls back only the work done +/// by `f` (leaving the surrounding transaction usable). This is what makes the +/// `continue-on-error` option safe in the face of Postgres aborting the +/// current transaction on a database error. +fn with_savepoint( + conn: &mut DBConnection, + name: &str, + f: impl FnOnce(&mut DBConnection) -> superposition::Result, +) -> superposition::Result { + diesel::sql_query(format!("SAVEPOINT {name}")) + .execute(conn) + .map_err(|e| db_error!(e))?; + match f(conn) { + Ok(v) => { + diesel::sql_query(format!("RELEASE SAVEPOINT {name}")) + .execute(conn) + .map_err(|e| db_error!(e))?; + Ok(v) + } + Err(e) => { + diesel::sql_query(format!("ROLLBACK TO SAVEPOINT {name}")) + .execute(conn) + .map_err(|e| db_error!(e))?; + Err(e) + } + } +} + +/// Apply the `on-error` policy to a single entity's result, recording it into +/// the report. Returns `Err` only when the policy is `abort`. +fn apply_outcome( + report: &mut EntityReport, + on_error: OnError, + id: &str, + result: superposition::Result, +) -> Result<(), TxError> { + match result { + Ok(outcome) => { + report.record(outcome); + Ok(()) + } + Err(e) => match on_error { + OnError::Continue => { + report.errors.push(ImportError { + id: id.to_string(), + error: e.message(), + }); + Ok(()) + } + OnError::Abort => Err(TxError::App(e)), + }, + } +} + +fn import_change_reason() -> ChangeReason { + ChangeReason::try_from("Imported via SuperTOML config import".to_string()) + .unwrap_or_default() +} + +fn import_description() -> Description { + Description::try_from("Config imported via TOML/JSON import".to_string()) + .unwrap_or_default() +} + +/// Deep-merge two JSON values, with `overlay` winning at the leaves. Objects +/// are merged key-by-key recursively; any other shape is replaced wholesale. +fn deep_merge(base: &Value, overlay: &Value) -> Value { + match (base, overlay) { + (Value::Object(base_map), Value::Object(overlay_map)) => { + let mut merged: Map = base_map.clone(); + for (k, v) in overlay_map { + let next = match merged.get(k) { + Some(existing) => deep_merge(existing, v), + None => v.clone(), + }; + merged.insert(k.clone(), next); + } + Value::Object(merged) + } + _ => overlay.clone(), + } +} + +// --------------------------------------------------------------------------- +// Per-entity writers +// --------------------------------------------------------------------------- + +fn write_dimension( + conn: &mut DBConnection, + schema_name: &SchemaName, + name: &str, + info: &DimensionInfo, + opts: &ImportOptions, + email: &str, +) -> superposition::Result { + let exists = dim_dsl::dimensions + .filter(dim_dsl::dimension.eq(name)) + .count() + .schema_name(schema_name) + .get_result::(conn)? + > 0; + + if exists && !opts.overwrite { + return Ok(Outcome::Skipped); + } + + let position = Position::try_from(info.position).map_err(|e| { + bad_argument!("Invalid position for dimension '{}': {}", name, e) + })?; + + if exists { + diesel::update(dim_dsl::dimensions.filter(dim_dsl::dimension.eq(name))) + .set(( + dim_dsl::schema.eq(info.schema.clone()), + dim_dsl::position.eq(position), + dim_dsl::dimension_type.eq(info.dimension_type.clone()), + dim_dsl::dependency_graph.eq(info.dependency_graph.clone()), + dim_dsl::value_compute_function_name + .eq(info.value_compute_function_name.clone()), + dim_dsl::last_modified_at.eq(Utc::now()), + dim_dsl::last_modified_by.eq(email), + dim_dsl::change_reason.eq(import_change_reason()), + )) + .schema_name(schema_name) + .execute(conn)?; + Ok(Outcome::Updated) + } else { + let dimension = Dimension { + dimension: name.to_string(), + schema: info.schema.clone(), + position, + dimension_type: info.dimension_type.clone(), + dependency_graph: info.dependency_graph.clone(), + value_compute_function_name: info.value_compute_function_name.clone(), + value_validation_function_name: None, + created_at: Utc::now(), + created_by: email.to_string(), + last_modified_at: Utc::now(), + last_modified_by: email.to_string(), + description: import_description(), + change_reason: import_change_reason(), + }; + diesel::insert_into(dim_dsl::dimensions) + .values(&dimension) + .schema_name(schema_name) + .execute(conn)?; + Ok(Outcome::Created) + } +} + +fn write_default_config( + conn: &mut DBConnection, + schema_name: &SchemaName, + key: &str, + info: &DefaultConfigInfo, + opts: &ImportOptions, + email: &str, +) -> superposition::Result { + let existing: Option = dc_dsl::default_configs + .filter(dc_dsl::key.eq(key)) + .select(dc_dsl::value) + .schema_name(schema_name) + .first::(conn) + .optional()?; + let exists = existing.is_some(); + + if exists && !opts.overwrite { + return Ok(Outcome::Skipped); + } + + let value = match (opts.value_merge, &existing) { + (true, Some(existing_value)) => deep_merge(existing_value, &info.value), + _ => info.value.clone(), + }; + + let schema = ExtendedMap::try_from(info.schema.clone()).map_err(|e| { + bad_argument!("Invalid schema for default config '{}': {}", key, e) + })?; + + if exists { + diesel::update(dc_dsl::default_configs.filter(dc_dsl::key.eq(key))) + .set(( + dc_dsl::value.eq(value), + dc_dsl::schema.eq(schema), + dc_dsl::last_modified_at.eq(Utc::now()), + dc_dsl::last_modified_by.eq(email), + dc_dsl::change_reason.eq(import_change_reason()), + )) + .schema_name(schema_name) + .execute(conn)?; + Ok(Outcome::Updated) + } else { + let default_config = DefaultConfig { + key: key.to_string(), + value, + schema, + value_validation_function_name: None, + value_compute_function_name: None, + created_at: Utc::now(), + created_by: email.to_string(), + last_modified_at: Utc::now(), + last_modified_by: email.to_string(), + description: import_description(), + change_reason: import_change_reason(), + }; + diesel::insert_into(dc_dsl::default_configs) + .values(&default_config) + .schema_name(schema_name) + .execute(conn)?; + Ok(Outcome::Created) + } +} + +#[allow(clippy::too_many_arguments)] +fn write_context( + conn: &mut DBConnection, + workspace_context: &WorkspaceContext, + ctx: &ConfigContext, + overrides: &std::collections::HashMap, + dimensions: &std::collections::HashMap, + opts: &ImportOptions, + user: &User, + email: &str, +) -> superposition::Result { + let schema_name = &workspace_context.schema_name; + let override_key = ctx.override_with_keys.get_key(); + let override_ = overrides.get(override_key).cloned().ok_or_else(|| { + bad_argument!( + "Override '{}' referenced by context '{}' not found in file", + override_key, + ctx.id + ) + })?; + + let exists = ctx_dsl::contexts + .filter(ctx_dsl::id.eq(&ctx.id)) + .count() + .schema_name(schema_name) + .get_result::(conn)? + > 0; + + if exists && !opts.overwrite { + return Ok(Outcome::Skipped); + } + + let weight = calculate_context_weight(&ctx.condition, dimensions) + .map_err(|e| bad_argument!("Failed to compute context weight: {}", e))?; + + let new_ctx = DbContext { + id: ctx.id.clone(), + value: ctx.condition.clone(), + override_id: override_key.clone(), + override_, + weight, + created_at: Utc::now(), + created_by: email.to_string(), + last_modified_at: Utc::now(), + last_modified_by: email.to_string(), + description: import_description(), + change_reason: import_change_reason(), + }; + + operations::upsert(conn, true, user, workspace_context, true, new_ctx)?; + Ok(if exists { + Outcome::Updated + } else { + Outcome::Created + }) +} + +// --------------------------------------------------------------------------- +// Core import +// --------------------------------------------------------------------------- + +pub async fn import_config( + body: &str, + opts: ImportOptions, + tags: Option>, + user: &User, + workspace_context: &WorkspaceContext, + state: &Data, + conn: &mut DBConnection, +) -> superposition::Result { + let parsed = F::parse_into_detailed(body) + .map_err(|e| bad_argument!("Failed to parse config: {}", e))?; + let schema_name = &workspace_context.schema_name; + let email = user.get_email(); + + let tx_result = conn.transaction::<_, TxError, _>(|conn| { + let mut summary = ImportSummary::new(&opts); + + // Order matters for referential consistency: dimensions and default + // configs are written before contexts that reference them. + for (name, info) in &parsed.dimensions { + let res = with_savepoint(conn, "cac_import_dim", |c| { + write_dimension(c, schema_name, name, info, &opts, &email) + }); + apply_outcome(&mut summary.dimensions, opts.on_error, name, res)?; + } + + for (key, info) in parsed.default_configs.iter() { + let res = with_savepoint(conn, "cac_import_dc", |c| { + write_default_config(c, schema_name, key, info, &opts, &email) + }); + apply_outcome(&mut summary.default_configs, opts.on_error, key, res)?; + } + + for ctx in &parsed.contexts { + let res = with_savepoint(conn, "cac_import_ctx", |c| { + write_context( + c, + workspace_context, + ctx, + &parsed.overrides, + &parsed.dimensions, + &opts, + user, + &email, + ) + }); + apply_outcome(&mut summary.contexts, opts.on_error, &ctx.id, res)?; + } + + // Replace (mirror) mode: delete anything in the workspace that is not + // present in the imported file. Contexts first, then the entities they + // can reference. + if opts.mode == ImportMode::Replace { + let file_ctx_ids: HashSet<&String> = + parsed.contexts.iter().map(|c| &c.id).collect(); + let db_ctx_ids: Vec = ctx_dsl::contexts + .select(ctx_dsl::id) + .schema_name(schema_name) + .load::(conn)?; + for id in db_ctx_ids { + if file_ctx_ids.contains(&id) { + continue; + } + let res = with_savepoint(conn, "cac_import_del_ctx", |c| { + diesel::delete(ctx_dsl::contexts.filter(ctx_dsl::id.eq(&id))) + .schema_name(schema_name) + .execute(c) + .map_err(|e| db_error!(e))?; + Ok(Outcome::Deleted) + }); + apply_outcome(&mut summary.contexts, opts.on_error, &id, res)?; + } + + let db_dc_keys: Vec = dc_dsl::default_configs + .select(dc_dsl::key) + .schema_name(schema_name) + .load::(conn)?; + for key in db_dc_keys { + if parsed.default_configs.contains_key(&key) { + continue; + } + let res = with_savepoint(conn, "cac_import_del_dc", |c| { + diesel::delete( + dc_dsl::default_configs.filter(dc_dsl::key.eq(&key)), + ) + .schema_name(schema_name) + .execute(c) + .map_err(|e| db_error!(e))?; + Ok(Outcome::Deleted) + }); + apply_outcome(&mut summary.default_configs, opts.on_error, &key, res)?; + } + + let db_dim_names: Vec = dim_dsl::dimensions + .select(dim_dsl::dimension) + .schema_name(schema_name) + .load::(conn)?; + for name in db_dim_names { + if parsed.dimensions.contains_key(&name) { + continue; + } + let res = with_savepoint(conn, "cac_import_del_dim", |c| { + diesel::delete( + dim_dsl::dimensions.filter(dim_dsl::dimension.eq(&name)), + ) + .schema_name(schema_name) + .execute(c) + .map_err(|e| db_error!(e))?; + Ok(Outcome::Deleted) + }); + apply_outcome(&mut summary.dimensions, opts.on_error, &name, res)?; + } + } + + if opts.dry_run { + // Roll back everything; the summary travels out via the error. + return Err(TxError::DryRun(summary)); + } + + let config_version = + add_config_version(state, tags, import_description(), conn, schema_name)?; + Ok((summary, config_version)) + }); + + match tx_result { + Ok((mut summary, config_version)) => { + summary.config_version = Some(config_version.id.to_string()); + + let _ = + put_config_in_redis(&config_version, state, schema_name, conn).await; + + let data = WebhookData { + payload: &summary, + resource: Resource::Config, + action: Action::Update, + event: WebhookEvent::ConfigChanged, + config_version_opt: Some(config_version.id.to_string()), + }; + let _ = execute_webhook_call(data, workspace_context, state, conn).await; + + Ok(summary) + } + Err(TxError::DryRun(summary)) => Ok(summary), + Err(TxError::App(e)) => Err(e), + } +} + +/// HTTP entry-point used by the `/config/toml` and `/config/json` handlers when +/// a body is POSTed. Parses options/tags from the request and returns the +/// import summary as JSON. +pub async fn handle_import( + body: &[u8], + req: &HttpRequest, + custom_headers: CustomHeaders, + user: &User, + workspace_context: &WorkspaceContext, + state: &Data, + conn: &mut DBConnection, +) -> superposition::Result { + let body_str = std::str::from_utf8(body) + .map_err(|_| bad_argument!("Request body is not valid UTF-8"))?; + let opts = ImportOptions::from_request(req)?; + let tags = parse_config_tags(custom_headers.config_tags)?; + + let summary = + import_config::(body_str, opts, tags, user, workspace_context, state, conn) + .await?; + + Ok(HttpResponse::Ok().json(summary)) +} + +#[cfg(test)] +mod tests { + use actix_web::test::TestRequest; + use serde_json::json; + + use super::*; + + #[test] + fn deep_merge_combines_nested_objects() { + let base = json!({ "a": 1, "nested": { "x": 1, "y": 2 } }); + let overlay = json!({ "b": 2, "nested": { "y": 20, "z": 30 } }); + assert_eq!( + deep_merge(&base, &overlay), + json!({ "a": 1, "b": 2, "nested": { "x": 1, "y": 20, "z": 30 } }) + ); + } + + #[test] + fn deep_merge_overlay_wins_for_scalars_and_arrays() { + assert_eq!(deep_merge(&json!(1), &json!(2)), json!(2)); + assert_eq!(deep_merge(&json!([1, 2]), &json!([3])), json!([3])); + // a non-object overlay fully replaces an object base + assert_eq!(deep_merge(&json!({ "a": 1 }), &json!("x")), json!("x")); + } + + #[test] + fn options_default_to_safe_merge() { + let req = TestRequest::default().to_http_request(); + let opts = ImportOptions::from_request(&req).unwrap(); + assert!(opts.mode == ImportMode::Merge); + assert!(opts.overwrite); + assert!(opts.on_error == OnError::Abort); + assert!(!opts.dry_run); + assert!(!opts.value_merge); + } + + #[test] + fn options_parsed_from_headers() { + let req = TestRequest::default() + .insert_header(("x-import-mode", "replace")) + .insert_header(("x-import-overwrite", "false")) + .insert_header(("x-import-on-error", "continue")) + .insert_header(("x-import-dry-run", "true")) + .insert_header(("x-import-value-merge", "true")) + .to_http_request(); + let opts = ImportOptions::from_request(&req).unwrap(); + assert!(opts.mode == ImportMode::Replace); + assert!(!opts.overwrite); + assert!(opts.on_error == OnError::Continue); + assert!(opts.dry_run); + assert!(opts.value_merge); + } + + #[test] + fn invalid_mode_is_rejected() { + let req = TestRequest::default() + .insert_header(("x-import-mode", "bogus")) + .to_http_request(); + assert!(ImportOptions::from_request(&req).is_err()); + } +} diff --git a/crates/superposition/src/main.rs b/crates/superposition/src/main.rs index 9da856cc6..7938688b2 100644 --- a/crates/superposition/src/main.rs +++ b/crates/superposition/src/main.rs @@ -12,7 +12,7 @@ use actix_files::Files; use actix_web::{ App, HttpRequest, HttpResponse, HttpServer, Scope, middleware::{Compress, Condition}, - web::{self, Data, PathConfig, QueryConfig, get, scope}, + web::{self, Data, PathConfig, PayloadConfig, QueryConfig, get, scope}, }; use context_aware_config::api::*; use experimentation_platform::api::*; @@ -235,6 +235,9 @@ async fn main() -> Result<()> { .app_data(app_state.clone()) .app_data(PathConfig::default().error_handler(|err, _| bad_argument!(err).into())) .app_data(QueryConfig::default().error_handler(|err, _| bad_argument!(err).into())) + // Raise the raw-body limit (default 256KB) so config imports posted + // to /config/toml and /config/json can carry large workspaces. + .app_data(PayloadConfig::new(10 * 1024 * 1024)) .leptos_routes( leptos_options.to_owned(), routes.to_owned(), diff --git a/docs/docs/superposition-config-file/import-export.md b/docs/docs/superposition-config-file/import-export.md new file mode 100644 index 000000000..20294fc08 --- /dev/null +++ b/docs/docs/superposition-config-file/import-export.md @@ -0,0 +1,115 @@ +--- +sidebar_position: 9 +title: Import & Export +description: Round-trip a whole workspace as a SuperTOML or JSON file +--- + +# Import & Export + +Superposition can serialize an entire workspace's configuration — +default-configs (with their schemas), dimensions and overrides — into a single +SuperTOML or JSON document, and read that same document back in. This makes it +easy to back up a workspace, copy config between workspaces, review changes as a +file diff, or manage configuration as code. + +Both directions use the **same pair of endpoints**: + +| Format | Endpoint | +| ------ | ------------------- | +| TOML | `POST /config/toml` | +| JSON | `POST /config/json` | + +The direction is decided by the request body: + +- **Export** — `GET` (or `POST` with an empty body) returns the current config. +- **Import** — `POST` with the file as the request body writes it back. + +## Exporting + +```bash +# TOML +curl -X GET https:///config/toml \ + -H "x-tenant: " -H "org-id: " > config.toml + +# JSON +curl -X GET https:///config/json \ + -H "x-tenant: " -H "org-id: " > config.json +``` + +The response body is a complete SuperTOML / JSON document — see +[Format Specification](./format-specification) for its shape. + +## Importing + +POST the file back to the same endpoint. The body is parsed and **fully +validated** (schemas, dimension positions, cohort relationships, and every +context condition / override) before anything is written. The whole import runs +in a single database transaction. + +```bash +curl -X POST https:///config/toml \ + -H "x-tenant: " -H "org-id: " \ + -H "Content-Type: application/toml" \ + --data-binary @config.toml +``` + +On success you get a JSON summary of what changed: + +```json +{ + "mode": "merge", + "dry_run": false, + "config_version": "7231...", + "dimensions": { "created": 2, "updated": 1, "skipped": 0, "deleted": 0 }, + "default_configs": { "created": 5, "updated": 0, "skipped": 0, "deleted": 0 }, + "contexts": { "created": 3, "updated": 1, "skipped": 0, "deleted": 0 } +} +``` + +### Import options + +Behaviour is controlled with request headers: + +| Header | Values | Default | Effect | +| ---------------------- | ----------------------- | ------- | -------------------------------------------------------------------------------------------------------- | +| `x-import-mode` | `merge` \| `replace` | `merge` | `merge` upserts what's in the file and leaves everything else alone. `replace` additionally **deletes** any dimension/default-config/context that is _not_ in the file (mirror the file exactly). | +| `x-import-overwrite` | `true` \| `false` | `true` | When `false`, entities that already exist are **skipped** (only new ones are created). | +| `x-import-on-error` | `abort` \| `continue` | `abort` | `abort` rolls the whole import back on the first error. `continue` applies everything that's valid and returns the per-entity errors in the summary. | +| `x-import-dry-run` | `true` \| `false` | `false` | Parse, validate and compute the summary **without writing anything**. Great for previewing an import. | +| `x-import-value-merge` | `true` \| `false` | `false` | For object-valued default configs, deep-merge with the existing value instead of replacing it wholesale. | + +When `x-import-on-error: continue` is used, failed entities appear under an +`errors` array in the relevant section of the summary: + +```json +"default_configs": { + "created": 4, "updated": 0, "skipped": 0, "deleted": 0, + "errors": [{ "id": "per_km_rate", "error": "..." }] +} +``` + +### Tips + +- Use `x-import-dry-run: true` first to see exactly what an import would do. +- Use `x-import-mode: replace` to make a workspace _exactly_ match a file + (e.g. restoring from a backup). Use the default `merge` to layer a file on top + of existing config without removing anything. +- Use `x-import-overwrite: false` to seed only the keys/dimensions/contexts that + don't exist yet, without touching anything already configured. +- `x-config-tags` is honoured and recorded against the config version the import + creates, just like other write endpoints. + +:::note +Value-validation and value-compute function bindings are not part of the +exported file, so they are not set by import. Bind functions to dimensions and +default-configs through their dedicated APIs after importing if needed. +::: + +:::caution +The imported file is validated as a self-contained config (every context only +references dimensions/keys defined in the same file), and dimension **positions** +are taken verbatim from the file. In `merge` mode this means the file's positions +should be consistent with the dimensions already in the workspace. To avoid +position clashes entirely, export the workspace, edit, and import back — or use +`replace` mode, which makes the workspace match the file exactly. +::: diff --git a/docs/docs/superposition-config-file/intro.md b/docs/docs/superposition-config-file/intro.md index 7b40b5add..1b980949e 100644 --- a/docs/docs/superposition-config-file/intro.md +++ b/docs/docs/superposition-config-file/intro.md @@ -154,5 +154,6 @@ This makes editing SuperTOML files as comfortable as working with code in an IDE - [Type Safety](./type-safety) - Deep dive into JSON Schema validation - [Cascading Model](./cascading-model) - How overrides cascade - [Deterministic Resolution](./deterministic-resolution) - Priority and conflict resolution +- [Import & Export](./import-export) - Round-trip a whole workspace as a file - [Examples](./examples) - Complete working examples - [Config File Compatibility](./config-file-compatibility) - Common Linux/macOS configs as SuperTOML From 112a8958dc5868f3d065c13faf197317a362e2e3 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 3 Jun 2026 08:25:34 +0000 Subject: [PATCH 02/15] test(supertoml): cover import decision and transform logic Extract the pure (non-DB) parts of the importer into small helpers (should_skip, resolve_default_value, dimension_position, build_dimension_row, build_schema, build_default_config_row) and unit-test them alongside option parsing, summary serialisation and report tallying. 16 tests cover: header option parsing (defaults, overrides, invalid values), merge-vs-replace value resolution, overwrite/skip decision, dimension position validation, model field mapping (incl. validation-fn bindings being dropped), deep-merge semantics and the JSON summary shape. The transactional DB write paths (upsert, replace-deletes, dry-run rollback, continue-on-error savepoints) reuse existing, exercised handler code and are left for an HTTP-level integration test once a test database harness exists. --- .../src/api/config/import.rs | 273 +++++++++++++++--- 1 file changed, 230 insertions(+), 43 deletions(-) diff --git a/crates/context_aware_config/src/api/config/import.rs b/crates/context_aware_config/src/api/config/import.rs index d7957c6fc..f41a398b9 100644 --- a/crates/context_aware_config/src/api/config/import.rs +++ b/crates/context_aware_config/src/api/config/import.rs @@ -299,6 +299,87 @@ fn deep_merge(base: &Value, overlay: &Value) -> Value { } } +// --------------------------------------------------------------------------- +// Pure builders / decisions (no database access — unit tested below) +// --------------------------------------------------------------------------- + +/// Whether an entity that already exists should be left untouched. +fn should_skip(exists: bool, overwrite: bool) -> bool { + exists && !overwrite +} + +/// The value to persist for a default config, honouring the `value_merge` +/// option (deep-merge with the existing value when both are objects). +fn resolve_default_value( + existing: Option<&Value>, + incoming: &Value, + value_merge: bool, +) -> Value { + match (value_merge, existing) { + (true, Some(existing)) => deep_merge(existing, incoming), + _ => incoming.clone(), + } +} + +fn dimension_position( + name: &str, + info: &DimensionInfo, +) -> superposition::Result { + Position::try_from(info.position).map_err(|e| { + bad_argument!("Invalid position for dimension '{}': {}", name, e) + }) +} + +fn build_dimension_row( + name: &str, + info: &DimensionInfo, + email: &str, +) -> superposition::Result { + Ok(Dimension { + dimension: name.to_string(), + schema: info.schema.clone(), + position: dimension_position(name, info)?, + dimension_type: info.dimension_type.clone(), + dependency_graph: info.dependency_graph.clone(), + value_compute_function_name: info.value_compute_function_name.clone(), + // Function bindings are not carried by the import file. + value_validation_function_name: None, + created_at: Utc::now(), + created_by: email.to_string(), + last_modified_at: Utc::now(), + last_modified_by: email.to_string(), + description: import_description(), + change_reason: import_change_reason(), + }) +} + +fn build_schema(key: &str, schema: &Value) -> superposition::Result { + ExtendedMap::try_from(schema.clone()).map_err(|e| { + bad_argument!("Invalid schema for default config '{}': {}", key, e) + }) +} + +fn build_default_config_row( + key: &str, + value: Value, + schema: ExtendedMap, + email: &str, +) -> DefaultConfig { + DefaultConfig { + key: key.to_string(), + value, + schema, + value_validation_function_name: None, + value_compute_function_name: None, + created_at: Utc::now(), + created_by: email.to_string(), + last_modified_at: Utc::now(), + last_modified_by: email.to_string(), + description: import_description(), + change_reason: import_change_reason(), + } +} + // --------------------------------------------------------------------------- // Per-entity writers // --------------------------------------------------------------------------- @@ -318,15 +399,12 @@ fn write_dimension( .get_result::(conn)? > 0; - if exists && !opts.overwrite { + if should_skip(exists, opts.overwrite) { return Ok(Outcome::Skipped); } - let position = Position::try_from(info.position).map_err(|e| { - bad_argument!("Invalid position for dimension '{}': {}", name, e) - })?; - if exists { + let position = dimension_position(name, info)?; diesel::update(dim_dsl::dimensions.filter(dim_dsl::dimension.eq(name))) .set(( dim_dsl::schema.eq(info.schema.clone()), @@ -343,21 +421,7 @@ fn write_dimension( .execute(conn)?; Ok(Outcome::Updated) } else { - let dimension = Dimension { - dimension: name.to_string(), - schema: info.schema.clone(), - position, - dimension_type: info.dimension_type.clone(), - dependency_graph: info.dependency_graph.clone(), - value_compute_function_name: info.value_compute_function_name.clone(), - value_validation_function_name: None, - created_at: Utc::now(), - created_by: email.to_string(), - last_modified_at: Utc::now(), - last_modified_by: email.to_string(), - description: import_description(), - change_reason: import_change_reason(), - }; + let dimension = build_dimension_row(name, info, email)?; diesel::insert_into(dim_dsl::dimensions) .values(&dimension) .schema_name(schema_name) @@ -382,18 +446,12 @@ fn write_default_config( .optional()?; let exists = existing.is_some(); - if exists && !opts.overwrite { + if should_skip(exists, opts.overwrite) { return Ok(Outcome::Skipped); } - let value = match (opts.value_merge, &existing) { - (true, Some(existing_value)) => deep_merge(existing_value, &info.value), - _ => info.value.clone(), - }; - - let schema = ExtendedMap::try_from(info.schema.clone()).map_err(|e| { - bad_argument!("Invalid schema for default config '{}': {}", key, e) - })?; + let value = resolve_default_value(existing.as_ref(), &info.value, opts.value_merge); + let schema = build_schema(key, &info.schema)?; if exists { diesel::update(dc_dsl::default_configs.filter(dc_dsl::key.eq(key))) @@ -408,19 +466,7 @@ fn write_default_config( .execute(conn)?; Ok(Outcome::Updated) } else { - let default_config = DefaultConfig { - key: key.to_string(), - value, - schema, - value_validation_function_name: None, - value_compute_function_name: None, - created_at: Utc::now(), - created_by: email.to_string(), - last_modified_at: Utc::now(), - last_modified_by: email.to_string(), - description: import_description(), - change_reason: import_change_reason(), - }; + let default_config = build_default_config_row(key, value, schema, email); diesel::insert_into(dc_dsl::default_configs) .values(&default_config) .schema_name(schema_name) @@ -457,7 +503,7 @@ fn write_context( .get_result::(conn)? > 0; - if exists && !opts.overwrite { + if should_skip(exists, opts.overwrite) { return Ok(Outcome::Skipped); } @@ -665,9 +711,20 @@ pub async fn handle_import( mod tests { use actix_web::test::TestRequest; use serde_json::json; + use superposition_types::database::models::cac::{DependencyGraph, DimensionType}; use super::*; + fn dim_info(position: i32) -> DimensionInfo { + DimensionInfo { + schema: ExtendedMap::try_from(json!({ "type": "string" })).unwrap(), + position, + dimension_type: DimensionType::Regular {}, + dependency_graph: DependencyGraph::default(), + value_compute_function_name: None, + } + } + #[test] fn deep_merge_combines_nested_objects() { let base = json!({ "a": 1, "nested": { "x": 1, "y": 2 } }); @@ -721,4 +778,134 @@ mod tests { .to_http_request(); assert!(ImportOptions::from_request(&req).is_err()); } + + #[test] + fn invalid_on_error_is_rejected() { + let req = TestRequest::default() + .insert_header(("x-import-on-error", "maybe")) + .to_http_request(); + assert!(ImportOptions::from_request(&req).is_err()); + } + + #[test] + fn should_skip_only_existing_without_overwrite() { + // skip only when the entity exists AND overwrite is disabled + assert!(should_skip(true, false)); + assert!(!should_skip(true, true)); // exists, but overwrite -> update + assert!(!should_skip(false, false)); // new -> always created + assert!(!should_skip(false, true)); + } + + #[test] + fn resolve_default_value_without_merge_replaces() { + let existing = json!({ "a": 1 }); + let incoming = json!({ "b": 2 }); + // value_merge disabled -> incoming wins wholesale + assert_eq!( + resolve_default_value(Some(&existing), &incoming, false), + json!({ "b": 2 }) + ); + } + + #[test] + fn resolve_default_value_with_merge_deep_merges_existing() { + let existing = json!({ "a": 1, "nested": { "x": 1 } }); + let incoming = json!({ "b": 2, "nested": { "y": 2 } }); + assert_eq!( + resolve_default_value(Some(&existing), &incoming, true), + json!({ "a": 1, "b": 2, "nested": { "x": 1, "y": 2 } }) + ); + } + + #[test] + fn resolve_default_value_with_merge_but_no_existing_uses_incoming() { + let incoming = json!({ "b": 2 }); + assert_eq!( + resolve_default_value(None, &incoming, true), + json!({ "b": 2 }) + ); + } + + #[test] + fn dimension_position_rejects_negative() { + assert!(dimension_position("city", &dim_info(-1)).is_err()); + assert!(dimension_position("city", &dim_info(0)).is_ok()); + assert!(dimension_position("city", &dim_info(7)).is_ok()); + } + + #[test] + fn build_dimension_row_maps_fields_and_clears_validation_fn() { + let mut info = dim_info(3); + info.value_compute_function_name = Some("compute_fn".to_string()); + + let row = build_dimension_row("city", &info, "tester@example.com").unwrap(); + + assert_eq!(row.dimension, "city"); + assert_eq!(i32::from(row.position), 3); + assert_eq!(row.value_compute_function_name.as_deref(), Some("compute_fn")); + // validation-fn bindings are not carried by the file + assert!(row.value_validation_function_name.is_none()); + assert_eq!(row.created_by, "tester@example.com"); + assert!(matches!(row.dimension_type, DimensionType::Regular {})); + } + + #[test] + fn build_schema_requires_an_object() { + assert!(build_schema("k", &json!({ "type": "number" })).is_ok()); + // a non-object schema is rejected + assert!(build_schema("k", &json!("not-an-object")).is_err()); + } + + #[test] + fn build_default_config_row_carries_value_and_schema() { + let schema = build_schema("per_km_rate", &json!({ "type": "number" })).unwrap(); + let row = build_default_config_row( + "per_km_rate", + json!(20.0), + schema, + "tester@example.com", + ); + + assert_eq!(row.key, "per_km_rate"); + assert_eq!(row.value, json!(20.0)); + assert!(row.value_validation_function_name.is_none()); + assert!(row.value_compute_function_name.is_none()); + assert_eq!(row.created_by, "tester@example.com"); + } + + #[test] + fn summary_serialises_with_mode_and_hides_empty_errors() { + let opts = ImportOptions { + mode: ImportMode::Replace, + overwrite: true, + on_error: OnError::Abort, + dry_run: true, + value_merge: false, + }; + let summary = ImportSummary::new(&opts); + let value = serde_json::to_value(&summary).unwrap(); + + assert_eq!(value["mode"], json!("replace")); + assert_eq!(value["dry_run"], json!(true)); + // config_version omitted until the import commits + assert!(value.get("config_version").is_none()); + // no errors array when empty + assert!(value["dimensions"].get("errors").is_none()); + assert_eq!(value["dimensions"]["created"], json!(0)); + } + + #[test] + fn entity_report_records_outcomes() { + let mut report = EntityReport::default(); + report.record(Outcome::Created); + report.record(Outcome::Created); + report.record(Outcome::Updated); + report.record(Outcome::Skipped); + report.record(Outcome::Deleted); + + assert_eq!(report.created, 2); + assert_eq!(report.updated, 1); + assert_eq!(report.skipped, 1); + assert_eq!(report.deleted, 1); + } } From 8098df426b444f131352806765d6218c92157ee1 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 3 Jun 2026 10:38:46 +0000 Subject: [PATCH 03/15] test(supertoml): add JS SDK integration tests for config import Adds tests/src/config_import.test.ts covering the import side of the /config/toml and /config/json endpoints end-to-end. The import POST goes via fetch (the generated SDK only models the read-only export), while the SDK is used to set up an isolated workspace and verify the resulting state. Because replace-mode import mirrors the whole workspace, the suite runs in its own dedicated workspace so it can't clobber other suites' data. Covers: - merge create of dimensions/default-configs/contexts - re-import updating instead of creating - overwrite=false skipping existing entities - value-merge deep-merging object default-config values - dry-run reporting without persisting (no key, no config version) - replace mode deleting entities absent from the file - invalid body and undeclared-dimension rejection (4xx) - export-then-import round-trip idempotency - a TOML-endpoint merge import Type-checks cleanly against the built SDK types. --- .../src/api/config/handlers.rs | 2 + .../src/api/config/import.rs | 55 ++- tests/src/config_import.test.ts | 362 ++++++++++++++++++ 3 files changed, 390 insertions(+), 29 deletions(-) create mode 100644 tests/src/config_import.test.ts diff --git a/crates/context_aware_config/src/api/config/handlers.rs b/crates/context_aware_config/src/api/config/handlers.rs index 4dbe8027f..5aaf80586 100644 --- a/crates/context_aware_config/src/api/config/handlers.rs +++ b/crates/context_aware_config/src/api/config/handlers.rs @@ -569,6 +569,7 @@ async fn get_handler( /// Handler that returns config in TOML format with schema information. /// This uses generate_detailed_cac to fetch schemas from the database. +#[allow(clippy::too_many_arguments)] #[authorized] #[routes] #[get("/toml")] @@ -634,6 +635,7 @@ async fn get_toml_handler( /// Handler that returns config in JSON format with schema information. /// This uses generate_detailed_cac to fetch schemas from the database. +#[allow(clippy::too_many_arguments)] #[authorized] #[routes] #[get("/json")] diff --git a/crates/context_aware_config/src/api/config/import.rs b/crates/context_aware_config/src/api/config/import.rs index f41a398b9..a73844ec5 100644 --- a/crates/context_aware_config/src/api/config/import.rs +++ b/crates/context_aware_config/src/api/config/import.rs @@ -20,32 +20,31 @@ use std::collections::HashSet; -use actix_web::{web::Data, HttpRequest, HttpResponse}; +use actix_web::{HttpRequest, HttpResponse, web::Data}; use chrono::Utc; -use diesel::{ - Connection, ExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl, -}; +use diesel::{Connection, ExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl}; use serde::Serialize; use serde_json::{Map, Value}; use service_utils::{ - helpers::{execute_webhook_call, parse_config_tags, WebhookData}, + helpers::{WebhookData, execute_webhook_call, parse_config_tags}, service::types::{AppState, CustomHeaders, SchemaName, WorkspaceContext}, }; -use superposition_core::{helpers::calculate_context_weight, ConfigFormat}; +use superposition_core::{ConfigFormat, helpers::calculate_context_weight}; use superposition_macros::{bad_argument, db_error}; use superposition_types::{ + Context as ConfigContext, DBConnection, DefaultConfigInfo, DimensionInfo, + ExtendedMap, Overrides, Resource, User, api::webhook::Action, database::models::{ + ChangeReason, Description, cac::{Context as DbContext, DefaultConfig, Dimension, Position}, others::WebhookEvent, - ChangeReason, Description, }, database::schema::{ contexts::dsl as ctx_dsl, default_configs::dsl as dc_dsl, dimensions::dsl as dim_dsl, }, - result as superposition, Context as ConfigContext, DBConnection, DefaultConfigInfo, - DimensionInfo, ExtendedMap, Overrides, Resource, User, + result as superposition, }; use crate::{ @@ -101,7 +100,7 @@ impl ImportOptions { return Err(bad_argument!( "Invalid x-import-mode '{}', expected 'merge' or 'replace'", s - )) + )); } }; let on_error = match req @@ -116,7 +115,7 @@ impl ImportOptions { return Err(bad_argument!( "Invalid x-import-on-error '{}', expected 'abort' or 'continue'", s - )) + )); } }; Ok(Self { @@ -201,7 +200,7 @@ enum Outcome { /// returned to the caller. enum TxError { App(superposition::AppError), - DryRun(ImportSummary), + DryRun(Box), } impl From for TxError { @@ -325,9 +324,8 @@ fn dimension_position( name: &str, info: &DimensionInfo, ) -> superposition::Result { - Position::try_from(info.position).map_err(|e| { - bad_argument!("Invalid position for dimension '{}': {}", name, e) - }) + Position::try_from(info.position) + .map_err(|e| bad_argument!("Invalid position for dimension '{}': {}", name, e)) } fn build_dimension_row( @@ -354,9 +352,8 @@ fn build_dimension_row( } fn build_schema(key: &str, schema: &Value) -> superposition::Result { - ExtendedMap::try_from(schema.clone()).map_err(|e| { - bad_argument!("Invalid schema for default config '{}': {}", key, e) - }) + ExtendedMap::try_from(schema.clone()) + .map_err(|e| bad_argument!("Invalid schema for default config '{}': {}", key, e)) } fn build_default_config_row( @@ -618,12 +615,10 @@ pub async fn import_config( continue; } let res = with_savepoint(conn, "cac_import_del_dc", |c| { - diesel::delete( - dc_dsl::default_configs.filter(dc_dsl::key.eq(&key)), - ) - .schema_name(schema_name) - .execute(c) - .map_err(|e| db_error!(e))?; + diesel::delete(dc_dsl::default_configs.filter(dc_dsl::key.eq(&key))) + .schema_name(schema_name) + .execute(c) + .map_err(|e| db_error!(e))?; Ok(Outcome::Deleted) }); apply_outcome(&mut summary.default_configs, opts.on_error, &key, res)?; @@ -652,7 +647,7 @@ pub async fn import_config( if opts.dry_run { // Roll back everything; the summary travels out via the error. - return Err(TxError::DryRun(summary)); + return Err(TxError::DryRun(Box::new(summary))); } let config_version = @@ -664,8 +659,7 @@ pub async fn import_config( Ok((mut summary, config_version)) => { summary.config_version = Some(config_version.id.to_string()); - let _ = - put_config_in_redis(&config_version, state, schema_name, conn).await; + let _ = put_config_in_redis(&config_version, state, schema_name, conn).await; let data = WebhookData { payload: &summary, @@ -678,7 +672,7 @@ pub async fn import_config( Ok(summary) } - Err(TxError::DryRun(summary)) => Ok(summary), + Err(TxError::DryRun(summary)) => Ok(*summary), Err(TxError::App(e)) => Err(e), } } @@ -842,7 +836,10 @@ mod tests { assert_eq!(row.dimension, "city"); assert_eq!(i32::from(row.position), 3); - assert_eq!(row.value_compute_function_name.as_deref(), Some("compute_fn")); + assert_eq!( + row.value_compute_function_name.as_deref(), + Some("compute_fn") + ); // validation-fn bindings are not carried by the file assert!(row.value_validation_function_name.is_none()); assert_eq!(row.created_by, "tester@example.com"); diff --git a/tests/src/config_import.test.ts b/tests/src/config_import.test.ts new file mode 100644 index 000000000..73668803a --- /dev/null +++ b/tests/src/config_import.test.ts @@ -0,0 +1,362 @@ +import { + CreateWorkspaceCommand, + MigrateWorkspaceSchemaCommand, + WorkspaceStatus, + ListDimensionsCommand, + ListDefaultConfigsCommand, + ListContextsCommand, + GetConfigJsonCommand, +} from "@juspay/superposition-sdk"; +import { superpositionClient, ENV } from "../env.ts"; +import { describe, test, expect, beforeAll } from "bun:test"; + +// Import is exposed by POSTing a body to the same endpoints used for export +// (`/config/toml`, `/config/json`). The generated SDK only models the +// (read-only) export side, so the import calls here go through `fetch` while the +// SDK is used for setup and for verifying the resulting workspace state. +// +// Import in `replace` mode mirrors the *entire* workspace, so these tests run in +// their own dedicated workspace to avoid clobbering data created by other suites. + +const IMPORT_WORKSPACE = "importtestws"; +const suffix = Math.random().toString(36).substring(7); + +const TIER = `imp_tier_${suffix}`; +const REGION = `imp_region_${suffix}`; +const RATE = `imp_rate_${suffix}`; +const FLAG = `imp_flag_${suffix}`; +const DRYRUN_KEY = `imp_dryrun_${suffix}`; +const TOML_KEY = `imp_toml_${suffix}`; + +type ImportSummary = { + mode: string; + dry_run: boolean; + config_version?: string; + dimensions: EntityReport; + default_configs: EntityReport; + contexts: EntityReport; +}; +type EntityReport = { + created: number; + updated: number; + skipped: number; + deleted: number; + errors?: { id: string; error: string }[]; +}; + +async function importConfig( + format: "toml" | "json", + body: string, + extraHeaders: Record = {}, +): Promise<{ status: number; summary?: ImportSummary; raw: string }> { + const res = await fetch(`${ENV.baseUrl}/config/${format}`, { + method: "POST", + headers: { + Authorization: "Bearer some-token", + "x-org-id": ENV.org_id, + "x-workspace": IMPORT_WORKSPACE, + "Content-Type": + format === "toml" ? "application/toml" : "application/json", + ...extraHeaders, + }, + body, + }); + const raw = await res.text(); + let summary: ImportSummary | undefined; + try { + summary = JSON.parse(raw); + } catch { + summary = undefined; + } + return { status: res.status, summary, raw }; +} + +// A self-consistent JSON config: contexts only reference dimensions/keys defined +// in the same document. `opts.includeFlag` lets a test drop one default-config. +function buildJsonConfig(opts: { includeFlag: boolean }): string { + const defaultConfigs: Record = { + [RATE]: { value: 10, schema: { type: "number" } }, + }; + if (opts.includeFlag) { + defaultConfigs[FLAG] = { + value: { enabled: true, mode: "a", nested: { x: 1 } }, + schema: { type: "object" }, + }; + } + return JSON.stringify({ + "default-configs": defaultConfigs, + dimensions: { + [TIER]: { + position: 1, + schema: { type: "string", enum: ["gold", "silver"] }, + }, + [REGION]: { + position: 2, + schema: { type: "string", enum: ["us", "eu"] }, + }, + }, + overrides: [{ _context_: { [TIER]: "gold" }, [RATE]: 20 }], + }); +} + +async function listDefaultConfigKeys(): Promise { + const out = await superpositionClient.send( + new ListDefaultConfigsCommand({ + workspace_id: IMPORT_WORKSPACE, + org_id: ENV.org_id, + count: 100, + page: 1, + }), + ); + return (out.data ?? []).map((d) => d.key as string); +} + +async function getDefaultConfigValue(key: string): Promise { + const out = await superpositionClient.send( + new ListDefaultConfigsCommand({ + workspace_id: IMPORT_WORKSPACE, + org_id: ENV.org_id, + count: 100, + page: 1, + }), + ); + return (out.data ?? []).find((d) => d.key === key)?.value; +} + +async function listDimensionNames(): Promise { + const out = await superpositionClient.send( + new ListDimensionsCommand({ + workspace_id: IMPORT_WORKSPACE, + org_id: ENV.org_id, + count: 100, + page: 1, + }), + ); + return (out.data ?? []).map((d) => d.dimension as string); +} + +async function countContexts(): Promise { + const out = await superpositionClient.send( + new ListContextsCommand({ + workspace_id: IMPORT_WORKSPACE, + org_id: ENV.org_id, + count: 100, + page: 1, + }), + ); + return (out.data ?? []).length; +} + +beforeAll(async () => { + // Dedicated workspace so `replace`-mode imports can't affect other suites. + try { + await superpositionClient.send( + new CreateWorkspaceCommand({ + org_id: ENV.org_id, + workspace_admin_email: "admin@example.com", + workspace_name: IMPORT_WORKSPACE, + workspace_status: WorkspaceStatus.ENABLED, + allow_experiment_self_approval: true, + auto_populate_control: false, + enable_context_validation: true, + enable_change_reason_validation: false, + }), + ); + console.log(`Created import test workspace: ${IMPORT_WORKSPACE}`); + } catch (e: any) { + // Already exists from a previous run — fine, reuse it. + console.log(`Reusing import test workspace: ${e?.message ?? ""}`); + } + + await superpositionClient.send( + new MigrateWorkspaceSchemaCommand({ + org_id: ENV.org_id, + workspace_name: IMPORT_WORKSPACE, + }), + ); +}); + +describe("Config import - JSON", () => { + test("merge import creates dimensions, default-configs and contexts", async () => { + const { status, summary } = await importConfig( + "json", + buildJsonConfig({ includeFlag: true }), + ); + + expect(status).toBe(200); + expect(summary).toBeDefined(); + expect(summary!.mode).toBe("merge"); + expect(summary!.dry_run).toBe(false); + expect(summary!.config_version).toBeDefined(); + expect(summary!.dimensions.created).toBeGreaterThanOrEqual(2); + expect(summary!.default_configs.created).toBeGreaterThanOrEqual(2); + expect(summary!.contexts.created).toBeGreaterThanOrEqual(1); + + const dims = await listDimensionNames(); + expect(dims).toContain(TIER); + expect(dims).toContain(REGION); + + const keys = await listDefaultConfigKeys(); + expect(keys).toContain(RATE); + expect(keys).toContain(FLAG); + + expect(await countContexts()).toBeGreaterThanOrEqual(1); + }); + + test("re-importing the same file updates instead of creating", async () => { + const { status, summary } = await importConfig( + "json", + buildJsonConfig({ includeFlag: true }), + ); + + expect(status).toBe(200); + expect(summary!.default_configs.created).toBe(0); + expect(summary!.default_configs.updated).toBeGreaterThanOrEqual(2); + expect(summary!.dimensions.updated).toBeGreaterThanOrEqual(2); + }); + + test("overwrite=false skips entities that already exist", async () => { + const { status, summary } = await importConfig( + "json", + buildJsonConfig({ includeFlag: true }), + { "x-import-overwrite": "false" }, + ); + + expect(status).toBe(200); + expect(summary!.default_configs.created).toBe(0); + expect(summary!.default_configs.updated).toBe(0); + expect(summary!.default_configs.skipped).toBeGreaterThanOrEqual(2); + expect(summary!.dimensions.skipped).toBeGreaterThanOrEqual(2); + }); + + test("value-merge deep-merges object default-config values", async () => { + const body = JSON.stringify({ + "default-configs": { + [FLAG]: { + value: { mode: "b", nested: { y: 2 } }, + schema: { type: "object" }, + }, + }, + dimensions: {}, + overrides: [], + }); + + const { status, summary } = await importConfig("json", body, { + "x-import-value-merge": "true", + }); + + expect(status).toBe(200); + expect(summary!.default_configs.updated).toBeGreaterThanOrEqual(1); + + const value = await getDefaultConfigValue(FLAG); + // existing { enabled, mode:"a", nested:{x:1} } deep-merged with the import + expect(value).toEqual({ + enabled: true, + mode: "b", + nested: { x: 1, y: 2 }, + }); + }); + + test("dry-run reports changes without persisting", async () => { + const body = JSON.stringify({ + "default-configs": { + [DRYRUN_KEY]: { value: 1, schema: { type: "number" } }, + }, + dimensions: {}, + overrides: [], + }); + + const { status, summary } = await importConfig("json", body, { + "x-import-dry-run": "true", + }); + + expect(status).toBe(200); + expect(summary!.dry_run).toBe(true); + expect(summary!.default_configs.created).toBeGreaterThanOrEqual(1); + // nothing committed, so no config version and the key must not exist + expect(summary!.config_version).toBeUndefined(); + + const keys = await listDefaultConfigKeys(); + expect(keys).not.toContain(DRYRUN_KEY); + }); + + test("replace mode deletes entities absent from the file", async () => { + // Drop FLAG from the document; replace mode should remove it. + const { status, summary } = await importConfig( + "json", + buildJsonConfig({ includeFlag: false }), + { "x-import-mode": "replace" }, + ); + + expect(status).toBe(200); + expect(summary!.mode).toBe("replace"); + expect(summary!.default_configs.deleted).toBeGreaterThanOrEqual(1); + + const keys = await listDefaultConfigKeys(); + expect(keys).toContain(RATE); + expect(keys).not.toContain(FLAG); + }); + + test("invalid body is rejected with a 4xx", async () => { + const { status } = await importConfig("json", "{ not valid json "); + expect(status).toBeGreaterThanOrEqual(400); + expect(status).toBeLessThan(500); + }); + + test("context referencing an undeclared dimension is rejected", async () => { + const body = JSON.stringify({ + "default-configs": { + [RATE]: { value: 10, schema: { type: "number" } }, + }, + dimensions: {}, + overrides: [{ _context_: { nonexistent_dim: "x" }, [RATE]: 20 }], + }); + const { status } = await importConfig("json", body); + expect(status).toBeGreaterThanOrEqual(400); + expect(status).toBeLessThan(500); + }); + + test("export via SDK can be re-imported (round-trip)", async () => { + const exported = await superpositionClient.send( + new GetConfigJsonCommand({ + workspace_id: IMPORT_WORKSPACE, + org_id: ENV.org_id, + }), + ); + expect(exported.json_config).toBeDefined(); + + const { status, summary } = await importConfig( + "json", + exported.json_config as string, + ); + expect(status).toBe(200); + // a faithful round-trip changes nothing new + expect(summary!.default_configs.created).toBe(0); + expect(summary!.dimensions.created).toBe(0); + }); +}); + +describe("Config import - TOML", () => { + test("merge import via the TOML endpoint", async () => { + const toml = [ + "[default-configs]", + `${TOML_KEY} = { value = 5, schema = { type = "number" } }`, + "", + "[dimensions]", + `${TIER} = { position = 1, schema = { type = "string", enum = ["gold", "silver"] } }`, + "", + "[[overrides]]", + `_context_ = { ${TIER} = "silver" }`, + `${TOML_KEY} = 7`, + "", + ].join("\n"); + + const { status, summary } = await importConfig("toml", toml); + + expect(status).toBe(200); + expect(summary!.default_configs.created).toBeGreaterThanOrEqual(1); + + const keys = await listDefaultConfigKeys(); + expect(keys).toContain(TOML_KEY); + }); +}); From c643c6f56896883af955088792f68cc1deed2c41 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 4 Jun 2026 13:56:30 +0000 Subject: [PATCH 04/15] feat(supertoml): model config import in Smithy via dedicated endpoints Move config import to its own endpoints, POST /config/{toml,json}/import, instead of dispatching on body presence at the shared export endpoint. This keeps the export operations cleanly @readonly and lets import be a first-class, typed operation in the generated SDKs. - Smithy: add ImportConfigToml / ImportConfigJson service operations with a typed payload (toml_config / json_config), the x-import-* option headers and x-config-tags, plus an ImportConfigOutput summary (mode, dry_run, config_version, and per-entity created/updated/skipped/deleted reports with optional errors). Validated with `smithy validate` (0 errors). - Handlers: add import_toml_handler / import_json_handler at /toml/import and /json/import (reusing the existing import logic) and revert the export handlers to their original read-only signatures. - Tests: point the JS SDK integration suite at the new /import paths. - Docs: document the dedicated import endpoints and correct workspace headers. Note: the generated SDKs and OpenAPI need regeneration via `make smithy-updates` (the codegen plugins live in a Maven repo not reachable from this environment). Once regenerated, the integration test's fetch helper can be replaced with the generated ImportConfigToml / ImportConfigJson commands. --- .../src/api/config/handlers.rs | 94 ++++++----- .../import-export.md | 28 +-- smithy/models/config.smithy | 159 ++++++++++++++++++ smithy/models/main.smithy | 4 + tests/src/config_import.test.ts | 13 +- 5 files changed, 240 insertions(+), 58 deletions(-) diff --git a/crates/context_aware_config/src/api/config/handlers.rs b/crates/context_aware_config/src/api/config/handlers.rs index 5aaf80586..ae4f951b4 100644 --- a/crates/context_aware_config/src/api/config/handlers.rs +++ b/crates/context_aware_config/src/api/config/handlers.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use actix_web::{ - HttpRequest, HttpResponse, Scope, get, put, routes, + HttpRequest, HttpResponse, Scope, get, post, put, routes, web::{Bytes, Data, Header, Json, Path, Query}, }; use chrono::{DateTime, Utc}; @@ -71,6 +71,8 @@ pub fn endpoints() -> Scope { .service(get_toml_handler) .service(get_json_handler) .service(detailed_resolve_handler) + .service(import_toml_handler) + .service(import_json_handler) .service(resolve_handler) .service(explain_resolve_handler) .service(reduce_handler) @@ -569,16 +571,12 @@ async fn get_handler( /// Handler that returns config in TOML format with schema information. /// This uses generate_detailed_cac to fetch schemas from the database. -#[allow(clippy::too_many_arguments)] #[authorized] #[routes] #[get("/toml")] #[post("/toml")] async fn get_toml_handler( req: HttpRequest, - body: Bytes, - user: User, - custom_headers: CustomHeaders, db_conn: DbConnection, workspace_context: WorkspaceContext, state: Data, @@ -586,21 +584,6 @@ async fn get_toml_handler( let DbConnection(mut conn) = db_conn; let is_smithy = matches!(req.method(), &actix_web::http::Method::POST); - // A non-empty body on POST means "import this config"; the export path - // (GET, or POST with an empty body) is left unchanged. - if is_smithy && !body.is_empty() { - return super::import::handle_import::( - &body, - &req, - custom_headers, - &user, - &workspace_context, - &state, - &mut conn, - ) - .await; - } - let max_created_at = read_through_cache::>( format!( "{}{LAST_MODIFIED_KEY_SUFFIX}", @@ -635,16 +618,12 @@ async fn get_toml_handler( /// Handler that returns config in JSON format with schema information. /// This uses generate_detailed_cac to fetch schemas from the database. -#[allow(clippy::too_many_arguments)] #[authorized] #[routes] #[get("/json")] #[post("/json")] async fn get_json_handler( req: HttpRequest, - body: Bytes, - user: User, - custom_headers: CustomHeaders, db_conn: DbConnection, workspace_context: WorkspaceContext, state: Data, @@ -652,21 +631,6 @@ async fn get_json_handler( let DbConnection(mut conn) = db_conn; let is_smithy = matches!(req.method(), &actix_web::http::Method::POST); - // A non-empty body on POST means "import this config"; the export path - // (GET, or POST with an empty body) is left unchanged. - if is_smithy && !body.is_empty() { - return super::import::handle_import::( - &body, - &req, - custom_headers, - &user, - &workspace_context, - &state, - &mut conn, - ) - .await; - } - let max_created_at = read_through_cache::>( format!( "{}{LAST_MODIFIED_KEY_SUFFIX}", @@ -699,6 +663,58 @@ async fn get_json_handler( Ok(response.body(json_str)) } +/// Imports a full config supplied as a TOML document in the request body. +/// See [`crate::api::config::import`] for the supported `x-import-*` options. +#[authorized] +#[post("/toml/import")] +async fn import_toml_handler( + req: HttpRequest, + body: Bytes, + user: User, + custom_headers: CustomHeaders, + db_conn: DbConnection, + workspace_context: WorkspaceContext, + state: Data, +) -> superposition::Result { + let DbConnection(mut conn) = db_conn; + super::import::handle_import::( + &body, + &req, + custom_headers, + &user, + &workspace_context, + &state, + &mut conn, + ) + .await +} + +/// Imports a full config supplied as a JSON document in the request body. +/// See [`crate::api::config::import`] for the supported `x-import-*` options. +#[authorized] +#[post("/json/import")] +async fn import_json_handler( + req: HttpRequest, + body: Bytes, + user: User, + custom_headers: CustomHeaders, + db_conn: DbConnection, + workspace_context: WorkspaceContext, + state: Data, +) -> superposition::Result { + let DbConnection(mut conn) = db_conn; + super::import::handle_import::( + &body, + &req, + custom_headers, + &user, + &workspace_context, + &state, + &mut conn, + ) + .await +} + #[allow(clippy::too_many_arguments)] #[authorized] #[routes] diff --git a/docs/docs/superposition-config-file/import-export.md b/docs/docs/superposition-config-file/import-export.md index 20294fc08..a93019d1c 100644 --- a/docs/docs/superposition-config-file/import-export.md +++ b/docs/docs/superposition-config-file/import-export.md @@ -12,28 +12,26 @@ SuperTOML or JSON document, and read that same document back in. This makes it easy to back up a workspace, copy config between workspaces, review changes as a file diff, or manage configuration as code. -Both directions use the **same pair of endpoints**: +Each format has an export endpoint and an import endpoint: -| Format | Endpoint | -| ------ | ------------------- | -| TOML | `POST /config/toml` | -| JSON | `POST /config/json` | +| Format | Export | Import | +| ------ | ------------------- | -------------------------- | +| TOML | `GET /config/toml` | `POST /config/toml/import` | +| JSON | `GET /config/json` | `POST /config/json/import` | -The direction is decided by the request body: - -- **Export** — `GET` (or `POST` with an empty body) returns the current config. -- **Import** — `POST` with the file as the request body writes it back. +All requests carry the usual workspace headers, `x-workspace: ` and +`x-org-id: ` (plus `Authorization`). ## Exporting ```bash # TOML curl -X GET https:///config/toml \ - -H "x-tenant: " -H "org-id: " > config.toml + -H "x-workspace: " -H "x-org-id: " > config.toml # JSON curl -X GET https:///config/json \ - -H "x-tenant: " -H "org-id: " > config.json + -H "x-workspace: " -H "x-org-id: " > config.json ``` The response body is a complete SuperTOML / JSON document — see @@ -41,14 +39,14 @@ The response body is a complete SuperTOML / JSON document — see ## Importing -POST the file back to the same endpoint. The body is parsed and **fully +POST the file to the matching `/import` endpoint. The body is parsed and **fully validated** (schemas, dimension positions, cohort relationships, and every context condition / override) before anything is written. The whole import runs in a single database transaction. ```bash -curl -X POST https:///config/toml \ - -H "x-tenant: " -H "org-id: " \ +curl -X POST https:///config/toml/import \ + -H "x-workspace: " -H "x-org-id: " \ -H "Content-Type: application/toml" \ --data-binary @config.toml ``` @@ -98,6 +96,8 @@ When `x-import-on-error: continue` is used, failed entities appear under an don't exist yet, without touching anything already configured. - `x-config-tags` is honoured and recorded against the config version the import creates, just like other write endpoints. +- The endpoints are available in the generated SDKs as the `ImportConfigToml` + and `ImportConfigJson` operations. :::note Value-validation and value-compute function bindings are not part of the diff --git a/smithy/models/config.smithy b/smithy/models/config.smithy index 7280fd994..27099f0c8 100644 --- a/smithy/models/config.smithy +++ b/smithy/models/config.smithy @@ -183,6 +183,165 @@ operation GetConfigJson { } } +@documentation("How an import treats workspace entities that are not present in the imported file.") +enum ImportMode { + @documentation("Upsert the entities in the file and leave everything else untouched.") + MERGE = "merge" + + @documentation("Mirror the file: additionally delete dimensions, default-configs and contexts that are absent from it.") + REPLACE = "replace" +} + +@documentation("How an import reacts when an individual entity fails to apply.") +enum ImportOnError { + @documentation("Roll the whole import back on the first error.") + ABORT = "abort" + + @documentation("Apply everything that is valid and report per-entity errors.") + CONTINUE = "continue" +} + +@documentation("Per-entity outcome counts for an import.") +structure ImportEntityReport { + @required + created: Integer + + @required + updated: Integer + + @required + skipped: Integer + + @required + deleted: Integer + + errors: ImportErrorList +} + +list ImportErrorList { + member: ImportErrorItem +} + +structure ImportErrorItem { + @required + id: String + + @required + error: String +} + +@documentation("Summary of what an import created, updated, skipped or deleted.") +structure ImportConfigOutput { + @required + @notProperty + mode: String + + @required + @notProperty + dry_run: Boolean + + @notProperty + config_version: String + + @required + @notProperty + dimensions: ImportEntityReport + + @required + @notProperty + default_configs: ImportEntityReport + + @required + @notProperty + contexts: ImportEntityReport +} + +@documentation("Imports a full config from a TOML document, persisting dimensions, default-configs and contexts in a single transaction after validating the document.") +@http(method: "POST", uri: "/config/toml/import") +@tags(["Configuration Management"]) +operation ImportConfigToml { + input := with [WorkspaceMixin] { + @documentation("Whether to merge (default) or replace existing workspace config.") + @httpHeader("x-import-mode") + @notProperty + mode: ImportMode + + @documentation("When false, entities that already exist are skipped instead of updated. Defaults to true.") + @httpHeader("x-import-overwrite") + @notProperty + overwrite: Boolean + + @documentation("Whether to abort (default) or continue on per-entity errors.") + @httpHeader("x-import-on-error") + @notProperty + on_error: ImportOnError + + @documentation("When true, validates and summarises the import without persisting anything. Defaults to false.") + @httpHeader("x-import-dry-run") + @notProperty + dry_run: Boolean + + @documentation("When true, deep-merges object-valued default-configs with the existing value. Defaults to false.") + @httpHeader("x-import-value-merge") + @notProperty + value_merge: Boolean + + @httpHeader("x-config-tags") + @notProperty + config_tags: String + + @httpPayload + @required + @notProperty + toml_config: String + } + + output: ImportConfigOutput +} + +@documentation("Imports a full config from a JSON document, persisting dimensions, default-configs and contexts in a single transaction after validating the document.") +@http(method: "POST", uri: "/config/json/import") +@tags(["Configuration Management"]) +operation ImportConfigJson { + input := with [WorkspaceMixin] { + @documentation("Whether to merge (default) or replace existing workspace config.") + @httpHeader("x-import-mode") + @notProperty + mode: ImportMode + + @documentation("When false, entities that already exist are skipped instead of updated. Defaults to true.") + @httpHeader("x-import-overwrite") + @notProperty + overwrite: Boolean + + @documentation("Whether to abort (default) or continue on per-entity errors.") + @httpHeader("x-import-on-error") + @notProperty + on_error: ImportOnError + + @documentation("When true, validates and summarises the import without persisting anything. Defaults to false.") + @httpHeader("x-import-dry-run") + @notProperty + dry_run: Boolean + + @documentation("When true, deep-merges object-valued default-configs with the existing value. Defaults to false.") + @httpHeader("x-import-value-merge") + @notProperty + value_merge: Boolean + + @httpHeader("x-config-tags") + @notProperty + config_tags: String + + @httpPayload + @required + @notProperty + json_config: String + } + + output: ImportConfigOutput +} + enum MergeStrategy { MERGE REPLACE diff --git a/smithy/models/main.smithy b/smithy/models/main.smithy index e67e344d5..b722bc3e6 100644 --- a/smithy/models/main.smithy +++ b/smithy/models/main.smithy @@ -30,6 +30,10 @@ service Superposition { Secret MasterKey ] + operations: [ + ImportConfigToml + ImportConfigJson + ] errors: [ InternalServerError ] diff --git a/tests/src/config_import.test.ts b/tests/src/config_import.test.ts index 73668803a..bc734ccd0 100644 --- a/tests/src/config_import.test.ts +++ b/tests/src/config_import.test.ts @@ -10,10 +10,13 @@ import { import { superpositionClient, ENV } from "../env.ts"; import { describe, test, expect, beforeAll } from "bun:test"; -// Import is exposed by POSTing a body to the same endpoints used for export -// (`/config/toml`, `/config/json`). The generated SDK only models the -// (read-only) export side, so the import calls here go through `fetch` while the -// SDK is used for setup and for verifying the resulting workspace state. +// Import lives at dedicated endpoints: POST /config/{toml,json}/import. These +// are modelled in Smithy as ImportConfigToml / ImportConfigJson, but until the +// SDK is regenerated (`make smithy-updates`) the commands aren't available, so +// the import calls here go through `fetch`. The SDK is used for setup and for +// verifying the resulting workspace state. Once the SDK is regenerated, the +// `importConfig` helper can be replaced with ImportConfigTomlCommand / +// ImportConfigJsonCommand. // // Import in `replace` mode mirrors the *entire* workspace, so these tests run in // their own dedicated workspace to avoid clobbering data created by other suites. @@ -49,7 +52,7 @@ async function importConfig( body: string, extraHeaders: Record = {}, ): Promise<{ status: number; summary?: ImportSummary; raw: string }> { - const res = await fetch(`${ENV.baseUrl}/config/${format}`, { + const res = await fetch(`${ENV.baseUrl}/config/${format}/import`, { method: "POST", headers: { Authorization: "Bearer some-token", From c7b8662704a1d71375047347b71d6cfa79f4641b Mon Sep 17 00:00:00 2001 From: Natarajan Kannan Date: Thu, 4 Jun 2026 21:34:39 +0530 Subject: [PATCH 05/15] build(smithy): clean output dir before regenerating SDKs make smithy-updates only depended on smithy-clients (which only depends on smithy-build). smithy build is incremental and does not delete codegen for operations that no longer exist in the model, so any output left over from a prior branch's build was being copied into clients/ and crates/superposition_sdk/ on the next make smithy-updates run, producing a dirty tree. CI never caught this because actions/checkout starts from a fresh tree with no smithy/output to begin with. Locally, anyone who switches between branches with different smithy operations (e.g. a feature branch that adds ImportConfigJson/ImportConfigToml) and then re-runs make smithy-updates hits the stale-output problem. Add smithy-clean as the first prerequisite of smithy-updates so the run always starts from an empty output dir. Direct invocations of smithy-clients and smithy-build are unchanged, so incremental local workflows still work. --- makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefile b/makefile index e4385c7b4..ac495cda4 100644 --- a/makefile +++ b/makefile @@ -318,7 +318,7 @@ smithy-api-docs: smithy-build cp $(SMITHY_BUILD_SRC)/openapi/Superposition.openapi.json docs/docs/api/ cd docs && npm ci && npm run openapi-docs -smithy-updates: smithy-clients smithy-api-docs +smithy-updates: smithy-clean smithy-clients smithy-api-docs leptosfmt: leptosfmt $(LEPTOS_FMT_FLAGS) $(LEPTOS_PACKAGES) From 5b5f7d6df37990664ea40abcd92bb634b91fce9f Mon Sep 17 00:00:00 2001 From: Natarajan Kannan Date: Thu, 4 Jun 2026 21:35:35 +0530 Subject: [PATCH 06/15] chore(smithy): regenerate SDKs and OpenAPI for ImportConfig{Json,Toml} c157366d added the ImportConfigJson and ImportConfigToml service operations to the Smithy model but explicitly deferred regenerating the SDKs because the codegen plugins live in a Maven repo that environment couldn't reach. This commit performs that regeneration now that we have a working environment. Covers: - crates/superposition_sdk: new operation/, client/, types/ and protocol_serde modules for both ImportConfig commands, plus updated dispatch in client.rs, operation.rs, types.rs and error_meta.rs. - clients/java/sdk: new ImportConfig{Json,Toml}{,Input,Output}, ImportMode, ImportOnError, ImportEntityReport, ImportErrorItem model classes and updated SharedSchemas / SharedSerde dispatch tables. - clients/javascript/sdk: new ImportConfig{Json,Toml}Command commands and updated Superposition.ts, SuperpositionClient.ts, commands/index.ts, models_0.ts and protocols/Aws_restJson1.ts. - clients/python/sdk: new entries in models.py, _private/schemas.py, client.py, config.py, serialize.py and deserialize.py. - clients/haskell/sdk: new Command/ImportConfig{Json,Toml}.hs and Model/ImportConfig{Json,Toml}{Input,Output}, ImportMode, ImportOnError, ImportEntityReport, ImportErrorItem modules, plus updated cabal file. - docs/docs/api: regenerated Superposition.openapi.json, new import-config-{json,toml}.* docs files and an updated sidebar. The four SuperpositionClient*.java files also change due to the known upstream nondeterministic ordering of httpBasicAuth vs httpBearerAuth fields in the smithy-java codegen; CI already excludes these from the dirty-tree check. Generated via make smithy-updates with the makefile fix from the preceding commit (build(smithy): clean output dir before regenerating SDKs). --- .../Superposition/Command/ImportConfigJson.hs | 40 ++ .../Superposition/Command/ImportConfigToml.hs | 40 ++ .../Model/ImportConfigJsonInput.hs | 194 +++++++ .../Model/ImportConfigJsonOutput.hs | 154 ++++++ .../Model/ImportConfigTomlInput.hs | 194 +++++++ .../Model/ImportConfigTomlOutput.hs | 154 ++++++ .../Superposition/Model/ImportEntityReport.hs | 122 +++++ .../Io/Superposition/Model/ImportErrorItem.hs | 81 +++ .../sdk/Io/Superposition/Model/ImportMode.hs | 44 ++ .../Io/Superposition/Model/ImportOnError.hs | 44 ++ clients/haskell/sdk/SuperpositionSDK.cabal | 10 + .../client/SuperpositionAsyncClient.java | 40 ++ .../client/SuperpositionAsyncClientImpl.java | 15 +- .../client/SuperpositionClient.java | 40 ++ .../client/SuperpositionClientImpl.java | 25 +- .../superposition/model/ImportConfigJson.java | 94 ++++ .../model/ImportConfigJsonInput.java | 419 ++++++++++++++ .../model/ImportConfigJsonOutput.java | 325 +++++++++++ .../superposition/model/ImportConfigToml.java | 94 ++++ .../model/ImportConfigTomlInput.java | 419 ++++++++++++++ .../model/ImportConfigTomlOutput.java | 325 +++++++++++ .../model/ImportEntityReport.java | 299 ++++++++++ .../superposition/model/ImportErrorItem.java | 204 +++++++ .../superposition/model/ImportMode.java | 165 ++++++ .../superposition/model/ImportOnError.java | 164 ++++++ .../superposition/model/SharedSchemas.java | 4 + .../superposition/model/SharedSerde.java | 31 ++ clients/javascript/sdk/src/Superposition.ts | 46 ++ .../javascript/sdk/src/SuperpositionClient.ts | 12 + .../src/commands/ImportConfigJsonCommand.ts | 141 +++++ .../src/commands/ImportConfigTomlCommand.ts | 141 +++++ clients/javascript/sdk/src/commands/index.ts | 2 + clients/javascript/sdk/src/models/models_0.ts | 165 ++++++ .../sdk/src/protocols/Aws_restJson1.ts | 136 +++++ .../sdk/superposition_sdk/_private/schemas.py | 513 ++++++++++++++++++ .../python/sdk/superposition_sdk/client.py | 66 +++ .../python/sdk/superposition_sdk/config.py | 96 +++- .../sdk/superposition_sdk/deserialize.py | 52 ++ .../python/sdk/superposition_sdk/models.py | 504 ++++++++++++++++- .../python/sdk/superposition_sdk/serialize.py | 94 ++++ crates/superposition_sdk/src/client.rs | 4 + .../src/client/import_config_json.rs | 27 + .../src/client/import_config_toml.rs | 27 + crates/superposition_sdk/src/error_meta.rs | 42 ++ crates/superposition_sdk/src/operation.rs | 6 + .../src/operation/import_config_json.rs | 299 ++++++++++ .../_import_config_json_input.rs | 231 ++++++++ .../_import_config_json_output.rs | 189 +++++++ .../operation/import_config_json/builders.rs | 226 ++++++++ .../src/operation/import_config_toml.rs | 299 ++++++++++ .../_import_config_toml_input.rs | 231 ++++++++ .../_import_config_toml_output.rs | 189 +++++++ .../operation/import_config_toml/builders.rs | 226 ++++++++ .../superposition_sdk/src/protocol_serde.rs | 14 + .../shape_import_config_json.rs | 208 +++++++ .../shape_import_config_json_input.rs | 12 + .../shape_import_config_toml.rs | 208 +++++++ .../shape_import_config_toml_input.rs | 12 + .../shape_import_entity_report.rs | 60 ++ .../protocol_serde/shape_import_error_item.rs | 45 ++ .../protocol_serde/shape_import_error_list.rs | 30 + crates/superposition_sdk/src/serde_util.rs | 31 ++ crates/superposition_sdk/src/types.rs | 16 + .../src/types/_import_entity_report.rs | 170 ++++++ .../src/types/_import_error_item.rs | 85 +++ .../src/types/_import_mode.rs | 107 ++++ .../src/types/_import_on_error.rs | 107 ++++ .../superposition_sdk/src/types/builders.rs | 4 + docs/docs/api/Superposition.openapi.json | 347 ++++++++++++ .../api/import-config-json.ParamsDetails.json | 1 + .../api/import-config-json.RequestSchema.json | 1 + .../api/import-config-json.StatusCodes.json | 1 + docs/docs/api/import-config-json.api.mdx | 69 +++ .../api/import-config-toml.ParamsDetails.json | 1 + .../api/import-config-toml.RequestSchema.json | 1 + .../api/import-config-toml.StatusCodes.json | 1 + docs/docs/api/import-config-toml.api.mdx | 69 +++ docs/docs/api/sidebar.ts | 14 + 78 files changed, 9313 insertions(+), 5 deletions(-) create mode 100644 clients/haskell/sdk/Io/Superposition/Command/ImportConfigJson.hs create mode 100644 clients/haskell/sdk/Io/Superposition/Command/ImportConfigToml.hs create mode 100644 clients/haskell/sdk/Io/Superposition/Model/ImportConfigJsonInput.hs create mode 100644 clients/haskell/sdk/Io/Superposition/Model/ImportConfigJsonOutput.hs create mode 100644 clients/haskell/sdk/Io/Superposition/Model/ImportConfigTomlInput.hs create mode 100644 clients/haskell/sdk/Io/Superposition/Model/ImportConfigTomlOutput.hs create mode 100644 clients/haskell/sdk/Io/Superposition/Model/ImportEntityReport.hs create mode 100644 clients/haskell/sdk/Io/Superposition/Model/ImportErrorItem.hs create mode 100644 clients/haskell/sdk/Io/Superposition/Model/ImportMode.hs create mode 100644 clients/haskell/sdk/Io/Superposition/Model/ImportOnError.hs create mode 100644 clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigJson.java create mode 100644 clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigJsonInput.java create mode 100644 clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigJsonOutput.java create mode 100644 clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigToml.java create mode 100644 clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigTomlInput.java create mode 100644 clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigTomlOutput.java create mode 100644 clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportEntityReport.java create mode 100644 clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportErrorItem.java create mode 100644 clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportMode.java create mode 100644 clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportOnError.java create mode 100644 clients/javascript/sdk/src/commands/ImportConfigJsonCommand.ts create mode 100644 clients/javascript/sdk/src/commands/ImportConfigTomlCommand.ts create mode 100644 crates/superposition_sdk/src/client/import_config_json.rs create mode 100644 crates/superposition_sdk/src/client/import_config_toml.rs create mode 100644 crates/superposition_sdk/src/operation/import_config_json.rs create mode 100644 crates/superposition_sdk/src/operation/import_config_json/_import_config_json_input.rs create mode 100644 crates/superposition_sdk/src/operation/import_config_json/_import_config_json_output.rs create mode 100644 crates/superposition_sdk/src/operation/import_config_json/builders.rs create mode 100644 crates/superposition_sdk/src/operation/import_config_toml.rs create mode 100644 crates/superposition_sdk/src/operation/import_config_toml/_import_config_toml_input.rs create mode 100644 crates/superposition_sdk/src/operation/import_config_toml/_import_config_toml_output.rs create mode 100644 crates/superposition_sdk/src/operation/import_config_toml/builders.rs create mode 100644 crates/superposition_sdk/src/protocol_serde/shape_import_config_json.rs create mode 100644 crates/superposition_sdk/src/protocol_serde/shape_import_config_json_input.rs create mode 100644 crates/superposition_sdk/src/protocol_serde/shape_import_config_toml.rs create mode 100644 crates/superposition_sdk/src/protocol_serde/shape_import_config_toml_input.rs create mode 100644 crates/superposition_sdk/src/protocol_serde/shape_import_entity_report.rs create mode 100644 crates/superposition_sdk/src/protocol_serde/shape_import_error_item.rs create mode 100644 crates/superposition_sdk/src/protocol_serde/shape_import_error_list.rs create mode 100644 crates/superposition_sdk/src/types/_import_entity_report.rs create mode 100644 crates/superposition_sdk/src/types/_import_error_item.rs create mode 100644 crates/superposition_sdk/src/types/_import_mode.rs create mode 100644 crates/superposition_sdk/src/types/_import_on_error.rs create mode 100644 docs/docs/api/import-config-json.ParamsDetails.json create mode 100644 docs/docs/api/import-config-json.RequestSchema.json create mode 100644 docs/docs/api/import-config-json.StatusCodes.json create mode 100644 docs/docs/api/import-config-json.api.mdx create mode 100644 docs/docs/api/import-config-toml.ParamsDetails.json create mode 100644 docs/docs/api/import-config-toml.RequestSchema.json create mode 100644 docs/docs/api/import-config-toml.StatusCodes.json create mode 100644 docs/docs/api/import-config-toml.api.mdx diff --git a/clients/haskell/sdk/Io/Superposition/Command/ImportConfigJson.hs b/clients/haskell/sdk/Io/Superposition/Command/ImportConfigJson.hs new file mode 100644 index 000000000..02556106c --- /dev/null +++ b/clients/haskell/sdk/Io/Superposition/Command/ImportConfigJson.hs @@ -0,0 +1,40 @@ +module Io.Superposition.Command.ImportConfigJson ( + ImportConfigJsonError (..), + importConfigJson +) where +import qualified Data.Aeson +import qualified Data.Maybe +import qualified Data.Text +import qualified GHC.Generics +import qualified GHC.Show +import qualified Io.Superposition.Model.ImportConfigJsonInput +import qualified Io.Superposition.Model.ImportConfigJsonOutput +import qualified Io.Superposition.Model.InternalServerError +import qualified Io.Superposition.SuperpositionClient +import qualified Io.Superposition.Utility + +data ImportConfigJsonError = + InternalServerError Io.Superposition.Model.InternalServerError.InternalServerError + | BuilderError Data.Text.Text + | DeSerializationError Io.Superposition.Utility.HttpMetadata Data.Text.Text + | UnexpectedError (Data.Maybe.Maybe Io.Superposition.Utility.HttpMetadata) Data.Text.Text + deriving (GHC.Generics.Generic, GHC.Show.Show) + +instance Data.Aeson.ToJSON ImportConfigJsonError +instance Io.Superposition.Utility.OperationError ImportConfigJsonError where + mkBuilderError = BuilderError + mkDeSerializationError = DeSerializationError + mkUnexpectedError = UnexpectedError + + getErrorParser status + | status == (Io.Superposition.Utility.expectedStatus @Io.Superposition.Model.InternalServerError.InternalServerError) = Just (fmap InternalServerError (Io.Superposition.Utility.responseParser @Io.Superposition.Model.InternalServerError.InternalServerError)) + | otherwise = Nothing + + +importConfigJson :: Io.Superposition.SuperpositionClient.SuperpositionClient -> Io.Superposition.Model.ImportConfigJsonInput.ImportConfigJsonInputBuilder () -> IO (Either ImportConfigJsonError Io.Superposition.Model.ImportConfigJsonOutput.ImportConfigJsonOutput) +importConfigJson client builder = + let endpoint = Io.Superposition.SuperpositionClient.endpointUri client + manager = Io.Superposition.SuperpositionClient.httpManager client + auth = Io.Superposition.SuperpositionClient.getAuth client + in Io.Superposition.Utility.runOperation endpoint manager auth (Io.Superposition.Model.ImportConfigJsonInput.build builder) + diff --git a/clients/haskell/sdk/Io/Superposition/Command/ImportConfigToml.hs b/clients/haskell/sdk/Io/Superposition/Command/ImportConfigToml.hs new file mode 100644 index 000000000..60c5174d2 --- /dev/null +++ b/clients/haskell/sdk/Io/Superposition/Command/ImportConfigToml.hs @@ -0,0 +1,40 @@ +module Io.Superposition.Command.ImportConfigToml ( + ImportConfigTomlError (..), + importConfigToml +) where +import qualified Data.Aeson +import qualified Data.Maybe +import qualified Data.Text +import qualified GHC.Generics +import qualified GHC.Show +import qualified Io.Superposition.Model.ImportConfigTomlInput +import qualified Io.Superposition.Model.ImportConfigTomlOutput +import qualified Io.Superposition.Model.InternalServerError +import qualified Io.Superposition.SuperpositionClient +import qualified Io.Superposition.Utility + +data ImportConfigTomlError = + InternalServerError Io.Superposition.Model.InternalServerError.InternalServerError + | BuilderError Data.Text.Text + | DeSerializationError Io.Superposition.Utility.HttpMetadata Data.Text.Text + | UnexpectedError (Data.Maybe.Maybe Io.Superposition.Utility.HttpMetadata) Data.Text.Text + deriving (GHC.Generics.Generic, GHC.Show.Show) + +instance Data.Aeson.ToJSON ImportConfigTomlError +instance Io.Superposition.Utility.OperationError ImportConfigTomlError where + mkBuilderError = BuilderError + mkDeSerializationError = DeSerializationError + mkUnexpectedError = UnexpectedError + + getErrorParser status + | status == (Io.Superposition.Utility.expectedStatus @Io.Superposition.Model.InternalServerError.InternalServerError) = Just (fmap InternalServerError (Io.Superposition.Utility.responseParser @Io.Superposition.Model.InternalServerError.InternalServerError)) + | otherwise = Nothing + + +importConfigToml :: Io.Superposition.SuperpositionClient.SuperpositionClient -> Io.Superposition.Model.ImportConfigTomlInput.ImportConfigTomlInputBuilder () -> IO (Either ImportConfigTomlError Io.Superposition.Model.ImportConfigTomlOutput.ImportConfigTomlOutput) +importConfigToml client builder = + let endpoint = Io.Superposition.SuperpositionClient.endpointUri client + manager = Io.Superposition.SuperpositionClient.httpManager client + auth = Io.Superposition.SuperpositionClient.getAuth client + in Io.Superposition.Utility.runOperation endpoint manager auth (Io.Superposition.Model.ImportConfigTomlInput.build builder) + diff --git a/clients/haskell/sdk/Io/Superposition/Model/ImportConfigJsonInput.hs b/clients/haskell/sdk/Io/Superposition/Model/ImportConfigJsonInput.hs new file mode 100644 index 000000000..2d0002891 --- /dev/null +++ b/clients/haskell/sdk/Io/Superposition/Model/ImportConfigJsonInput.hs @@ -0,0 +1,194 @@ +module Io.Superposition.Model.ImportConfigJsonInput ( + setWorkspaceId, + setOrgId, + setMode, + setOverwrite, + setOnError, + setDryRun, + setValueMerge, + setConfigTags, + setJsonConfig, + build, + ImportConfigJsonInputBuilder, + ImportConfigJsonInput, + workspace_id, + org_id, + mode, + overwrite, + on_error, + dry_run, + value_merge, + config_tags, + json_config +) where +import qualified Control.Applicative +import qualified Control.Monad.State.Strict +import qualified Data.Aeson +import qualified Data.Either +import qualified Data.Eq +import qualified Data.Functor +import qualified Data.Maybe +import qualified Data.Text +import qualified GHC.Generics +import qualified GHC.Show +import qualified Io.Superposition.Model.ImportMode +import qualified Io.Superposition.Model.ImportOnError +import qualified Io.Superposition.Utility +import qualified Network.HTTP.Types.Method + +data ImportConfigJsonInput = ImportConfigJsonInput { + workspace_id :: Data.Text.Text, + org_id :: Data.Text.Text, + mode :: Data.Maybe.Maybe Io.Superposition.Model.ImportMode.ImportMode, + overwrite :: Data.Maybe.Maybe Bool, + on_error :: Data.Maybe.Maybe Io.Superposition.Model.ImportOnError.ImportOnError, + dry_run :: Data.Maybe.Maybe Bool, + value_merge :: Data.Maybe.Maybe Bool, + config_tags :: Data.Maybe.Maybe Data.Text.Text, + json_config :: Data.Text.Text +} deriving ( + GHC.Show.Show, + Data.Eq.Eq, + GHC.Generics.Generic + ) + +instance Data.Aeson.ToJSON ImportConfigJsonInput where + toJSON a = Data.Aeson.object [ + "workspace_id" Data.Aeson..= workspace_id a, + "org_id" Data.Aeson..= org_id a, + "mode" Data.Aeson..= mode a, + "overwrite" Data.Aeson..= overwrite a, + "on_error" Data.Aeson..= on_error a, + "dry_run" Data.Aeson..= dry_run a, + "value_merge" Data.Aeson..= value_merge a, + "config_tags" Data.Aeson..= config_tags a, + "json_config" Data.Aeson..= json_config a + ] + + +instance Io.Superposition.Utility.SerializeBody ImportConfigJsonInput + +instance Data.Aeson.FromJSON ImportConfigJsonInput where + parseJSON = Data.Aeson.withObject "ImportConfigJsonInput" $ \v -> ImportConfigJsonInput + Data.Functor.<$> (v Data.Aeson..: "workspace_id") + Control.Applicative.<*> (v Data.Aeson..: "org_id") + Control.Applicative.<*> (v Data.Aeson..:? "mode") + Control.Applicative.<*> (v Data.Aeson..:? "overwrite") + Control.Applicative.<*> (v Data.Aeson..:? "on_error") + Control.Applicative.<*> (v Data.Aeson..:? "dry_run") + Control.Applicative.<*> (v Data.Aeson..:? "value_merge") + Control.Applicative.<*> (v Data.Aeson..:? "config_tags") + Control.Applicative.<*> (v Data.Aeson..: "json_config") + + + + +data ImportConfigJsonInputBuilderState = ImportConfigJsonInputBuilderState { + workspace_idBuilderState :: Data.Maybe.Maybe Data.Text.Text, + org_idBuilderState :: Data.Maybe.Maybe Data.Text.Text, + modeBuilderState :: Data.Maybe.Maybe Io.Superposition.Model.ImportMode.ImportMode, + overwriteBuilderState :: Data.Maybe.Maybe Bool, + on_errorBuilderState :: Data.Maybe.Maybe Io.Superposition.Model.ImportOnError.ImportOnError, + dry_runBuilderState :: Data.Maybe.Maybe Bool, + value_mergeBuilderState :: Data.Maybe.Maybe Bool, + config_tagsBuilderState :: Data.Maybe.Maybe Data.Text.Text, + json_configBuilderState :: Data.Maybe.Maybe Data.Text.Text +} deriving ( + GHC.Generics.Generic + ) + +defaultBuilderState :: ImportConfigJsonInputBuilderState +defaultBuilderState = ImportConfigJsonInputBuilderState { + workspace_idBuilderState = Data.Maybe.Nothing, + org_idBuilderState = Data.Maybe.Nothing, + modeBuilderState = Data.Maybe.Nothing, + overwriteBuilderState = Data.Maybe.Nothing, + on_errorBuilderState = Data.Maybe.Nothing, + dry_runBuilderState = Data.Maybe.Nothing, + value_mergeBuilderState = Data.Maybe.Nothing, + config_tagsBuilderState = Data.Maybe.Nothing, + json_configBuilderState = Data.Maybe.Nothing +} + +type ImportConfigJsonInputBuilder = Control.Monad.State.Strict.State ImportConfigJsonInputBuilderState + +setWorkspaceId :: Data.Text.Text -> ImportConfigJsonInputBuilder () +setWorkspaceId value = + Control.Monad.State.Strict.modify (\s -> (s { workspace_idBuilderState = Data.Maybe.Just value })) + +setOrgId :: Data.Text.Text -> ImportConfigJsonInputBuilder () +setOrgId value = + Control.Monad.State.Strict.modify (\s -> (s { org_idBuilderState = Data.Maybe.Just value })) + +setMode :: Data.Maybe.Maybe Io.Superposition.Model.ImportMode.ImportMode -> ImportConfigJsonInputBuilder () +setMode value = + Control.Monad.State.Strict.modify (\s -> (s { modeBuilderState = value })) + +setOverwrite :: Data.Maybe.Maybe Bool -> ImportConfigJsonInputBuilder () +setOverwrite value = + Control.Monad.State.Strict.modify (\s -> (s { overwriteBuilderState = value })) + +setOnError :: Data.Maybe.Maybe Io.Superposition.Model.ImportOnError.ImportOnError -> ImportConfigJsonInputBuilder () +setOnError value = + Control.Monad.State.Strict.modify (\s -> (s { on_errorBuilderState = value })) + +setDryRun :: Data.Maybe.Maybe Bool -> ImportConfigJsonInputBuilder () +setDryRun value = + Control.Monad.State.Strict.modify (\s -> (s { dry_runBuilderState = value })) + +setValueMerge :: Data.Maybe.Maybe Bool -> ImportConfigJsonInputBuilder () +setValueMerge value = + Control.Monad.State.Strict.modify (\s -> (s { value_mergeBuilderState = value })) + +setConfigTags :: Data.Maybe.Maybe Data.Text.Text -> ImportConfigJsonInputBuilder () +setConfigTags value = + Control.Monad.State.Strict.modify (\s -> (s { config_tagsBuilderState = value })) + +setJsonConfig :: Data.Text.Text -> ImportConfigJsonInputBuilder () +setJsonConfig value = + Control.Monad.State.Strict.modify (\s -> (s { json_configBuilderState = Data.Maybe.Just value })) + +build :: ImportConfigJsonInputBuilder () -> Data.Either.Either Data.Text.Text ImportConfigJsonInput +build builder = do + let (_, st) = Control.Monad.State.Strict.runState builder defaultBuilderState + workspace_id' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigJsonInput.ImportConfigJsonInput.workspace_id is a required property.") Data.Either.Right (workspace_idBuilderState st) + org_id' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigJsonInput.ImportConfigJsonInput.org_id is a required property.") Data.Either.Right (org_idBuilderState st) + mode' <- Data.Either.Right (modeBuilderState st) + overwrite' <- Data.Either.Right (overwriteBuilderState st) + on_error' <- Data.Either.Right (on_errorBuilderState st) + dry_run' <- Data.Either.Right (dry_runBuilderState st) + value_merge' <- Data.Either.Right (value_mergeBuilderState st) + config_tags' <- Data.Either.Right (config_tagsBuilderState st) + json_config' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigJsonInput.ImportConfigJsonInput.json_config is a required property.") Data.Either.Right (json_configBuilderState st) + Data.Either.Right (ImportConfigJsonInput { + workspace_id = workspace_id', + org_id = org_id', + mode = mode', + overwrite = overwrite', + on_error = on_error', + dry_run = dry_run', + value_merge = value_merge', + config_tags = config_tags', + json_config = json_config' + }) + + +instance Io.Superposition.Utility.IntoRequestBuilder ImportConfigJsonInput where + intoRequestBuilder self = do + Io.Superposition.Utility.setMethod Network.HTTP.Types.Method.methodPost + Io.Superposition.Utility.setPath [ + "config", + "json", + "import" + ] + + Io.Superposition.Utility.serHeader "x-workspace" (workspace_id self) + Io.Superposition.Utility.serHeader "x-import-mode" (mode self) + Io.Superposition.Utility.serHeader "x-org-id" (org_id self) + Io.Superposition.Utility.serHeader "x-import-on-error" (on_error self) + Io.Superposition.Utility.serHeader "x-import-dry-run" (dry_run self) + Io.Superposition.Utility.serHeader "x-import-overwrite" (overwrite self) + Io.Superposition.Utility.serHeader "x-import-value-merge" (value_merge self) + Io.Superposition.Utility.serHeader "x-config-tags" (config_tags self) + Io.Superposition.Utility.serBody "text/plain" (json_config self) + diff --git a/clients/haskell/sdk/Io/Superposition/Model/ImportConfigJsonOutput.hs b/clients/haskell/sdk/Io/Superposition/Model/ImportConfigJsonOutput.hs new file mode 100644 index 000000000..1eeb3d457 --- /dev/null +++ b/clients/haskell/sdk/Io/Superposition/Model/ImportConfigJsonOutput.hs @@ -0,0 +1,154 @@ +module Io.Superposition.Model.ImportConfigJsonOutput ( + setMode, + setDryRun, + setConfigVersion, + setDimensions, + setDefaultConfigs, + setContexts, + build, + ImportConfigJsonOutputBuilder, + ImportConfigJsonOutput, + mode, + dry_run, + config_version, + dimensions, + default_configs, + contexts +) where +import qualified Control.Applicative +import qualified Control.Monad.State.Strict +import qualified Data.Aeson +import qualified Data.Either +import qualified Data.Eq +import qualified Data.Functor +import qualified Data.Maybe +import qualified Data.Text +import qualified GHC.Generics +import qualified GHC.Show +import qualified Io.Superposition.Model.ImportEntityReport +import qualified Io.Superposition.Utility +import qualified Network.HTTP.Types + +data ImportConfigJsonOutput = ImportConfigJsonOutput { + mode :: Data.Text.Text, + dry_run :: Bool, + config_version :: Data.Maybe.Maybe Data.Text.Text, + dimensions :: Io.Superposition.Model.ImportEntityReport.ImportEntityReport, + default_configs :: Io.Superposition.Model.ImportEntityReport.ImportEntityReport, + contexts :: Io.Superposition.Model.ImportEntityReport.ImportEntityReport +} deriving ( + GHC.Show.Show, + Data.Eq.Eq, + GHC.Generics.Generic + ) + +instance Data.Aeson.ToJSON ImportConfigJsonOutput where + toJSON a = Data.Aeson.object [ + "mode" Data.Aeson..= mode a, + "dry_run" Data.Aeson..= dry_run a, + "config_version" Data.Aeson..= config_version a, + "dimensions" Data.Aeson..= dimensions a, + "default_configs" Data.Aeson..= default_configs a, + "contexts" Data.Aeson..= contexts a + ] + + +instance Io.Superposition.Utility.SerializeBody ImportConfigJsonOutput + +instance Data.Aeson.FromJSON ImportConfigJsonOutput where + parseJSON = Data.Aeson.withObject "ImportConfigJsonOutput" $ \v -> ImportConfigJsonOutput + Data.Functor.<$> (v Data.Aeson..: "mode") + Control.Applicative.<*> (v Data.Aeson..: "dry_run") + Control.Applicative.<*> (v Data.Aeson..:? "config_version") + Control.Applicative.<*> (v Data.Aeson..: "dimensions") + Control.Applicative.<*> (v Data.Aeson..: "default_configs") + Control.Applicative.<*> (v Data.Aeson..: "contexts") + + + + +data ImportConfigJsonOutputBuilderState = ImportConfigJsonOutputBuilderState { + modeBuilderState :: Data.Maybe.Maybe Data.Text.Text, + dry_runBuilderState :: Data.Maybe.Maybe Bool, + config_versionBuilderState :: Data.Maybe.Maybe Data.Text.Text, + dimensionsBuilderState :: Data.Maybe.Maybe Io.Superposition.Model.ImportEntityReport.ImportEntityReport, + default_configsBuilderState :: Data.Maybe.Maybe Io.Superposition.Model.ImportEntityReport.ImportEntityReport, + contextsBuilderState :: Data.Maybe.Maybe Io.Superposition.Model.ImportEntityReport.ImportEntityReport +} deriving ( + GHC.Generics.Generic + ) + +defaultBuilderState :: ImportConfigJsonOutputBuilderState +defaultBuilderState = ImportConfigJsonOutputBuilderState { + modeBuilderState = Data.Maybe.Nothing, + dry_runBuilderState = Data.Maybe.Nothing, + config_versionBuilderState = Data.Maybe.Nothing, + dimensionsBuilderState = Data.Maybe.Nothing, + default_configsBuilderState = Data.Maybe.Nothing, + contextsBuilderState = Data.Maybe.Nothing +} + +type ImportConfigJsonOutputBuilder = Control.Monad.State.Strict.State ImportConfigJsonOutputBuilderState + +setMode :: Data.Text.Text -> ImportConfigJsonOutputBuilder () +setMode value = + Control.Monad.State.Strict.modify (\s -> (s { modeBuilderState = Data.Maybe.Just value })) + +setDryRun :: Bool -> ImportConfigJsonOutputBuilder () +setDryRun value = + Control.Monad.State.Strict.modify (\s -> (s { dry_runBuilderState = Data.Maybe.Just value })) + +setConfigVersion :: Data.Maybe.Maybe Data.Text.Text -> ImportConfigJsonOutputBuilder () +setConfigVersion value = + Control.Monad.State.Strict.modify (\s -> (s { config_versionBuilderState = value })) + +setDimensions :: Io.Superposition.Model.ImportEntityReport.ImportEntityReport -> ImportConfigJsonOutputBuilder () +setDimensions value = + Control.Monad.State.Strict.modify (\s -> (s { dimensionsBuilderState = Data.Maybe.Just value })) + +setDefaultConfigs :: Io.Superposition.Model.ImportEntityReport.ImportEntityReport -> ImportConfigJsonOutputBuilder () +setDefaultConfigs value = + Control.Monad.State.Strict.modify (\s -> (s { default_configsBuilderState = Data.Maybe.Just value })) + +setContexts :: Io.Superposition.Model.ImportEntityReport.ImportEntityReport -> ImportConfigJsonOutputBuilder () +setContexts value = + Control.Monad.State.Strict.modify (\s -> (s { contextsBuilderState = Data.Maybe.Just value })) + +build :: ImportConfigJsonOutputBuilder () -> Data.Either.Either Data.Text.Text ImportConfigJsonOutput +build builder = do + let (_, st) = Control.Monad.State.Strict.runState builder defaultBuilderState + mode' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigJsonOutput.ImportConfigJsonOutput.mode is a required property.") Data.Either.Right (modeBuilderState st) + dry_run' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigJsonOutput.ImportConfigJsonOutput.dry_run is a required property.") Data.Either.Right (dry_runBuilderState st) + config_version' <- Data.Either.Right (config_versionBuilderState st) + dimensions' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigJsonOutput.ImportConfigJsonOutput.dimensions is a required property.") Data.Either.Right (dimensionsBuilderState st) + default_configs' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigJsonOutput.ImportConfigJsonOutput.default_configs is a required property.") Data.Either.Right (default_configsBuilderState st) + contexts' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigJsonOutput.ImportConfigJsonOutput.contexts is a required property.") Data.Either.Right (contextsBuilderState st) + Data.Either.Right (ImportConfigJsonOutput { + mode = mode', + dry_run = dry_run', + config_version = config_version', + dimensions = dimensions', + default_configs = default_configs', + contexts = contexts' + }) + + +instance Io.Superposition.Utility.FromResponseParser ImportConfigJsonOutput where + expectedStatus = (Network.HTTP.Types.mkStatus 200 "") + responseParser = do + + var0 <- Io.Superposition.Utility.deSerField "mode" + var1 <- Io.Superposition.Utility.deSerField "dry_run" + var2 <- Io.Superposition.Utility.deSerField "contexts" + var3 <- Io.Superposition.Utility.deSerField "default_configs" + var4 <- Io.Superposition.Utility.deSerField "config_version" + var5 <- Io.Superposition.Utility.deSerField "dimensions" + pure $ ImportConfigJsonOutput { + mode = var0, + dry_run = var1, + config_version = var4, + dimensions = var5, + default_configs = var3, + contexts = var2 + } + diff --git a/clients/haskell/sdk/Io/Superposition/Model/ImportConfigTomlInput.hs b/clients/haskell/sdk/Io/Superposition/Model/ImportConfigTomlInput.hs new file mode 100644 index 000000000..00145e75b --- /dev/null +++ b/clients/haskell/sdk/Io/Superposition/Model/ImportConfigTomlInput.hs @@ -0,0 +1,194 @@ +module Io.Superposition.Model.ImportConfigTomlInput ( + setWorkspaceId, + setOrgId, + setMode, + setOverwrite, + setOnError, + setDryRun, + setValueMerge, + setConfigTags, + setTomlConfig, + build, + ImportConfigTomlInputBuilder, + ImportConfigTomlInput, + workspace_id, + org_id, + mode, + overwrite, + on_error, + dry_run, + value_merge, + config_tags, + toml_config +) where +import qualified Control.Applicative +import qualified Control.Monad.State.Strict +import qualified Data.Aeson +import qualified Data.Either +import qualified Data.Eq +import qualified Data.Functor +import qualified Data.Maybe +import qualified Data.Text +import qualified GHC.Generics +import qualified GHC.Show +import qualified Io.Superposition.Model.ImportMode +import qualified Io.Superposition.Model.ImportOnError +import qualified Io.Superposition.Utility +import qualified Network.HTTP.Types.Method + +data ImportConfigTomlInput = ImportConfigTomlInput { + workspace_id :: Data.Text.Text, + org_id :: Data.Text.Text, + mode :: Data.Maybe.Maybe Io.Superposition.Model.ImportMode.ImportMode, + overwrite :: Data.Maybe.Maybe Bool, + on_error :: Data.Maybe.Maybe Io.Superposition.Model.ImportOnError.ImportOnError, + dry_run :: Data.Maybe.Maybe Bool, + value_merge :: Data.Maybe.Maybe Bool, + config_tags :: Data.Maybe.Maybe Data.Text.Text, + toml_config :: Data.Text.Text +} deriving ( + GHC.Show.Show, + Data.Eq.Eq, + GHC.Generics.Generic + ) + +instance Data.Aeson.ToJSON ImportConfigTomlInput where + toJSON a = Data.Aeson.object [ + "workspace_id" Data.Aeson..= workspace_id a, + "org_id" Data.Aeson..= org_id a, + "mode" Data.Aeson..= mode a, + "overwrite" Data.Aeson..= overwrite a, + "on_error" Data.Aeson..= on_error a, + "dry_run" Data.Aeson..= dry_run a, + "value_merge" Data.Aeson..= value_merge a, + "config_tags" Data.Aeson..= config_tags a, + "toml_config" Data.Aeson..= toml_config a + ] + + +instance Io.Superposition.Utility.SerializeBody ImportConfigTomlInput + +instance Data.Aeson.FromJSON ImportConfigTomlInput where + parseJSON = Data.Aeson.withObject "ImportConfigTomlInput" $ \v -> ImportConfigTomlInput + Data.Functor.<$> (v Data.Aeson..: "workspace_id") + Control.Applicative.<*> (v Data.Aeson..: "org_id") + Control.Applicative.<*> (v Data.Aeson..:? "mode") + Control.Applicative.<*> (v Data.Aeson..:? "overwrite") + Control.Applicative.<*> (v Data.Aeson..:? "on_error") + Control.Applicative.<*> (v Data.Aeson..:? "dry_run") + Control.Applicative.<*> (v Data.Aeson..:? "value_merge") + Control.Applicative.<*> (v Data.Aeson..:? "config_tags") + Control.Applicative.<*> (v Data.Aeson..: "toml_config") + + + + +data ImportConfigTomlInputBuilderState = ImportConfigTomlInputBuilderState { + workspace_idBuilderState :: Data.Maybe.Maybe Data.Text.Text, + org_idBuilderState :: Data.Maybe.Maybe Data.Text.Text, + modeBuilderState :: Data.Maybe.Maybe Io.Superposition.Model.ImportMode.ImportMode, + overwriteBuilderState :: Data.Maybe.Maybe Bool, + on_errorBuilderState :: Data.Maybe.Maybe Io.Superposition.Model.ImportOnError.ImportOnError, + dry_runBuilderState :: Data.Maybe.Maybe Bool, + value_mergeBuilderState :: Data.Maybe.Maybe Bool, + config_tagsBuilderState :: Data.Maybe.Maybe Data.Text.Text, + toml_configBuilderState :: Data.Maybe.Maybe Data.Text.Text +} deriving ( + GHC.Generics.Generic + ) + +defaultBuilderState :: ImportConfigTomlInputBuilderState +defaultBuilderState = ImportConfigTomlInputBuilderState { + workspace_idBuilderState = Data.Maybe.Nothing, + org_idBuilderState = Data.Maybe.Nothing, + modeBuilderState = Data.Maybe.Nothing, + overwriteBuilderState = Data.Maybe.Nothing, + on_errorBuilderState = Data.Maybe.Nothing, + dry_runBuilderState = Data.Maybe.Nothing, + value_mergeBuilderState = Data.Maybe.Nothing, + config_tagsBuilderState = Data.Maybe.Nothing, + toml_configBuilderState = Data.Maybe.Nothing +} + +type ImportConfigTomlInputBuilder = Control.Monad.State.Strict.State ImportConfigTomlInputBuilderState + +setWorkspaceId :: Data.Text.Text -> ImportConfigTomlInputBuilder () +setWorkspaceId value = + Control.Monad.State.Strict.modify (\s -> (s { workspace_idBuilderState = Data.Maybe.Just value })) + +setOrgId :: Data.Text.Text -> ImportConfigTomlInputBuilder () +setOrgId value = + Control.Monad.State.Strict.modify (\s -> (s { org_idBuilderState = Data.Maybe.Just value })) + +setMode :: Data.Maybe.Maybe Io.Superposition.Model.ImportMode.ImportMode -> ImportConfigTomlInputBuilder () +setMode value = + Control.Monad.State.Strict.modify (\s -> (s { modeBuilderState = value })) + +setOverwrite :: Data.Maybe.Maybe Bool -> ImportConfigTomlInputBuilder () +setOverwrite value = + Control.Monad.State.Strict.modify (\s -> (s { overwriteBuilderState = value })) + +setOnError :: Data.Maybe.Maybe Io.Superposition.Model.ImportOnError.ImportOnError -> ImportConfigTomlInputBuilder () +setOnError value = + Control.Monad.State.Strict.modify (\s -> (s { on_errorBuilderState = value })) + +setDryRun :: Data.Maybe.Maybe Bool -> ImportConfigTomlInputBuilder () +setDryRun value = + Control.Monad.State.Strict.modify (\s -> (s { dry_runBuilderState = value })) + +setValueMerge :: Data.Maybe.Maybe Bool -> ImportConfigTomlInputBuilder () +setValueMerge value = + Control.Monad.State.Strict.modify (\s -> (s { value_mergeBuilderState = value })) + +setConfigTags :: Data.Maybe.Maybe Data.Text.Text -> ImportConfigTomlInputBuilder () +setConfigTags value = + Control.Monad.State.Strict.modify (\s -> (s { config_tagsBuilderState = value })) + +setTomlConfig :: Data.Text.Text -> ImportConfigTomlInputBuilder () +setTomlConfig value = + Control.Monad.State.Strict.modify (\s -> (s { toml_configBuilderState = Data.Maybe.Just value })) + +build :: ImportConfigTomlInputBuilder () -> Data.Either.Either Data.Text.Text ImportConfigTomlInput +build builder = do + let (_, st) = Control.Monad.State.Strict.runState builder defaultBuilderState + workspace_id' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigTomlInput.ImportConfigTomlInput.workspace_id is a required property.") Data.Either.Right (workspace_idBuilderState st) + org_id' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigTomlInput.ImportConfigTomlInput.org_id is a required property.") Data.Either.Right (org_idBuilderState st) + mode' <- Data.Either.Right (modeBuilderState st) + overwrite' <- Data.Either.Right (overwriteBuilderState st) + on_error' <- Data.Either.Right (on_errorBuilderState st) + dry_run' <- Data.Either.Right (dry_runBuilderState st) + value_merge' <- Data.Either.Right (value_mergeBuilderState st) + config_tags' <- Data.Either.Right (config_tagsBuilderState st) + toml_config' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigTomlInput.ImportConfigTomlInput.toml_config is a required property.") Data.Either.Right (toml_configBuilderState st) + Data.Either.Right (ImportConfigTomlInput { + workspace_id = workspace_id', + org_id = org_id', + mode = mode', + overwrite = overwrite', + on_error = on_error', + dry_run = dry_run', + value_merge = value_merge', + config_tags = config_tags', + toml_config = toml_config' + }) + + +instance Io.Superposition.Utility.IntoRequestBuilder ImportConfigTomlInput where + intoRequestBuilder self = do + Io.Superposition.Utility.setMethod Network.HTTP.Types.Method.methodPost + Io.Superposition.Utility.setPath [ + "config", + "toml", + "import" + ] + + Io.Superposition.Utility.serHeader "x-workspace" (workspace_id self) + Io.Superposition.Utility.serHeader "x-import-mode" (mode self) + Io.Superposition.Utility.serHeader "x-org-id" (org_id self) + Io.Superposition.Utility.serHeader "x-import-on-error" (on_error self) + Io.Superposition.Utility.serHeader "x-import-dry-run" (dry_run self) + Io.Superposition.Utility.serHeader "x-import-overwrite" (overwrite self) + Io.Superposition.Utility.serHeader "x-import-value-merge" (value_merge self) + Io.Superposition.Utility.serHeader "x-config-tags" (config_tags self) + Io.Superposition.Utility.serBody "text/plain" (toml_config self) + diff --git a/clients/haskell/sdk/Io/Superposition/Model/ImportConfigTomlOutput.hs b/clients/haskell/sdk/Io/Superposition/Model/ImportConfigTomlOutput.hs new file mode 100644 index 000000000..d99c68953 --- /dev/null +++ b/clients/haskell/sdk/Io/Superposition/Model/ImportConfigTomlOutput.hs @@ -0,0 +1,154 @@ +module Io.Superposition.Model.ImportConfigTomlOutput ( + setMode, + setDryRun, + setConfigVersion, + setDimensions, + setDefaultConfigs, + setContexts, + build, + ImportConfigTomlOutputBuilder, + ImportConfigTomlOutput, + mode, + dry_run, + config_version, + dimensions, + default_configs, + contexts +) where +import qualified Control.Applicative +import qualified Control.Monad.State.Strict +import qualified Data.Aeson +import qualified Data.Either +import qualified Data.Eq +import qualified Data.Functor +import qualified Data.Maybe +import qualified Data.Text +import qualified GHC.Generics +import qualified GHC.Show +import qualified Io.Superposition.Model.ImportEntityReport +import qualified Io.Superposition.Utility +import qualified Network.HTTP.Types + +data ImportConfigTomlOutput = ImportConfigTomlOutput { + mode :: Data.Text.Text, + dry_run :: Bool, + config_version :: Data.Maybe.Maybe Data.Text.Text, + dimensions :: Io.Superposition.Model.ImportEntityReport.ImportEntityReport, + default_configs :: Io.Superposition.Model.ImportEntityReport.ImportEntityReport, + contexts :: Io.Superposition.Model.ImportEntityReport.ImportEntityReport +} deriving ( + GHC.Show.Show, + Data.Eq.Eq, + GHC.Generics.Generic + ) + +instance Data.Aeson.ToJSON ImportConfigTomlOutput where + toJSON a = Data.Aeson.object [ + "mode" Data.Aeson..= mode a, + "dry_run" Data.Aeson..= dry_run a, + "config_version" Data.Aeson..= config_version a, + "dimensions" Data.Aeson..= dimensions a, + "default_configs" Data.Aeson..= default_configs a, + "contexts" Data.Aeson..= contexts a + ] + + +instance Io.Superposition.Utility.SerializeBody ImportConfigTomlOutput + +instance Data.Aeson.FromJSON ImportConfigTomlOutput where + parseJSON = Data.Aeson.withObject "ImportConfigTomlOutput" $ \v -> ImportConfigTomlOutput + Data.Functor.<$> (v Data.Aeson..: "mode") + Control.Applicative.<*> (v Data.Aeson..: "dry_run") + Control.Applicative.<*> (v Data.Aeson..:? "config_version") + Control.Applicative.<*> (v Data.Aeson..: "dimensions") + Control.Applicative.<*> (v Data.Aeson..: "default_configs") + Control.Applicative.<*> (v Data.Aeson..: "contexts") + + + + +data ImportConfigTomlOutputBuilderState = ImportConfigTomlOutputBuilderState { + modeBuilderState :: Data.Maybe.Maybe Data.Text.Text, + dry_runBuilderState :: Data.Maybe.Maybe Bool, + config_versionBuilderState :: Data.Maybe.Maybe Data.Text.Text, + dimensionsBuilderState :: Data.Maybe.Maybe Io.Superposition.Model.ImportEntityReport.ImportEntityReport, + default_configsBuilderState :: Data.Maybe.Maybe Io.Superposition.Model.ImportEntityReport.ImportEntityReport, + contextsBuilderState :: Data.Maybe.Maybe Io.Superposition.Model.ImportEntityReport.ImportEntityReport +} deriving ( + GHC.Generics.Generic + ) + +defaultBuilderState :: ImportConfigTomlOutputBuilderState +defaultBuilderState = ImportConfigTomlOutputBuilderState { + modeBuilderState = Data.Maybe.Nothing, + dry_runBuilderState = Data.Maybe.Nothing, + config_versionBuilderState = Data.Maybe.Nothing, + dimensionsBuilderState = Data.Maybe.Nothing, + default_configsBuilderState = Data.Maybe.Nothing, + contextsBuilderState = Data.Maybe.Nothing +} + +type ImportConfigTomlOutputBuilder = Control.Monad.State.Strict.State ImportConfigTomlOutputBuilderState + +setMode :: Data.Text.Text -> ImportConfigTomlOutputBuilder () +setMode value = + Control.Monad.State.Strict.modify (\s -> (s { modeBuilderState = Data.Maybe.Just value })) + +setDryRun :: Bool -> ImportConfigTomlOutputBuilder () +setDryRun value = + Control.Monad.State.Strict.modify (\s -> (s { dry_runBuilderState = Data.Maybe.Just value })) + +setConfigVersion :: Data.Maybe.Maybe Data.Text.Text -> ImportConfigTomlOutputBuilder () +setConfigVersion value = + Control.Monad.State.Strict.modify (\s -> (s { config_versionBuilderState = value })) + +setDimensions :: Io.Superposition.Model.ImportEntityReport.ImportEntityReport -> ImportConfigTomlOutputBuilder () +setDimensions value = + Control.Monad.State.Strict.modify (\s -> (s { dimensionsBuilderState = Data.Maybe.Just value })) + +setDefaultConfigs :: Io.Superposition.Model.ImportEntityReport.ImportEntityReport -> ImportConfigTomlOutputBuilder () +setDefaultConfigs value = + Control.Monad.State.Strict.modify (\s -> (s { default_configsBuilderState = Data.Maybe.Just value })) + +setContexts :: Io.Superposition.Model.ImportEntityReport.ImportEntityReport -> ImportConfigTomlOutputBuilder () +setContexts value = + Control.Monad.State.Strict.modify (\s -> (s { contextsBuilderState = Data.Maybe.Just value })) + +build :: ImportConfigTomlOutputBuilder () -> Data.Either.Either Data.Text.Text ImportConfigTomlOutput +build builder = do + let (_, st) = Control.Monad.State.Strict.runState builder defaultBuilderState + mode' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigTomlOutput.ImportConfigTomlOutput.mode is a required property.") Data.Either.Right (modeBuilderState st) + dry_run' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigTomlOutput.ImportConfigTomlOutput.dry_run is a required property.") Data.Either.Right (dry_runBuilderState st) + config_version' <- Data.Either.Right (config_versionBuilderState st) + dimensions' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigTomlOutput.ImportConfigTomlOutput.dimensions is a required property.") Data.Either.Right (dimensionsBuilderState st) + default_configs' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigTomlOutput.ImportConfigTomlOutput.default_configs is a required property.") Data.Either.Right (default_configsBuilderState st) + contexts' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigTomlOutput.ImportConfigTomlOutput.contexts is a required property.") Data.Either.Right (contextsBuilderState st) + Data.Either.Right (ImportConfigTomlOutput { + mode = mode', + dry_run = dry_run', + config_version = config_version', + dimensions = dimensions', + default_configs = default_configs', + contexts = contexts' + }) + + +instance Io.Superposition.Utility.FromResponseParser ImportConfigTomlOutput where + expectedStatus = (Network.HTTP.Types.mkStatus 200 "") + responseParser = do + + var0 <- Io.Superposition.Utility.deSerField "mode" + var1 <- Io.Superposition.Utility.deSerField "dry_run" + var2 <- Io.Superposition.Utility.deSerField "contexts" + var3 <- Io.Superposition.Utility.deSerField "default_configs" + var4 <- Io.Superposition.Utility.deSerField "config_version" + var5 <- Io.Superposition.Utility.deSerField "dimensions" + pure $ ImportConfigTomlOutput { + mode = var0, + dry_run = var1, + config_version = var4, + dimensions = var5, + default_configs = var3, + contexts = var2 + } + diff --git a/clients/haskell/sdk/Io/Superposition/Model/ImportEntityReport.hs b/clients/haskell/sdk/Io/Superposition/Model/ImportEntityReport.hs new file mode 100644 index 000000000..cfa0737b4 --- /dev/null +++ b/clients/haskell/sdk/Io/Superposition/Model/ImportEntityReport.hs @@ -0,0 +1,122 @@ +module Io.Superposition.Model.ImportEntityReport ( + setCreated, + setUpdated, + setSkipped, + setDeleted, + setErrors, + build, + ImportEntityReportBuilder, + ImportEntityReport, + created, + updated, + skipped, + deleted, + errors +) where +import qualified Control.Applicative +import qualified Control.Monad.State.Strict +import qualified Data.Aeson +import qualified Data.Either +import qualified Data.Eq +import qualified Data.Functor +import qualified Data.Int +import qualified Data.Maybe +import qualified Data.Text +import qualified GHC.Generics +import qualified GHC.Show +import qualified Io.Superposition.Model.ImportErrorItem +import qualified Io.Superposition.Utility + +data ImportEntityReport = ImportEntityReport { + created :: Data.Int.Int32, + updated :: Data.Int.Int32, + skipped :: Data.Int.Int32, + deleted :: Data.Int.Int32, + errors :: Data.Maybe.Maybe ([] Io.Superposition.Model.ImportErrorItem.ImportErrorItem) +} deriving ( + GHC.Show.Show, + Data.Eq.Eq, + GHC.Generics.Generic + ) + +instance Data.Aeson.ToJSON ImportEntityReport where + toJSON a = Data.Aeson.object [ + "created" Data.Aeson..= created a, + "updated" Data.Aeson..= updated a, + "skipped" Data.Aeson..= skipped a, + "deleted" Data.Aeson..= deleted a, + "errors" Data.Aeson..= errors a + ] + + +instance Io.Superposition.Utility.SerializeBody ImportEntityReport + +instance Data.Aeson.FromJSON ImportEntityReport where + parseJSON = Data.Aeson.withObject "ImportEntityReport" $ \v -> ImportEntityReport + Data.Functor.<$> (v Data.Aeson..: "created") + Control.Applicative.<*> (v Data.Aeson..: "updated") + Control.Applicative.<*> (v Data.Aeson..: "skipped") + Control.Applicative.<*> (v Data.Aeson..: "deleted") + Control.Applicative.<*> (v Data.Aeson..:? "errors") + + + + +data ImportEntityReportBuilderState = ImportEntityReportBuilderState { + createdBuilderState :: Data.Maybe.Maybe Data.Int.Int32, + updatedBuilderState :: Data.Maybe.Maybe Data.Int.Int32, + skippedBuilderState :: Data.Maybe.Maybe Data.Int.Int32, + deletedBuilderState :: Data.Maybe.Maybe Data.Int.Int32, + errorsBuilderState :: Data.Maybe.Maybe ([] Io.Superposition.Model.ImportErrorItem.ImportErrorItem) +} deriving ( + GHC.Generics.Generic + ) + +defaultBuilderState :: ImportEntityReportBuilderState +defaultBuilderState = ImportEntityReportBuilderState { + createdBuilderState = Data.Maybe.Nothing, + updatedBuilderState = Data.Maybe.Nothing, + skippedBuilderState = Data.Maybe.Nothing, + deletedBuilderState = Data.Maybe.Nothing, + errorsBuilderState = Data.Maybe.Nothing +} + +type ImportEntityReportBuilder = Control.Monad.State.Strict.State ImportEntityReportBuilderState + +setCreated :: Data.Int.Int32 -> ImportEntityReportBuilder () +setCreated value = + Control.Monad.State.Strict.modify (\s -> (s { createdBuilderState = Data.Maybe.Just value })) + +setUpdated :: Data.Int.Int32 -> ImportEntityReportBuilder () +setUpdated value = + Control.Monad.State.Strict.modify (\s -> (s { updatedBuilderState = Data.Maybe.Just value })) + +setSkipped :: Data.Int.Int32 -> ImportEntityReportBuilder () +setSkipped value = + Control.Monad.State.Strict.modify (\s -> (s { skippedBuilderState = Data.Maybe.Just value })) + +setDeleted :: Data.Int.Int32 -> ImportEntityReportBuilder () +setDeleted value = + Control.Monad.State.Strict.modify (\s -> (s { deletedBuilderState = Data.Maybe.Just value })) + +setErrors :: Data.Maybe.Maybe ([] Io.Superposition.Model.ImportErrorItem.ImportErrorItem) -> ImportEntityReportBuilder () +setErrors value = + Control.Monad.State.Strict.modify (\s -> (s { errorsBuilderState = value })) + +build :: ImportEntityReportBuilder () -> Data.Either.Either Data.Text.Text ImportEntityReport +build builder = do + let (_, st) = Control.Monad.State.Strict.runState builder defaultBuilderState + created' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportEntityReport.ImportEntityReport.created is a required property.") Data.Either.Right (createdBuilderState st) + updated' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportEntityReport.ImportEntityReport.updated is a required property.") Data.Either.Right (updatedBuilderState st) + skipped' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportEntityReport.ImportEntityReport.skipped is a required property.") Data.Either.Right (skippedBuilderState st) + deleted' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportEntityReport.ImportEntityReport.deleted is a required property.") Data.Either.Right (deletedBuilderState st) + errors' <- Data.Either.Right (errorsBuilderState st) + Data.Either.Right (ImportEntityReport { + created = created', + updated = updated', + skipped = skipped', + deleted = deleted', + errors = errors' + }) + + diff --git a/clients/haskell/sdk/Io/Superposition/Model/ImportErrorItem.hs b/clients/haskell/sdk/Io/Superposition/Model/ImportErrorItem.hs new file mode 100644 index 000000000..5e7891a76 --- /dev/null +++ b/clients/haskell/sdk/Io/Superposition/Model/ImportErrorItem.hs @@ -0,0 +1,81 @@ +module Io.Superposition.Model.ImportErrorItem ( + setId', + setError, + build, + ImportErrorItemBuilder, + ImportErrorItem, + id', + error +) where +import qualified Control.Applicative +import qualified Control.Monad.State.Strict +import qualified Data.Aeson +import qualified Data.Either +import qualified Data.Eq +import qualified Data.Functor +import qualified Data.Maybe +import qualified Data.Text +import qualified GHC.Generics +import qualified GHC.Show +import qualified Io.Superposition.Utility + +data ImportErrorItem = ImportErrorItem { + id' :: Data.Text.Text, + error :: Data.Text.Text +} deriving ( + GHC.Show.Show, + Data.Eq.Eq, + GHC.Generics.Generic + ) + +instance Data.Aeson.ToJSON ImportErrorItem where + toJSON a = Data.Aeson.object [ + "id" Data.Aeson..= id' a, + "error" Data.Aeson..= error a + ] + + +instance Io.Superposition.Utility.SerializeBody ImportErrorItem + +instance Data.Aeson.FromJSON ImportErrorItem where + parseJSON = Data.Aeson.withObject "ImportErrorItem" $ \v -> ImportErrorItem + Data.Functor.<$> (v Data.Aeson..: "id") + Control.Applicative.<*> (v Data.Aeson..: "error") + + + + +data ImportErrorItemBuilderState = ImportErrorItemBuilderState { + id'BuilderState :: Data.Maybe.Maybe Data.Text.Text, + errorBuilderState :: Data.Maybe.Maybe Data.Text.Text +} deriving ( + GHC.Generics.Generic + ) + +defaultBuilderState :: ImportErrorItemBuilderState +defaultBuilderState = ImportErrorItemBuilderState { + id'BuilderState = Data.Maybe.Nothing, + errorBuilderState = Data.Maybe.Nothing +} + +type ImportErrorItemBuilder = Control.Monad.State.Strict.State ImportErrorItemBuilderState + +setId' :: Data.Text.Text -> ImportErrorItemBuilder () +setId' value = + Control.Monad.State.Strict.modify (\s -> (s { id'BuilderState = Data.Maybe.Just value })) + +setError :: Data.Text.Text -> ImportErrorItemBuilder () +setError value = + Control.Monad.State.Strict.modify (\s -> (s { errorBuilderState = Data.Maybe.Just value })) + +build :: ImportErrorItemBuilder () -> Data.Either.Either Data.Text.Text ImportErrorItem +build builder = do + let (_, st) = Control.Monad.State.Strict.runState builder defaultBuilderState + id'' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportErrorItem.ImportErrorItem.id' is a required property.") Data.Either.Right (id'BuilderState st) + error' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportErrorItem.ImportErrorItem.error is a required property.") Data.Either.Right (errorBuilderState st) + Data.Either.Right (ImportErrorItem { + id' = id'', + error = error' + }) + + diff --git a/clients/haskell/sdk/Io/Superposition/Model/ImportMode.hs b/clients/haskell/sdk/Io/Superposition/Model/ImportMode.hs new file mode 100644 index 000000000..052ba8d7f --- /dev/null +++ b/clients/haskell/sdk/Io/Superposition/Model/ImportMode.hs @@ -0,0 +1,44 @@ +module Io.Superposition.Model.ImportMode ( + ImportMode(..) +) where +import qualified Data.Aeson +import qualified Data.Eq +import qualified Data.Text +import qualified Data.Text.Encoding +import qualified GHC.Generics +import qualified GHC.Show +import qualified Io.Superposition.Utility + +-- Enum implementation for ImportMode +data ImportMode = + MERGE + | REPLACE + deriving ( + GHC.Generics.Generic, + Data.Eq.Eq, + GHC.Show.Show + ) + +instance Data.Aeson.ToJSON ImportMode where + toJSON MERGE = Data.Aeson.String $ Data.Text.pack "merge" + toJSON REPLACE = Data.Aeson.String $ Data.Text.pack "replace" + +instance Data.Aeson.FromJSON ImportMode where + parseJSON = Data.Aeson.withText "ImportMode" $ \v -> + case v of + "merge" -> pure MERGE + "replace" -> pure REPLACE + _ -> fail $ "Unknown value for ImportMode: " <> Data.Text.unpack v + + + +instance Io.Superposition.Utility.SerDe ImportMode where + serializeElement MERGE = Data.Text.Encoding.encodeUtf8 $ Data.Text.pack "merge" + serializeElement REPLACE = Data.Text.Encoding.encodeUtf8 $ Data.Text.pack "replace" + deSerializeElement bs = case Data.Text.Encoding.decodeUtf8 bs of + "merge" -> Right MERGE + "replace" -> Right REPLACE + e -> Left ("Failed to de-serialize ImportMode, encountered unknown variant: " ++ (show bs)) + + + diff --git a/clients/haskell/sdk/Io/Superposition/Model/ImportOnError.hs b/clients/haskell/sdk/Io/Superposition/Model/ImportOnError.hs new file mode 100644 index 000000000..e367606ec --- /dev/null +++ b/clients/haskell/sdk/Io/Superposition/Model/ImportOnError.hs @@ -0,0 +1,44 @@ +module Io.Superposition.Model.ImportOnError ( + ImportOnError(..) +) where +import qualified Data.Aeson +import qualified Data.Eq +import qualified Data.Text +import qualified Data.Text.Encoding +import qualified GHC.Generics +import qualified GHC.Show +import qualified Io.Superposition.Utility + +-- Enum implementation for ImportOnError +data ImportOnError = + ABORT + | CONTINUE + deriving ( + GHC.Generics.Generic, + Data.Eq.Eq, + GHC.Show.Show + ) + +instance Data.Aeson.ToJSON ImportOnError where + toJSON ABORT = Data.Aeson.String $ Data.Text.pack "abort" + toJSON CONTINUE = Data.Aeson.String $ Data.Text.pack "continue" + +instance Data.Aeson.FromJSON ImportOnError where + parseJSON = Data.Aeson.withText "ImportOnError" $ \v -> + case v of + "abort" -> pure ABORT + "continue" -> pure CONTINUE + _ -> fail $ "Unknown value for ImportOnError: " <> Data.Text.unpack v + + + +instance Io.Superposition.Utility.SerDe ImportOnError where + serializeElement ABORT = Data.Text.Encoding.encodeUtf8 $ Data.Text.pack "abort" + serializeElement CONTINUE = Data.Text.Encoding.encodeUtf8 $ Data.Text.pack "continue" + deSerializeElement bs = case Data.Text.Encoding.decodeUtf8 bs of + "abort" -> Right ABORT + "continue" -> Right CONTINUE + e -> Left ("Failed to de-serialize ImportOnError, encountered unknown variant: " ++ (show bs)) + + + diff --git a/clients/haskell/sdk/SuperpositionSDK.cabal b/clients/haskell/sdk/SuperpositionSDK.cabal index b7af69db9..76af397b5 100644 --- a/clients/haskell/sdk/SuperpositionSDK.cabal +++ b/clients/haskell/sdk/SuperpositionSDK.cabal @@ -53,6 +53,7 @@ library Io.Superposition.Model.GetVersionInput, Io.Superposition.Command.Publish, Io.Superposition.Command.UpdateExperimentGroup, + Io.Superposition.Model.ImportConfigJsonInput, Io.Superposition.Model.BulkOperationInput, Io.Superposition.Model.DeleteSecretOutput, Io.Superposition.Model.WebhookFailed, @@ -70,6 +71,7 @@ library Io.Superposition.Model.DeleteExperimentGroupOutput, Io.Superposition.Model.UpdateTypeTemplatesOutput, Io.Superposition.Model.AuditAction, + Io.Superposition.Command.ImportConfigJson, Io.Superposition.Model.UpdateDimensionInput, Io.Superposition.Model.GetConfigJsonInput, Io.Superposition.Model.GetContextOutput, @@ -149,6 +151,7 @@ library Io.Superposition.Model.UpdateDefaultConfigInput, Io.Superposition.Model.UpdateWorkspaceOutput, Io.Superposition.Model.ValidateContextOutput, + Io.Superposition.Model.ImportEntityReport, Io.Superposition.Model.CreateOrganisationOutput, Io.Superposition.Command.AddMembersToGroup, Io.Superposition.Model.CreateWebhookInput, @@ -165,10 +168,12 @@ library Io.Superposition.Model.GetDimensionInput, Io.Superposition.Model.ListVersionsOutput, Io.Superposition.Model.WeightRecomputeInput, + Io.Superposition.Model.ImportConfigTomlOutput, Io.Superposition.Command.CreateTypeTemplates, Io.Superposition.Model.ContextActionOut, Io.Superposition.Command.ListExperiment, Io.Superposition.Model.UpdateDimensionOutput, + Io.Superposition.Command.ImportConfigToml, Io.Superposition.Model.ListDimensionsOutput, Io.Superposition.Command.UpdateSecret, Io.Superposition.Command.RemoveMembersFromGroup, @@ -212,6 +217,7 @@ library Io.Superposition.Model.ListFunctionInput, Io.Superposition.Model.SortBy, Io.Superposition.Model.ListExperimentGroupsInput, + Io.Superposition.Model.ImportConfigTomlInput, Io.Superposition.Command.UpdateWebhook, Io.Superposition.SuperpositionClient, Io.Superposition.Model.UpdateContextOverrideRequest, @@ -238,6 +244,7 @@ library Io.Superposition.Model.GetConfigTomlOutput, Io.Superposition.Model.GetExperimentGroupInput, Io.Superposition.Command.Test, + Io.Superposition.Model.ImportMode, Io.Superposition.Model.UpdateVariableOutput, Io.Superposition.Model.DeleteContextOutput, Io.Superposition.Command.CreateSecret, @@ -280,6 +287,7 @@ library Io.Superposition.Command.GetVersion, Io.Superposition.Model.ExperimentResponse, Io.Superposition.Model.GetTypeTemplatesListInput, + Io.Superposition.Model.ImportErrorItem, Io.Superposition.Command.ListWorkspace, Io.Superposition.Model.OrganisationResponse, Io.Superposition.Model.DimensionResponse, @@ -294,6 +302,7 @@ library Io.Superposition.Command.UpdateWorkspace, Io.Superposition.Model.GetWebhookByEventInput, Io.Superposition.Model.VariableResponse, + Io.Superposition.Model.ImportOnError, Io.Superposition.Model.CreateDefaultConfigOutput, Io.Superposition.Model.DeleteSecretInput, Io.Superposition.Command.ListVariables, @@ -327,6 +336,7 @@ library Io.Superposition.Model.CreateVariableOutput, Io.Superposition.Model.ResourceNotFound, Io.Superposition.Model.ContextPut, + Io.Superposition.Model.ImportConfigJsonOutput, Io.Superposition.Command.DeleteDimension, Io.Superposition.Command.GetDimension, Io.Superposition.Model.CreateDimensionInput, diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClient.java b/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClient.java index a319ce8f7..eeab27ee4 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClient.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClient.java @@ -101,6 +101,10 @@ import io.juspay.superposition.model.GetWebhookOutput; import io.juspay.superposition.model.GetWorkspaceInput; import io.juspay.superposition.model.GetWorkspaceOutput; +import io.juspay.superposition.model.ImportConfigJsonInput; +import io.juspay.superposition.model.ImportConfigJsonOutput; +import io.juspay.superposition.model.ImportConfigTomlInput; +import io.juspay.superposition.model.ImportConfigTomlOutput; import io.juspay.superposition.model.InternalServerError; import io.juspay.superposition.model.ListAuditLogsInput; import io.juspay.superposition.model.ListAuditLogsOutput; @@ -1158,6 +1162,42 @@ default CompletableFuture getWorkspace(GetWorkspaceInput inp */ CompletableFuture getWorkspace(GetWorkspaceInput input, RequestOverrideConfig overrideConfig); + /** + * Imports a full config from a JSON document, persisting dimensions, default-configs and contexts in a + * single transaction after validating the document. + * + * @throws InternalServerError + */ + default CompletableFuture importConfigJson(ImportConfigJsonInput input) { + return importConfigJson(input, null); + } + + /** + * Imports a full config from a JSON document, persisting dimensions, default-configs and contexts in a + * single transaction after validating the document. + * + * @throws InternalServerError + */ + CompletableFuture importConfigJson(ImportConfigJsonInput input, RequestOverrideConfig overrideConfig); + + /** + * Imports a full config from a TOML document, persisting dimensions, default-configs and contexts in a + * single transaction after validating the document. + * + * @throws InternalServerError + */ + default CompletableFuture importConfigToml(ImportConfigTomlInput input) { + return importConfigToml(input, null); + } + + /** + * Imports a full config from a TOML document, persisting dimensions, default-configs and contexts in a + * single transaction after validating the document. + * + * @throws InternalServerError + */ + CompletableFuture importConfigToml(ImportConfigTomlInput input, RequestOverrideConfig overrideConfig); + /** * Retrieves a paginated list of audit logs with support for filtering by date range, table names, * actions, and usernames for compliance and monitoring purposes. diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClientImpl.java b/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClientImpl.java index abb26734f..30c843425 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClientImpl.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClientImpl.java @@ -151,6 +151,12 @@ import io.juspay.superposition.model.GetWorkspace; import io.juspay.superposition.model.GetWorkspaceInput; import io.juspay.superposition.model.GetWorkspaceOutput; +import io.juspay.superposition.model.ImportConfigJson; +import io.juspay.superposition.model.ImportConfigJsonInput; +import io.juspay.superposition.model.ImportConfigJsonOutput; +import io.juspay.superposition.model.ImportConfigToml; +import io.juspay.superposition.model.ImportConfigTomlInput; +import io.juspay.superposition.model.ImportConfigTomlOutput; import io.juspay.superposition.model.ListAuditLogs; import io.juspay.superposition.model.ListAuditLogsInput; import io.juspay.superposition.model.ListAuditLogsOutput; @@ -491,6 +497,14 @@ final class SuperpositionAsyncClientImpl extends Client implements Superposition public CompletableFuture getWorkspace(GetWorkspaceInput input, RequestOverrideConfig overrideConfig) {return call(input, GetWorkspace.instance(), overrideConfig); } + @Override + public CompletableFuture importConfigJson(ImportConfigJsonInput input, RequestOverrideConfig overrideConfig) {return call(input, ImportConfigJson.instance(), overrideConfig); + } + + @Override + public CompletableFuture importConfigToml(ImportConfigTomlInput input, RequestOverrideConfig overrideConfig) {return call(input, ImportConfigToml.instance(), overrideConfig); + } + @Override public CompletableFuture listAuditLogs(ListAuditLogsInput input, RequestOverrideConfig overrideConfig) {return call(input, ListAuditLogs.instance(), overrideConfig); } @@ -644,4 +658,3 @@ protected TypeRegistry typeRegistry() { return TYPE_REGISTRY; } } - diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClient.java b/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClient.java index da93167db..5a9b1f8fc 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClient.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClient.java @@ -101,6 +101,10 @@ import io.juspay.superposition.model.GetWebhookOutput; import io.juspay.superposition.model.GetWorkspaceInput; import io.juspay.superposition.model.GetWorkspaceOutput; +import io.juspay.superposition.model.ImportConfigJsonInput; +import io.juspay.superposition.model.ImportConfigJsonOutput; +import io.juspay.superposition.model.ImportConfigTomlInput; +import io.juspay.superposition.model.ImportConfigTomlOutput; import io.juspay.superposition.model.InternalServerError; import io.juspay.superposition.model.ListAuditLogsInput; import io.juspay.superposition.model.ListAuditLogsOutput; @@ -1157,6 +1161,42 @@ default GetWorkspaceOutput getWorkspace(GetWorkspaceInput input) { */ GetWorkspaceOutput getWorkspace(GetWorkspaceInput input, RequestOverrideConfig overrideConfig); + /** + * Imports a full config from a JSON document, persisting dimensions, default-configs and contexts in a + * single transaction after validating the document. + * + * @throws InternalServerError + */ + default ImportConfigJsonOutput importConfigJson(ImportConfigJsonInput input) { + return importConfigJson(input, null); + } + + /** + * Imports a full config from a JSON document, persisting dimensions, default-configs and contexts in a + * single transaction after validating the document. + * + * @throws InternalServerError + */ + ImportConfigJsonOutput importConfigJson(ImportConfigJsonInput input, RequestOverrideConfig overrideConfig); + + /** + * Imports a full config from a TOML document, persisting dimensions, default-configs and contexts in a + * single transaction after validating the document. + * + * @throws InternalServerError + */ + default ImportConfigTomlOutput importConfigToml(ImportConfigTomlInput input) { + return importConfigToml(input, null); + } + + /** + * Imports a full config from a TOML document, persisting dimensions, default-configs and contexts in a + * single transaction after validating the document. + * + * @throws InternalServerError + */ + ImportConfigTomlOutput importConfigToml(ImportConfigTomlInput input, RequestOverrideConfig overrideConfig); + /** * Retrieves a paginated list of audit logs with support for filtering by date range, table names, * actions, and usernames for compliance and monitoring purposes. diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClientImpl.java b/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClientImpl.java index 4f2474012..137c5f2d3 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClientImpl.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClientImpl.java @@ -151,6 +151,12 @@ import io.juspay.superposition.model.GetWorkspace; import io.juspay.superposition.model.GetWorkspaceInput; import io.juspay.superposition.model.GetWorkspaceOutput; +import io.juspay.superposition.model.ImportConfigJson; +import io.juspay.superposition.model.ImportConfigJsonInput; +import io.juspay.superposition.model.ImportConfigJsonOutput; +import io.juspay.superposition.model.ImportConfigToml; +import io.juspay.superposition.model.ImportConfigTomlInput; +import io.juspay.superposition.model.ImportConfigTomlOutput; import io.juspay.superposition.model.ListAuditLogs; import io.juspay.superposition.model.ListAuditLogsInput; import io.juspay.superposition.model.ListAuditLogsOutput; @@ -741,6 +747,24 @@ public GetWorkspaceOutput getWorkspace(GetWorkspaceInput input, RequestOverrideC } } + @Override + public ImportConfigJsonOutput importConfigJson(ImportConfigJsonInput input, RequestOverrideConfig overrideConfig) { + try { + return call(input, ImportConfigJson.instance(), overrideConfig).join(); + } catch (CompletionException e) { + throw unwrapAndThrow(e); + } + } + + @Override + public ImportConfigTomlOutput importConfigToml(ImportConfigTomlInput input, RequestOverrideConfig overrideConfig) { + try { + return call(input, ImportConfigToml.instance(), overrideConfig).join(); + } catch (CompletionException e) { + throw unwrapAndThrow(e); + } + } + @Override public ListAuditLogsOutput listAuditLogs(ListAuditLogsInput input, RequestOverrideConfig overrideConfig) { try { @@ -1079,4 +1103,3 @@ protected TypeRegistry typeRegistry() { return TYPE_REGISTRY; } } - diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigJson.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigJson.java new file mode 100644 index 000000000..c62688fb9 --- /dev/null +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigJson.java @@ -0,0 +1,94 @@ + +package io.juspay.superposition.model; + +import java.util.List; +import software.amazon.smithy.java.core.schema.ApiOperation; +import software.amazon.smithy.java.core.schema.Schema; +import software.amazon.smithy.java.core.schema.ShapeBuilder; +import software.amazon.smithy.java.core.serde.TypeRegistry; +import software.amazon.smithy.model.pattern.UriPattern; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.HttpTrait; +import software.amazon.smithy.utils.SmithyGenerated; + +/** + * Imports a full config from a JSON document, persisting dimensions, default-configs and contexts in a + * single transaction after validating the document. + */ +@SmithyGenerated +public final class ImportConfigJson implements ApiOperation { + public static final ShapeId $ID = ShapeId.from("io.superposition#ImportConfigJson"); + + private static final ImportConfigJson $INSTANCE = new ImportConfigJson(); + + static final Schema $SCHEMA = Schema.createOperation($ID, + HttpTrait.builder().method("POST").code(200).uri(UriPattern.parse("/config/json/import")).build()); + + private static final TypeRegistry TYPE_REGISTRY = TypeRegistry.builder() + .putType(InternalServerError.$ID, InternalServerError.class, InternalServerError::builder) + .build(); + + private static final List SCHEMES = List.of(ShapeId.from("smithy.api#httpBasicAuth"), ShapeId.from("smithy.api#httpBearerAuth")); + + /** + * Get an instance of this {@code ApiOperation}. + * + * @return An instance of this class. + */ + public static ImportConfigJson instance() { + return $INSTANCE; + } + + private ImportConfigJson() {} + + @Override + public ShapeBuilder inputBuilder() { + return ImportConfigJsonInput.builder(); + } + + @Override + public ShapeBuilder outputBuilder() { + return ImportConfigJsonOutput.builder(); + } + + @Override + public Schema schema() { + return $SCHEMA; + } + + @Override + public Schema inputSchema() { + return ImportConfigJsonInput.$SCHEMA; + } + + @Override + public Schema outputSchema() { + return ImportConfigJsonOutput.$SCHEMA; + } + + @Override + public TypeRegistry errorRegistry() { + return TYPE_REGISTRY; + } + + @Override + public List effectiveAuthSchemes() { + return SCHEMES; + } + + @Override + public Schema inputStreamMember() { + return null; + } + + @Override + public Schema outputStreamMember() { + return null; + } + + @Override + public Schema idempotencyTokenMember() { + return null; + } +} + diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigJsonInput.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigJsonInput.java new file mode 100644 index 000000000..a72a68daa --- /dev/null +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigJsonInput.java @@ -0,0 +1,419 @@ + +package io.juspay.superposition.model; + +import java.util.Objects; +import software.amazon.smithy.java.core.schema.PreludeSchemas; +import software.amazon.smithy.java.core.schema.PresenceTracker; +import software.amazon.smithy.java.core.schema.Schema; +import software.amazon.smithy.java.core.schema.SchemaUtils; +import software.amazon.smithy.java.core.schema.SerializableStruct; +import software.amazon.smithy.java.core.schema.ShapeBuilder; +import software.amazon.smithy.java.core.serde.ShapeDeserializer; +import software.amazon.smithy.java.core.serde.ShapeSerializer; +import software.amazon.smithy.java.core.serde.ToStringSerializer; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.HttpHeaderTrait; +import software.amazon.smithy.model.traits.HttpPayloadTrait; +import software.amazon.smithy.model.traits.RequiredTrait; +import software.amazon.smithy.utils.SmithyGenerated; + +@SmithyGenerated +public final class ImportConfigJsonInput implements SerializableStruct { + public static final ShapeId $ID = ShapeId.from("io.superposition#ImportConfigJsonInput"); + + public static final Schema $SCHEMA = Schema.structureBuilder($ID) + .putMember("workspace_id", PreludeSchemas.STRING, + new HttpHeaderTrait("x-workspace"), + new RequiredTrait()) + .putMember("org_id", PreludeSchemas.STRING, + new HttpHeaderTrait("x-org-id"), + new RequiredTrait()) + .putMember("mode", ImportMode.$SCHEMA, + new HttpHeaderTrait("x-import-mode")) + .putMember("overwrite", PreludeSchemas.BOOLEAN, + new HttpHeaderTrait("x-import-overwrite")) + .putMember("on_error", ImportOnError.$SCHEMA, + new HttpHeaderTrait("x-import-on-error")) + .putMember("dry_run", PreludeSchemas.BOOLEAN, + new HttpHeaderTrait("x-import-dry-run")) + .putMember("value_merge", PreludeSchemas.BOOLEAN, + new HttpHeaderTrait("x-import-value-merge")) + .putMember("config_tags", PreludeSchemas.STRING, + new HttpHeaderTrait("x-config-tags")) + .putMember("json_config", PreludeSchemas.STRING, + new RequiredTrait(), + new HttpPayloadTrait()) + .build(); + + private static final Schema $SCHEMA_WORKSPACE_ID = $SCHEMA.member("workspace_id"); + private static final Schema $SCHEMA_ORG_ID = $SCHEMA.member("org_id"); + private static final Schema $SCHEMA_MODE = $SCHEMA.member("mode"); + private static final Schema $SCHEMA_OVERWRITE = $SCHEMA.member("overwrite"); + private static final Schema $SCHEMA_ON_ERROR = $SCHEMA.member("on_error"); + private static final Schema $SCHEMA_DRY_RUN = $SCHEMA.member("dry_run"); + private static final Schema $SCHEMA_VALUE_MERGE = $SCHEMA.member("value_merge"); + private static final Schema $SCHEMA_CONFIG_TAGS = $SCHEMA.member("config_tags"); + private static final Schema $SCHEMA_JSON_CONFIG = $SCHEMA.member("json_config"); + + private final transient String workspaceId; + private final transient String orgId; + private final transient ImportMode mode; + private final transient Boolean overwrite; + private final transient ImportOnError onError; + private final transient Boolean dryRun; + private final transient Boolean valueMerge; + private final transient String configTags; + private final transient String jsonConfig; + + private ImportConfigJsonInput(Builder builder) { + this.workspaceId = builder.workspaceId; + this.orgId = builder.orgId; + this.mode = builder.mode; + this.overwrite = builder.overwrite; + this.onError = builder.onError; + this.dryRun = builder.dryRun; + this.valueMerge = builder.valueMerge; + this.configTags = builder.configTags; + this.jsonConfig = builder.jsonConfig; + } + + public String workspaceId() { + return workspaceId; + } + + public String orgId() { + return orgId; + } + + /** + * Whether to merge (default) or replace existing workspace config. + */ + public ImportMode mode() { + return mode; + } + + /** + * When false, entities that already exist are skipped instead of updated. Defaults to true. + */ + public Boolean overwrite() { + return overwrite; + } + + /** + * Whether to abort (default) or continue on per-entity errors. + */ + public ImportOnError onError() { + return onError; + } + + /** + * When true, validates and summarises the import without persisting anything. Defaults to false. + */ + public Boolean dryRun() { + return dryRun; + } + + /** + * When true, deep-merges object-valued default-configs with the existing value. Defaults to false. + */ + public Boolean valueMerge() { + return valueMerge; + } + + public String configTags() { + return configTags; + } + + public String jsonConfig() { + return jsonConfig; + } + + @Override + public String toString() { + return ToStringSerializer.serialize(this); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + ImportConfigJsonInput that = (ImportConfigJsonInput) other; + return Objects.equals(this.workspaceId, that.workspaceId) + && Objects.equals(this.orgId, that.orgId) + && Objects.equals(this.mode, that.mode) + && Objects.equals(this.overwrite, that.overwrite) + && Objects.equals(this.onError, that.onError) + && Objects.equals(this.dryRun, that.dryRun) + && Objects.equals(this.valueMerge, that.valueMerge) + && Objects.equals(this.configTags, that.configTags) + && Objects.equals(this.jsonConfig, that.jsonConfig); + } + + @Override + public int hashCode() { + return Objects.hash(workspaceId, orgId, mode, overwrite, onError, dryRun, valueMerge, configTags, jsonConfig); + } + + @Override + public Schema schema() { + return $SCHEMA; + } + + @Override + public void serializeMembers(ShapeSerializer serializer) { + serializer.writeString($SCHEMA_WORKSPACE_ID, workspaceId); + serializer.writeString($SCHEMA_ORG_ID, orgId); + if (mode != null) { + serializer.writeString($SCHEMA_MODE, mode.value()); + } + if (overwrite != null) { + serializer.writeBoolean($SCHEMA_OVERWRITE, overwrite); + } + if (onError != null) { + serializer.writeString($SCHEMA_ON_ERROR, onError.value()); + } + if (dryRun != null) { + serializer.writeBoolean($SCHEMA_DRY_RUN, dryRun); + } + if (valueMerge != null) { + serializer.writeBoolean($SCHEMA_VALUE_MERGE, valueMerge); + } + if (configTags != null) { + serializer.writeString($SCHEMA_CONFIG_TAGS, configTags); + } + serializer.writeString($SCHEMA_JSON_CONFIG, jsonConfig); + } + + @Override + @SuppressWarnings("unchecked") + public T getMemberValue(Schema member) { + return switch (member.memberIndex()) { + case 0 -> (T) SchemaUtils.validateSameMember($SCHEMA_WORKSPACE_ID, member, workspaceId); + case 1 -> (T) SchemaUtils.validateSameMember($SCHEMA_ORG_ID, member, orgId); + case 2 -> (T) SchemaUtils.validateSameMember($SCHEMA_JSON_CONFIG, member, jsonConfig); + case 3 -> (T) SchemaUtils.validateSameMember($SCHEMA_MODE, member, mode); + case 4 -> (T) SchemaUtils.validateSameMember($SCHEMA_OVERWRITE, member, overwrite); + case 5 -> (T) SchemaUtils.validateSameMember($SCHEMA_ON_ERROR, member, onError); + case 6 -> (T) SchemaUtils.validateSameMember($SCHEMA_DRY_RUN, member, dryRun); + case 7 -> (T) SchemaUtils.validateSameMember($SCHEMA_VALUE_MERGE, member, valueMerge); + case 8 -> (T) SchemaUtils.validateSameMember($SCHEMA_CONFIG_TAGS, member, configTags); + default -> throw new IllegalArgumentException("Attempted to get non-existent member: " + member.id()); + }; + } + + /** + * Create a new builder containing all the current property values of this object. + * + *

Note: This method performs only a shallow copy of the original properties. + * + * @return a builder for {@link ImportConfigJsonInput}. + */ + public Builder toBuilder() { + var builder = new Builder(); + builder.workspaceId(this.workspaceId); + builder.orgId(this.orgId); + builder.mode(this.mode); + builder.overwrite(this.overwrite); + builder.onError(this.onError); + builder.dryRun(this.dryRun); + builder.valueMerge(this.valueMerge); + builder.configTags(this.configTags); + builder.jsonConfig(this.jsonConfig); + return builder; + } + + /** + * @return returns a new Builder. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder for {@link ImportConfigJsonInput}. + */ + public static final class Builder implements ShapeBuilder { + private final PresenceTracker tracker = PresenceTracker.of($SCHEMA); + private String workspaceId; + private String orgId; + private ImportMode mode; + private Boolean overwrite; + private ImportOnError onError; + private Boolean dryRun; + private Boolean valueMerge; + private String configTags; + private String jsonConfig; + + private Builder() {} + + @Override + public Schema schema() { + return $SCHEMA; + } + + /** + *

Required + * @return this builder. + */ + public Builder workspaceId(String workspaceId) { + this.workspaceId = Objects.requireNonNull(workspaceId, "workspaceId cannot be null"); + tracker.setMember($SCHEMA_WORKSPACE_ID); + return this; + } + + /** + *

Required + * @return this builder. + */ + public Builder orgId(String orgId) { + this.orgId = Objects.requireNonNull(orgId, "orgId cannot be null"); + tracker.setMember($SCHEMA_ORG_ID); + return this; + } + + /** + * Whether to merge (default) or replace existing workspace config. + * + * @return this builder. + */ + public Builder mode(ImportMode mode) { + this.mode = mode; + return this; + } + + /** + * When false, entities that already exist are skipped instead of updated. Defaults to true. + * + * @return this builder. + */ + public Builder overwrite(boolean overwrite) { + this.overwrite = overwrite; + return this; + } + + /** + * Whether to abort (default) or continue on per-entity errors. + * + * @return this builder. + */ + public Builder onError(ImportOnError onError) { + this.onError = onError; + return this; + } + + /** + * When true, validates and summarises the import without persisting anything. Defaults to false. + * + * @return this builder. + */ + public Builder dryRun(boolean dryRun) { + this.dryRun = dryRun; + return this; + } + + /** + * When true, deep-merges object-valued default-configs with the existing value. Defaults to false. + * + * @return this builder. + */ + public Builder valueMerge(boolean valueMerge) { + this.valueMerge = valueMerge; + return this; + } + + /** + * @return this builder. + */ + public Builder configTags(String configTags) { + this.configTags = configTags; + return this; + } + + /** + *

Required + * @return this builder. + */ + public Builder jsonConfig(String jsonConfig) { + this.jsonConfig = Objects.requireNonNull(jsonConfig, "jsonConfig cannot be null"); + tracker.setMember($SCHEMA_JSON_CONFIG); + return this; + } + + @Override + public ImportConfigJsonInput build() { + tracker.validate(); + return new ImportConfigJsonInput(this); + } + + @Override + @SuppressWarnings("unchecked") + public void setMemberValue(Schema member, Object value) { + switch (member.memberIndex()) { + case 0 -> workspaceId((String) SchemaUtils.validateSameMember($SCHEMA_WORKSPACE_ID, member, value)); + case 1 -> orgId((String) SchemaUtils.validateSameMember($SCHEMA_ORG_ID, member, value)); + case 2 -> jsonConfig((String) SchemaUtils.validateSameMember($SCHEMA_JSON_CONFIG, member, value)); + case 3 -> mode((ImportMode) SchemaUtils.validateSameMember($SCHEMA_MODE, member, value)); + case 4 -> overwrite((boolean) SchemaUtils.validateSameMember($SCHEMA_OVERWRITE, member, value)); + case 5 -> onError((ImportOnError) SchemaUtils.validateSameMember($SCHEMA_ON_ERROR, member, value)); + case 6 -> dryRun((boolean) SchemaUtils.validateSameMember($SCHEMA_DRY_RUN, member, value)); + case 7 -> valueMerge((boolean) SchemaUtils.validateSameMember($SCHEMA_VALUE_MERGE, member, value)); + case 8 -> configTags((String) SchemaUtils.validateSameMember($SCHEMA_CONFIG_TAGS, member, value)); + default -> ShapeBuilder.super.setMemberValue(member, value); + } + } + + @Override + public ShapeBuilder errorCorrection() { + if (tracker.allSet()) { + return this; + } + if (!tracker.checkMember($SCHEMA_WORKSPACE_ID)) { + workspaceId(""); + } + if (!tracker.checkMember($SCHEMA_ORG_ID)) { + orgId(""); + } + if (!tracker.checkMember($SCHEMA_JSON_CONFIG)) { + jsonConfig(""); + } + return this; + } + + @Override + public Builder deserialize(ShapeDeserializer decoder) { + decoder.readStruct($SCHEMA, this, $InnerDeserializer.INSTANCE); + return this; + } + + @Override + public Builder deserializeMember(ShapeDeserializer decoder, Schema schema) { + decoder.readStruct(schema.assertMemberTargetIs($SCHEMA), this, $InnerDeserializer.INSTANCE); + return this; + } + + private static final class $InnerDeserializer implements ShapeDeserializer.StructMemberConsumer { + private static final $InnerDeserializer INSTANCE = new $InnerDeserializer(); + + @Override + public void accept(Builder builder, Schema member, ShapeDeserializer de) { + switch (member.memberIndex()) { + case 0 -> builder.workspaceId(de.readString(member)); + case 1 -> builder.orgId(de.readString(member)); + case 2 -> builder.jsonConfig(de.readString(member)); + case 3 -> builder.mode(ImportMode.builder().deserializeMember(de, member).build()); + case 4 -> builder.overwrite(de.readBoolean(member)); + case 5 -> builder.onError(ImportOnError.builder().deserializeMember(de, member).build()); + case 6 -> builder.dryRun(de.readBoolean(member)); + case 7 -> builder.valueMerge(de.readBoolean(member)); + case 8 -> builder.configTags(de.readString(member)); + default -> throw new IllegalArgumentException("Unexpected member: " + member.memberName()); + } + } + } + } +} + diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigJsonOutput.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigJsonOutput.java new file mode 100644 index 000000000..c2bf372b4 --- /dev/null +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigJsonOutput.java @@ -0,0 +1,325 @@ + +package io.juspay.superposition.model; + +import java.util.Objects; +import software.amazon.smithy.java.core.schema.PreludeSchemas; +import software.amazon.smithy.java.core.schema.PresenceTracker; +import software.amazon.smithy.java.core.schema.Schema; +import software.amazon.smithy.java.core.schema.SchemaUtils; +import software.amazon.smithy.java.core.schema.SerializableStruct; +import software.amazon.smithy.java.core.schema.ShapeBuilder; +import software.amazon.smithy.java.core.serde.ShapeDeserializer; +import software.amazon.smithy.java.core.serde.ShapeSerializer; +import software.amazon.smithy.java.core.serde.ToStringSerializer; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.RequiredTrait; +import software.amazon.smithy.utils.SmithyGenerated; + +/** + * Summary of what an import created, updated, skipped or deleted. + */ +@SmithyGenerated +public final class ImportConfigJsonOutput implements SerializableStruct { + public static final ShapeId $ID = ShapeId.from("io.superposition#ImportConfigOutput"); + + public static final Schema $SCHEMA = Schema.structureBuilder($ID) + .putMember("mode", PreludeSchemas.STRING, + new RequiredTrait()) + .putMember("dry_run", PreludeSchemas.BOOLEAN, + new RequiredTrait()) + .putMember("config_version", PreludeSchemas.STRING) + .putMember("dimensions", ImportEntityReport.$SCHEMA, + new RequiredTrait()) + .putMember("default_configs", ImportEntityReport.$SCHEMA, + new RequiredTrait()) + .putMember("contexts", ImportEntityReport.$SCHEMA, + new RequiredTrait()) + .build(); + + private static final Schema $SCHEMA_MODE = $SCHEMA.member("mode"); + private static final Schema $SCHEMA_DRY_RUN = $SCHEMA.member("dry_run"); + private static final Schema $SCHEMA_CONFIG_VERSION = $SCHEMA.member("config_version"); + private static final Schema $SCHEMA_DIMENSIONS = $SCHEMA.member("dimensions"); + private static final Schema $SCHEMA_DEFAULT_CONFIGS = $SCHEMA.member("default_configs"); + private static final Schema $SCHEMA_CONTEXTS = $SCHEMA.member("contexts"); + + private final transient String mode; + private final transient boolean dryRun; + private final transient String configVersion; + private final transient ImportEntityReport dimensions; + private final transient ImportEntityReport defaultConfigs; + private final transient ImportEntityReport contexts; + + private ImportConfigJsonOutput(Builder builder) { + this.mode = builder.mode; + this.dryRun = builder.dryRun; + this.configVersion = builder.configVersion; + this.dimensions = builder.dimensions; + this.defaultConfigs = builder.defaultConfigs; + this.contexts = builder.contexts; + } + + public String mode() { + return mode; + } + + public boolean dryRun() { + return dryRun; + } + + public String configVersion() { + return configVersion; + } + + public ImportEntityReport dimensions() { + return dimensions; + } + + public ImportEntityReport defaultConfigs() { + return defaultConfigs; + } + + public ImportEntityReport contexts() { + return contexts; + } + + @Override + public String toString() { + return ToStringSerializer.serialize(this); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + ImportConfigJsonOutput that = (ImportConfigJsonOutput) other; + return Objects.equals(this.mode, that.mode) + && this.dryRun == that.dryRun + && Objects.equals(this.configVersion, that.configVersion) + && Objects.equals(this.dimensions, that.dimensions) + && Objects.equals(this.defaultConfigs, that.defaultConfigs) + && Objects.equals(this.contexts, that.contexts); + } + + @Override + public int hashCode() { + return Objects.hash(mode, dryRun, configVersion, dimensions, defaultConfigs, contexts); + } + + @Override + public Schema schema() { + return $SCHEMA; + } + + @Override + public void serializeMembers(ShapeSerializer serializer) { + serializer.writeString($SCHEMA_MODE, mode); + serializer.writeBoolean($SCHEMA_DRY_RUN, dryRun); + if (configVersion != null) { + serializer.writeString($SCHEMA_CONFIG_VERSION, configVersion); + } + if (dimensions != null) { + serializer.writeStruct($SCHEMA_DIMENSIONS, dimensions); + } + if (defaultConfigs != null) { + serializer.writeStruct($SCHEMA_DEFAULT_CONFIGS, defaultConfigs); + } + if (contexts != null) { + serializer.writeStruct($SCHEMA_CONTEXTS, contexts); + } + } + + @Override + @SuppressWarnings("unchecked") + public T getMemberValue(Schema member) { + return switch (member.memberIndex()) { + case 0 -> (T) SchemaUtils.validateSameMember($SCHEMA_MODE, member, mode); + case 1 -> (T) SchemaUtils.validateSameMember($SCHEMA_DRY_RUN, member, dryRun); + case 2 -> (T) SchemaUtils.validateSameMember($SCHEMA_DIMENSIONS, member, dimensions); + case 3 -> (T) SchemaUtils.validateSameMember($SCHEMA_DEFAULT_CONFIGS, member, defaultConfigs); + case 4 -> (T) SchemaUtils.validateSameMember($SCHEMA_CONTEXTS, member, contexts); + case 5 -> (T) SchemaUtils.validateSameMember($SCHEMA_CONFIG_VERSION, member, configVersion); + default -> throw new IllegalArgumentException("Attempted to get non-existent member: " + member.id()); + }; + } + + /** + * Create a new builder containing all the current property values of this object. + * + *

Note: This method performs only a shallow copy of the original properties. + * + * @return a builder for {@link ImportConfigJsonOutput}. + */ + public Builder toBuilder() { + var builder = new Builder(); + builder.mode(this.mode); + builder.dryRun(this.dryRun); + builder.configVersion(this.configVersion); + builder.dimensions(this.dimensions); + builder.defaultConfigs(this.defaultConfigs); + builder.contexts(this.contexts); + return builder; + } + + /** + * @return returns a new Builder. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder for {@link ImportConfigJsonOutput}. + */ + public static final class Builder implements ShapeBuilder { + private final PresenceTracker tracker = PresenceTracker.of($SCHEMA); + private String mode; + private boolean dryRun; + private String configVersion; + private ImportEntityReport dimensions; + private ImportEntityReport defaultConfigs; + private ImportEntityReport contexts; + + private Builder() {} + + @Override + public Schema schema() { + return $SCHEMA; + } + + /** + *

Required + * @return this builder. + */ + public Builder mode(String mode) { + this.mode = Objects.requireNonNull(mode, "mode cannot be null"); + tracker.setMember($SCHEMA_MODE); + return this; + } + + /** + *

Required + * @return this builder. + */ + public Builder dryRun(boolean dryRun) { + this.dryRun = dryRun; + tracker.setMember($SCHEMA_DRY_RUN); + return this; + } + + /** + * @return this builder. + */ + public Builder configVersion(String configVersion) { + this.configVersion = configVersion; + return this; + } + + /** + *

Required + * @return this builder. + */ + public Builder dimensions(ImportEntityReport dimensions) { + this.dimensions = Objects.requireNonNull(dimensions, "dimensions cannot be null"); + tracker.setMember($SCHEMA_DIMENSIONS); + return this; + } + + /** + *

Required + * @return this builder. + */ + public Builder defaultConfigs(ImportEntityReport defaultConfigs) { + this.defaultConfigs = Objects.requireNonNull(defaultConfigs, "defaultConfigs cannot be null"); + tracker.setMember($SCHEMA_DEFAULT_CONFIGS); + return this; + } + + /** + *

Required + * @return this builder. + */ + public Builder contexts(ImportEntityReport contexts) { + this.contexts = Objects.requireNonNull(contexts, "contexts cannot be null"); + tracker.setMember($SCHEMA_CONTEXTS); + return this; + } + + @Override + public ImportConfigJsonOutput build() { + tracker.validate(); + return new ImportConfigJsonOutput(this); + } + + @Override + @SuppressWarnings("unchecked") + public void setMemberValue(Schema member, Object value) { + switch (member.memberIndex()) { + case 0 -> mode((String) SchemaUtils.validateSameMember($SCHEMA_MODE, member, value)); + case 1 -> dryRun((boolean) SchemaUtils.validateSameMember($SCHEMA_DRY_RUN, member, value)); + case 2 -> dimensions((ImportEntityReport) SchemaUtils.validateSameMember($SCHEMA_DIMENSIONS, member, value)); + case 3 -> defaultConfigs((ImportEntityReport) SchemaUtils.validateSameMember($SCHEMA_DEFAULT_CONFIGS, member, value)); + case 4 -> contexts((ImportEntityReport) SchemaUtils.validateSameMember($SCHEMA_CONTEXTS, member, value)); + case 5 -> configVersion((String) SchemaUtils.validateSameMember($SCHEMA_CONFIG_VERSION, member, value)); + default -> ShapeBuilder.super.setMemberValue(member, value); + } + } + + @Override + public ShapeBuilder errorCorrection() { + if (tracker.allSet()) { + return this; + } + if (!tracker.checkMember($SCHEMA_MODE)) { + mode(""); + } + if (!tracker.checkMember($SCHEMA_DRY_RUN)) { + tracker.setMember($SCHEMA_DRY_RUN); + } + if (!tracker.checkMember($SCHEMA_DIMENSIONS)) { + tracker.setMember($SCHEMA_DIMENSIONS); + } + if (!tracker.checkMember($SCHEMA_DEFAULT_CONFIGS)) { + tracker.setMember($SCHEMA_DEFAULT_CONFIGS); + } + if (!tracker.checkMember($SCHEMA_CONTEXTS)) { + tracker.setMember($SCHEMA_CONTEXTS); + } + return this; + } + + @Override + public Builder deserialize(ShapeDeserializer decoder) { + decoder.readStruct($SCHEMA, this, $InnerDeserializer.INSTANCE); + return this; + } + + @Override + public Builder deserializeMember(ShapeDeserializer decoder, Schema schema) { + decoder.readStruct(schema.assertMemberTargetIs($SCHEMA), this, $InnerDeserializer.INSTANCE); + return this; + } + + private static final class $InnerDeserializer implements ShapeDeserializer.StructMemberConsumer { + private static final $InnerDeserializer INSTANCE = new $InnerDeserializer(); + + @Override + public void accept(Builder builder, Schema member, ShapeDeserializer de) { + switch (member.memberIndex()) { + case 0 -> builder.mode(de.readString(member)); + case 1 -> builder.dryRun(de.readBoolean(member)); + case 2 -> builder.dimensions(ImportEntityReport.builder().deserializeMember(de, member).build()); + case 3 -> builder.defaultConfigs(ImportEntityReport.builder().deserializeMember(de, member).build()); + case 4 -> builder.contexts(ImportEntityReport.builder().deserializeMember(de, member).build()); + case 5 -> builder.configVersion(de.readString(member)); + default -> throw new IllegalArgumentException("Unexpected member: " + member.memberName()); + } + } + } + } +} + diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigToml.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigToml.java new file mode 100644 index 000000000..85d00fa9a --- /dev/null +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigToml.java @@ -0,0 +1,94 @@ + +package io.juspay.superposition.model; + +import java.util.List; +import software.amazon.smithy.java.core.schema.ApiOperation; +import software.amazon.smithy.java.core.schema.Schema; +import software.amazon.smithy.java.core.schema.ShapeBuilder; +import software.amazon.smithy.java.core.serde.TypeRegistry; +import software.amazon.smithy.model.pattern.UriPattern; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.HttpTrait; +import software.amazon.smithy.utils.SmithyGenerated; + +/** + * Imports a full config from a TOML document, persisting dimensions, default-configs and contexts in a + * single transaction after validating the document. + */ +@SmithyGenerated +public final class ImportConfigToml implements ApiOperation { + public static final ShapeId $ID = ShapeId.from("io.superposition#ImportConfigToml"); + + private static final ImportConfigToml $INSTANCE = new ImportConfigToml(); + + static final Schema $SCHEMA = Schema.createOperation($ID, + HttpTrait.builder().method("POST").code(200).uri(UriPattern.parse("/config/toml/import")).build()); + + private static final TypeRegistry TYPE_REGISTRY = TypeRegistry.builder() + .putType(InternalServerError.$ID, InternalServerError.class, InternalServerError::builder) + .build(); + + private static final List SCHEMES = List.of(ShapeId.from("smithy.api#httpBasicAuth"), ShapeId.from("smithy.api#httpBearerAuth")); + + /** + * Get an instance of this {@code ApiOperation}. + * + * @return An instance of this class. + */ + public static ImportConfigToml instance() { + return $INSTANCE; + } + + private ImportConfigToml() {} + + @Override + public ShapeBuilder inputBuilder() { + return ImportConfigTomlInput.builder(); + } + + @Override + public ShapeBuilder outputBuilder() { + return ImportConfigTomlOutput.builder(); + } + + @Override + public Schema schema() { + return $SCHEMA; + } + + @Override + public Schema inputSchema() { + return ImportConfigTomlInput.$SCHEMA; + } + + @Override + public Schema outputSchema() { + return ImportConfigTomlOutput.$SCHEMA; + } + + @Override + public TypeRegistry errorRegistry() { + return TYPE_REGISTRY; + } + + @Override + public List effectiveAuthSchemes() { + return SCHEMES; + } + + @Override + public Schema inputStreamMember() { + return null; + } + + @Override + public Schema outputStreamMember() { + return null; + } + + @Override + public Schema idempotencyTokenMember() { + return null; + } +} + diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigTomlInput.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigTomlInput.java new file mode 100644 index 000000000..a7bbd561d --- /dev/null +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigTomlInput.java @@ -0,0 +1,419 @@ + +package io.juspay.superposition.model; + +import java.util.Objects; +import software.amazon.smithy.java.core.schema.PreludeSchemas; +import software.amazon.smithy.java.core.schema.PresenceTracker; +import software.amazon.smithy.java.core.schema.Schema; +import software.amazon.smithy.java.core.schema.SchemaUtils; +import software.amazon.smithy.java.core.schema.SerializableStruct; +import software.amazon.smithy.java.core.schema.ShapeBuilder; +import software.amazon.smithy.java.core.serde.ShapeDeserializer; +import software.amazon.smithy.java.core.serde.ShapeSerializer; +import software.amazon.smithy.java.core.serde.ToStringSerializer; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.HttpHeaderTrait; +import software.amazon.smithy.model.traits.HttpPayloadTrait; +import software.amazon.smithy.model.traits.RequiredTrait; +import software.amazon.smithy.utils.SmithyGenerated; + +@SmithyGenerated +public final class ImportConfigTomlInput implements SerializableStruct { + public static final ShapeId $ID = ShapeId.from("io.superposition#ImportConfigTomlInput"); + + public static final Schema $SCHEMA = Schema.structureBuilder($ID) + .putMember("workspace_id", PreludeSchemas.STRING, + new HttpHeaderTrait("x-workspace"), + new RequiredTrait()) + .putMember("org_id", PreludeSchemas.STRING, + new HttpHeaderTrait("x-org-id"), + new RequiredTrait()) + .putMember("mode", ImportMode.$SCHEMA, + new HttpHeaderTrait("x-import-mode")) + .putMember("overwrite", PreludeSchemas.BOOLEAN, + new HttpHeaderTrait("x-import-overwrite")) + .putMember("on_error", ImportOnError.$SCHEMA, + new HttpHeaderTrait("x-import-on-error")) + .putMember("dry_run", PreludeSchemas.BOOLEAN, + new HttpHeaderTrait("x-import-dry-run")) + .putMember("value_merge", PreludeSchemas.BOOLEAN, + new HttpHeaderTrait("x-import-value-merge")) + .putMember("config_tags", PreludeSchemas.STRING, + new HttpHeaderTrait("x-config-tags")) + .putMember("toml_config", PreludeSchemas.STRING, + new RequiredTrait(), + new HttpPayloadTrait()) + .build(); + + private static final Schema $SCHEMA_WORKSPACE_ID = $SCHEMA.member("workspace_id"); + private static final Schema $SCHEMA_ORG_ID = $SCHEMA.member("org_id"); + private static final Schema $SCHEMA_MODE = $SCHEMA.member("mode"); + private static final Schema $SCHEMA_OVERWRITE = $SCHEMA.member("overwrite"); + private static final Schema $SCHEMA_ON_ERROR = $SCHEMA.member("on_error"); + private static final Schema $SCHEMA_DRY_RUN = $SCHEMA.member("dry_run"); + private static final Schema $SCHEMA_VALUE_MERGE = $SCHEMA.member("value_merge"); + private static final Schema $SCHEMA_CONFIG_TAGS = $SCHEMA.member("config_tags"); + private static final Schema $SCHEMA_TOML_CONFIG = $SCHEMA.member("toml_config"); + + private final transient String workspaceId; + private final transient String orgId; + private final transient ImportMode mode; + private final transient Boolean overwrite; + private final transient ImportOnError onError; + private final transient Boolean dryRun; + private final transient Boolean valueMerge; + private final transient String configTags; + private final transient String tomlConfig; + + private ImportConfigTomlInput(Builder builder) { + this.workspaceId = builder.workspaceId; + this.orgId = builder.orgId; + this.mode = builder.mode; + this.overwrite = builder.overwrite; + this.onError = builder.onError; + this.dryRun = builder.dryRun; + this.valueMerge = builder.valueMerge; + this.configTags = builder.configTags; + this.tomlConfig = builder.tomlConfig; + } + + public String workspaceId() { + return workspaceId; + } + + public String orgId() { + return orgId; + } + + /** + * Whether to merge (default) or replace existing workspace config. + */ + public ImportMode mode() { + return mode; + } + + /** + * When false, entities that already exist are skipped instead of updated. Defaults to true. + */ + public Boolean overwrite() { + return overwrite; + } + + /** + * Whether to abort (default) or continue on per-entity errors. + */ + public ImportOnError onError() { + return onError; + } + + /** + * When true, validates and summarises the import without persisting anything. Defaults to false. + */ + public Boolean dryRun() { + return dryRun; + } + + /** + * When true, deep-merges object-valued default-configs with the existing value. Defaults to false. + */ + public Boolean valueMerge() { + return valueMerge; + } + + public String configTags() { + return configTags; + } + + public String tomlConfig() { + return tomlConfig; + } + + @Override + public String toString() { + return ToStringSerializer.serialize(this); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + ImportConfigTomlInput that = (ImportConfigTomlInput) other; + return Objects.equals(this.workspaceId, that.workspaceId) + && Objects.equals(this.orgId, that.orgId) + && Objects.equals(this.mode, that.mode) + && Objects.equals(this.overwrite, that.overwrite) + && Objects.equals(this.onError, that.onError) + && Objects.equals(this.dryRun, that.dryRun) + && Objects.equals(this.valueMerge, that.valueMerge) + && Objects.equals(this.configTags, that.configTags) + && Objects.equals(this.tomlConfig, that.tomlConfig); + } + + @Override + public int hashCode() { + return Objects.hash(workspaceId, orgId, mode, overwrite, onError, dryRun, valueMerge, configTags, tomlConfig); + } + + @Override + public Schema schema() { + return $SCHEMA; + } + + @Override + public void serializeMembers(ShapeSerializer serializer) { + serializer.writeString($SCHEMA_WORKSPACE_ID, workspaceId); + serializer.writeString($SCHEMA_ORG_ID, orgId); + if (mode != null) { + serializer.writeString($SCHEMA_MODE, mode.value()); + } + if (overwrite != null) { + serializer.writeBoolean($SCHEMA_OVERWRITE, overwrite); + } + if (onError != null) { + serializer.writeString($SCHEMA_ON_ERROR, onError.value()); + } + if (dryRun != null) { + serializer.writeBoolean($SCHEMA_DRY_RUN, dryRun); + } + if (valueMerge != null) { + serializer.writeBoolean($SCHEMA_VALUE_MERGE, valueMerge); + } + if (configTags != null) { + serializer.writeString($SCHEMA_CONFIG_TAGS, configTags); + } + serializer.writeString($SCHEMA_TOML_CONFIG, tomlConfig); + } + + @Override + @SuppressWarnings("unchecked") + public T getMemberValue(Schema member) { + return switch (member.memberIndex()) { + case 0 -> (T) SchemaUtils.validateSameMember($SCHEMA_WORKSPACE_ID, member, workspaceId); + case 1 -> (T) SchemaUtils.validateSameMember($SCHEMA_ORG_ID, member, orgId); + case 2 -> (T) SchemaUtils.validateSameMember($SCHEMA_TOML_CONFIG, member, tomlConfig); + case 3 -> (T) SchemaUtils.validateSameMember($SCHEMA_MODE, member, mode); + case 4 -> (T) SchemaUtils.validateSameMember($SCHEMA_OVERWRITE, member, overwrite); + case 5 -> (T) SchemaUtils.validateSameMember($SCHEMA_ON_ERROR, member, onError); + case 6 -> (T) SchemaUtils.validateSameMember($SCHEMA_DRY_RUN, member, dryRun); + case 7 -> (T) SchemaUtils.validateSameMember($SCHEMA_VALUE_MERGE, member, valueMerge); + case 8 -> (T) SchemaUtils.validateSameMember($SCHEMA_CONFIG_TAGS, member, configTags); + default -> throw new IllegalArgumentException("Attempted to get non-existent member: " + member.id()); + }; + } + + /** + * Create a new builder containing all the current property values of this object. + * + *

Note: This method performs only a shallow copy of the original properties. + * + * @return a builder for {@link ImportConfigTomlInput}. + */ + public Builder toBuilder() { + var builder = new Builder(); + builder.workspaceId(this.workspaceId); + builder.orgId(this.orgId); + builder.mode(this.mode); + builder.overwrite(this.overwrite); + builder.onError(this.onError); + builder.dryRun(this.dryRun); + builder.valueMerge(this.valueMerge); + builder.configTags(this.configTags); + builder.tomlConfig(this.tomlConfig); + return builder; + } + + /** + * @return returns a new Builder. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder for {@link ImportConfigTomlInput}. + */ + public static final class Builder implements ShapeBuilder { + private final PresenceTracker tracker = PresenceTracker.of($SCHEMA); + private String workspaceId; + private String orgId; + private ImportMode mode; + private Boolean overwrite; + private ImportOnError onError; + private Boolean dryRun; + private Boolean valueMerge; + private String configTags; + private String tomlConfig; + + private Builder() {} + + @Override + public Schema schema() { + return $SCHEMA; + } + + /** + *

Required + * @return this builder. + */ + public Builder workspaceId(String workspaceId) { + this.workspaceId = Objects.requireNonNull(workspaceId, "workspaceId cannot be null"); + tracker.setMember($SCHEMA_WORKSPACE_ID); + return this; + } + + /** + *

Required + * @return this builder. + */ + public Builder orgId(String orgId) { + this.orgId = Objects.requireNonNull(orgId, "orgId cannot be null"); + tracker.setMember($SCHEMA_ORG_ID); + return this; + } + + /** + * Whether to merge (default) or replace existing workspace config. + * + * @return this builder. + */ + public Builder mode(ImportMode mode) { + this.mode = mode; + return this; + } + + /** + * When false, entities that already exist are skipped instead of updated. Defaults to true. + * + * @return this builder. + */ + public Builder overwrite(boolean overwrite) { + this.overwrite = overwrite; + return this; + } + + /** + * Whether to abort (default) or continue on per-entity errors. + * + * @return this builder. + */ + public Builder onError(ImportOnError onError) { + this.onError = onError; + return this; + } + + /** + * When true, validates and summarises the import without persisting anything. Defaults to false. + * + * @return this builder. + */ + public Builder dryRun(boolean dryRun) { + this.dryRun = dryRun; + return this; + } + + /** + * When true, deep-merges object-valued default-configs with the existing value. Defaults to false. + * + * @return this builder. + */ + public Builder valueMerge(boolean valueMerge) { + this.valueMerge = valueMerge; + return this; + } + + /** + * @return this builder. + */ + public Builder configTags(String configTags) { + this.configTags = configTags; + return this; + } + + /** + *

Required + * @return this builder. + */ + public Builder tomlConfig(String tomlConfig) { + this.tomlConfig = Objects.requireNonNull(tomlConfig, "tomlConfig cannot be null"); + tracker.setMember($SCHEMA_TOML_CONFIG); + return this; + } + + @Override + public ImportConfigTomlInput build() { + tracker.validate(); + return new ImportConfigTomlInput(this); + } + + @Override + @SuppressWarnings("unchecked") + public void setMemberValue(Schema member, Object value) { + switch (member.memberIndex()) { + case 0 -> workspaceId((String) SchemaUtils.validateSameMember($SCHEMA_WORKSPACE_ID, member, value)); + case 1 -> orgId((String) SchemaUtils.validateSameMember($SCHEMA_ORG_ID, member, value)); + case 2 -> tomlConfig((String) SchemaUtils.validateSameMember($SCHEMA_TOML_CONFIG, member, value)); + case 3 -> mode((ImportMode) SchemaUtils.validateSameMember($SCHEMA_MODE, member, value)); + case 4 -> overwrite((boolean) SchemaUtils.validateSameMember($SCHEMA_OVERWRITE, member, value)); + case 5 -> onError((ImportOnError) SchemaUtils.validateSameMember($SCHEMA_ON_ERROR, member, value)); + case 6 -> dryRun((boolean) SchemaUtils.validateSameMember($SCHEMA_DRY_RUN, member, value)); + case 7 -> valueMerge((boolean) SchemaUtils.validateSameMember($SCHEMA_VALUE_MERGE, member, value)); + case 8 -> configTags((String) SchemaUtils.validateSameMember($SCHEMA_CONFIG_TAGS, member, value)); + default -> ShapeBuilder.super.setMemberValue(member, value); + } + } + + @Override + public ShapeBuilder errorCorrection() { + if (tracker.allSet()) { + return this; + } + if (!tracker.checkMember($SCHEMA_WORKSPACE_ID)) { + workspaceId(""); + } + if (!tracker.checkMember($SCHEMA_ORG_ID)) { + orgId(""); + } + if (!tracker.checkMember($SCHEMA_TOML_CONFIG)) { + tomlConfig(""); + } + return this; + } + + @Override + public Builder deserialize(ShapeDeserializer decoder) { + decoder.readStruct($SCHEMA, this, $InnerDeserializer.INSTANCE); + return this; + } + + @Override + public Builder deserializeMember(ShapeDeserializer decoder, Schema schema) { + decoder.readStruct(schema.assertMemberTargetIs($SCHEMA), this, $InnerDeserializer.INSTANCE); + return this; + } + + private static final class $InnerDeserializer implements ShapeDeserializer.StructMemberConsumer { + private static final $InnerDeserializer INSTANCE = new $InnerDeserializer(); + + @Override + public void accept(Builder builder, Schema member, ShapeDeserializer de) { + switch (member.memberIndex()) { + case 0 -> builder.workspaceId(de.readString(member)); + case 1 -> builder.orgId(de.readString(member)); + case 2 -> builder.tomlConfig(de.readString(member)); + case 3 -> builder.mode(ImportMode.builder().deserializeMember(de, member).build()); + case 4 -> builder.overwrite(de.readBoolean(member)); + case 5 -> builder.onError(ImportOnError.builder().deserializeMember(de, member).build()); + case 6 -> builder.dryRun(de.readBoolean(member)); + case 7 -> builder.valueMerge(de.readBoolean(member)); + case 8 -> builder.configTags(de.readString(member)); + default -> throw new IllegalArgumentException("Unexpected member: " + member.memberName()); + } + } + } + } +} + diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigTomlOutput.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigTomlOutput.java new file mode 100644 index 000000000..7eb29101b --- /dev/null +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigTomlOutput.java @@ -0,0 +1,325 @@ + +package io.juspay.superposition.model; + +import java.util.Objects; +import software.amazon.smithy.java.core.schema.PreludeSchemas; +import software.amazon.smithy.java.core.schema.PresenceTracker; +import software.amazon.smithy.java.core.schema.Schema; +import software.amazon.smithy.java.core.schema.SchemaUtils; +import software.amazon.smithy.java.core.schema.SerializableStruct; +import software.amazon.smithy.java.core.schema.ShapeBuilder; +import software.amazon.smithy.java.core.serde.ShapeDeserializer; +import software.amazon.smithy.java.core.serde.ShapeSerializer; +import software.amazon.smithy.java.core.serde.ToStringSerializer; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.RequiredTrait; +import software.amazon.smithy.utils.SmithyGenerated; + +/** + * Summary of what an import created, updated, skipped or deleted. + */ +@SmithyGenerated +public final class ImportConfigTomlOutput implements SerializableStruct { + public static final ShapeId $ID = ShapeId.from("io.superposition#ImportConfigOutput"); + + public static final Schema $SCHEMA = Schema.structureBuilder($ID) + .putMember("mode", PreludeSchemas.STRING, + new RequiredTrait()) + .putMember("dry_run", PreludeSchemas.BOOLEAN, + new RequiredTrait()) + .putMember("config_version", PreludeSchemas.STRING) + .putMember("dimensions", ImportEntityReport.$SCHEMA, + new RequiredTrait()) + .putMember("default_configs", ImportEntityReport.$SCHEMA, + new RequiredTrait()) + .putMember("contexts", ImportEntityReport.$SCHEMA, + new RequiredTrait()) + .build(); + + private static final Schema $SCHEMA_MODE = $SCHEMA.member("mode"); + private static final Schema $SCHEMA_DRY_RUN = $SCHEMA.member("dry_run"); + private static final Schema $SCHEMA_CONFIG_VERSION = $SCHEMA.member("config_version"); + private static final Schema $SCHEMA_DIMENSIONS = $SCHEMA.member("dimensions"); + private static final Schema $SCHEMA_DEFAULT_CONFIGS = $SCHEMA.member("default_configs"); + private static final Schema $SCHEMA_CONTEXTS = $SCHEMA.member("contexts"); + + private final transient String mode; + private final transient boolean dryRun; + private final transient String configVersion; + private final transient ImportEntityReport dimensions; + private final transient ImportEntityReport defaultConfigs; + private final transient ImportEntityReport contexts; + + private ImportConfigTomlOutput(Builder builder) { + this.mode = builder.mode; + this.dryRun = builder.dryRun; + this.configVersion = builder.configVersion; + this.dimensions = builder.dimensions; + this.defaultConfigs = builder.defaultConfigs; + this.contexts = builder.contexts; + } + + public String mode() { + return mode; + } + + public boolean dryRun() { + return dryRun; + } + + public String configVersion() { + return configVersion; + } + + public ImportEntityReport dimensions() { + return dimensions; + } + + public ImportEntityReport defaultConfigs() { + return defaultConfigs; + } + + public ImportEntityReport contexts() { + return contexts; + } + + @Override + public String toString() { + return ToStringSerializer.serialize(this); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + ImportConfigTomlOutput that = (ImportConfigTomlOutput) other; + return Objects.equals(this.mode, that.mode) + && this.dryRun == that.dryRun + && Objects.equals(this.configVersion, that.configVersion) + && Objects.equals(this.dimensions, that.dimensions) + && Objects.equals(this.defaultConfigs, that.defaultConfigs) + && Objects.equals(this.contexts, that.contexts); + } + + @Override + public int hashCode() { + return Objects.hash(mode, dryRun, configVersion, dimensions, defaultConfigs, contexts); + } + + @Override + public Schema schema() { + return $SCHEMA; + } + + @Override + public void serializeMembers(ShapeSerializer serializer) { + serializer.writeString($SCHEMA_MODE, mode); + serializer.writeBoolean($SCHEMA_DRY_RUN, dryRun); + if (configVersion != null) { + serializer.writeString($SCHEMA_CONFIG_VERSION, configVersion); + } + if (dimensions != null) { + serializer.writeStruct($SCHEMA_DIMENSIONS, dimensions); + } + if (defaultConfigs != null) { + serializer.writeStruct($SCHEMA_DEFAULT_CONFIGS, defaultConfigs); + } + if (contexts != null) { + serializer.writeStruct($SCHEMA_CONTEXTS, contexts); + } + } + + @Override + @SuppressWarnings("unchecked") + public T getMemberValue(Schema member) { + return switch (member.memberIndex()) { + case 0 -> (T) SchemaUtils.validateSameMember($SCHEMA_MODE, member, mode); + case 1 -> (T) SchemaUtils.validateSameMember($SCHEMA_DRY_RUN, member, dryRun); + case 2 -> (T) SchemaUtils.validateSameMember($SCHEMA_DIMENSIONS, member, dimensions); + case 3 -> (T) SchemaUtils.validateSameMember($SCHEMA_DEFAULT_CONFIGS, member, defaultConfigs); + case 4 -> (T) SchemaUtils.validateSameMember($SCHEMA_CONTEXTS, member, contexts); + case 5 -> (T) SchemaUtils.validateSameMember($SCHEMA_CONFIG_VERSION, member, configVersion); + default -> throw new IllegalArgumentException("Attempted to get non-existent member: " + member.id()); + }; + } + + /** + * Create a new builder containing all the current property values of this object. + * + *

Note: This method performs only a shallow copy of the original properties. + * + * @return a builder for {@link ImportConfigTomlOutput}. + */ + public Builder toBuilder() { + var builder = new Builder(); + builder.mode(this.mode); + builder.dryRun(this.dryRun); + builder.configVersion(this.configVersion); + builder.dimensions(this.dimensions); + builder.defaultConfigs(this.defaultConfigs); + builder.contexts(this.contexts); + return builder; + } + + /** + * @return returns a new Builder. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder for {@link ImportConfigTomlOutput}. + */ + public static final class Builder implements ShapeBuilder { + private final PresenceTracker tracker = PresenceTracker.of($SCHEMA); + private String mode; + private boolean dryRun; + private String configVersion; + private ImportEntityReport dimensions; + private ImportEntityReport defaultConfigs; + private ImportEntityReport contexts; + + private Builder() {} + + @Override + public Schema schema() { + return $SCHEMA; + } + + /** + *

Required + * @return this builder. + */ + public Builder mode(String mode) { + this.mode = Objects.requireNonNull(mode, "mode cannot be null"); + tracker.setMember($SCHEMA_MODE); + return this; + } + + /** + *

Required + * @return this builder. + */ + public Builder dryRun(boolean dryRun) { + this.dryRun = dryRun; + tracker.setMember($SCHEMA_DRY_RUN); + return this; + } + + /** + * @return this builder. + */ + public Builder configVersion(String configVersion) { + this.configVersion = configVersion; + return this; + } + + /** + *

Required + * @return this builder. + */ + public Builder dimensions(ImportEntityReport dimensions) { + this.dimensions = Objects.requireNonNull(dimensions, "dimensions cannot be null"); + tracker.setMember($SCHEMA_DIMENSIONS); + return this; + } + + /** + *

Required + * @return this builder. + */ + public Builder defaultConfigs(ImportEntityReport defaultConfigs) { + this.defaultConfigs = Objects.requireNonNull(defaultConfigs, "defaultConfigs cannot be null"); + tracker.setMember($SCHEMA_DEFAULT_CONFIGS); + return this; + } + + /** + *

Required + * @return this builder. + */ + public Builder contexts(ImportEntityReport contexts) { + this.contexts = Objects.requireNonNull(contexts, "contexts cannot be null"); + tracker.setMember($SCHEMA_CONTEXTS); + return this; + } + + @Override + public ImportConfigTomlOutput build() { + tracker.validate(); + return new ImportConfigTomlOutput(this); + } + + @Override + @SuppressWarnings("unchecked") + public void setMemberValue(Schema member, Object value) { + switch (member.memberIndex()) { + case 0 -> mode((String) SchemaUtils.validateSameMember($SCHEMA_MODE, member, value)); + case 1 -> dryRun((boolean) SchemaUtils.validateSameMember($SCHEMA_DRY_RUN, member, value)); + case 2 -> dimensions((ImportEntityReport) SchemaUtils.validateSameMember($SCHEMA_DIMENSIONS, member, value)); + case 3 -> defaultConfigs((ImportEntityReport) SchemaUtils.validateSameMember($SCHEMA_DEFAULT_CONFIGS, member, value)); + case 4 -> contexts((ImportEntityReport) SchemaUtils.validateSameMember($SCHEMA_CONTEXTS, member, value)); + case 5 -> configVersion((String) SchemaUtils.validateSameMember($SCHEMA_CONFIG_VERSION, member, value)); + default -> ShapeBuilder.super.setMemberValue(member, value); + } + } + + @Override + public ShapeBuilder errorCorrection() { + if (tracker.allSet()) { + return this; + } + if (!tracker.checkMember($SCHEMA_MODE)) { + mode(""); + } + if (!tracker.checkMember($SCHEMA_DRY_RUN)) { + tracker.setMember($SCHEMA_DRY_RUN); + } + if (!tracker.checkMember($SCHEMA_DIMENSIONS)) { + tracker.setMember($SCHEMA_DIMENSIONS); + } + if (!tracker.checkMember($SCHEMA_DEFAULT_CONFIGS)) { + tracker.setMember($SCHEMA_DEFAULT_CONFIGS); + } + if (!tracker.checkMember($SCHEMA_CONTEXTS)) { + tracker.setMember($SCHEMA_CONTEXTS); + } + return this; + } + + @Override + public Builder deserialize(ShapeDeserializer decoder) { + decoder.readStruct($SCHEMA, this, $InnerDeserializer.INSTANCE); + return this; + } + + @Override + public Builder deserializeMember(ShapeDeserializer decoder, Schema schema) { + decoder.readStruct(schema.assertMemberTargetIs($SCHEMA), this, $InnerDeserializer.INSTANCE); + return this; + } + + private static final class $InnerDeserializer implements ShapeDeserializer.StructMemberConsumer { + private static final $InnerDeserializer INSTANCE = new $InnerDeserializer(); + + @Override + public void accept(Builder builder, Schema member, ShapeDeserializer de) { + switch (member.memberIndex()) { + case 0 -> builder.mode(de.readString(member)); + case 1 -> builder.dryRun(de.readBoolean(member)); + case 2 -> builder.dimensions(ImportEntityReport.builder().deserializeMember(de, member).build()); + case 3 -> builder.defaultConfigs(ImportEntityReport.builder().deserializeMember(de, member).build()); + case 4 -> builder.contexts(ImportEntityReport.builder().deserializeMember(de, member).build()); + case 5 -> builder.configVersion(de.readString(member)); + default -> throw new IllegalArgumentException("Unexpected member: " + member.memberName()); + } + } + } + } +} + diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportEntityReport.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportEntityReport.java new file mode 100644 index 000000000..8d10c79a7 --- /dev/null +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportEntityReport.java @@ -0,0 +1,299 @@ + +package io.juspay.superposition.model; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import software.amazon.smithy.java.core.schema.PreludeSchemas; +import software.amazon.smithy.java.core.schema.PresenceTracker; +import software.amazon.smithy.java.core.schema.Schema; +import software.amazon.smithy.java.core.schema.SchemaUtils; +import software.amazon.smithy.java.core.schema.SerializableStruct; +import software.amazon.smithy.java.core.schema.ShapeBuilder; +import software.amazon.smithy.java.core.serde.ShapeDeserializer; +import software.amazon.smithy.java.core.serde.ShapeSerializer; +import software.amazon.smithy.java.core.serde.ToStringSerializer; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.RequiredTrait; +import software.amazon.smithy.utils.SmithyGenerated; + +/** + * Per-entity outcome counts for an import. + */ +@SmithyGenerated +public final class ImportEntityReport implements SerializableStruct { + public static final ShapeId $ID = ShapeId.from("io.superposition#ImportEntityReport"); + + public static final Schema $SCHEMA = Schema.structureBuilder($ID) + .putMember("created", PreludeSchemas.INTEGER, + new RequiredTrait()) + .putMember("updated", PreludeSchemas.INTEGER, + new RequiredTrait()) + .putMember("skipped", PreludeSchemas.INTEGER, + new RequiredTrait()) + .putMember("deleted", PreludeSchemas.INTEGER, + new RequiredTrait()) + .putMember("errors", SharedSchemas.IMPORT_ERROR_LIST) + .build(); + + private static final Schema $SCHEMA_CREATED = $SCHEMA.member("created"); + private static final Schema $SCHEMA_UPDATED = $SCHEMA.member("updated"); + private static final Schema $SCHEMA_SKIPPED = $SCHEMA.member("skipped"); + private static final Schema $SCHEMA_DELETED = $SCHEMA.member("deleted"); + private static final Schema $SCHEMA_ERRORS = $SCHEMA.member("errors"); + + private final transient int created; + private final transient int updated; + private final transient int skipped; + private final transient int deleted; + private final transient List errors; + + private ImportEntityReport(Builder builder) { + this.created = builder.created; + this.updated = builder.updated; + this.skipped = builder.skipped; + this.deleted = builder.deleted; + this.errors = builder.errors == null ? null : Collections.unmodifiableList(builder.errors); + } + + public int created() { + return created; + } + + public int updated() { + return updated; + } + + public int skipped() { + return skipped; + } + + public int deleted() { + return deleted; + } + + public List errors() { + if (errors == null) { + return Collections.emptyList(); + } + return errors; + } + + public boolean hasErrors() { + return errors != null; + } + + @Override + public String toString() { + return ToStringSerializer.serialize(this); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + ImportEntityReport that = (ImportEntityReport) other; + return this.created == that.created + && this.updated == that.updated + && this.skipped == that.skipped + && this.deleted == that.deleted + && Objects.equals(this.errors, that.errors); + } + + @Override + public int hashCode() { + return Objects.hash(created, updated, skipped, deleted, errors); + } + + @Override + public Schema schema() { + return $SCHEMA; + } + + @Override + public void serializeMembers(ShapeSerializer serializer) { + serializer.writeInteger($SCHEMA_CREATED, created); + serializer.writeInteger($SCHEMA_UPDATED, updated); + serializer.writeInteger($SCHEMA_SKIPPED, skipped); + serializer.writeInteger($SCHEMA_DELETED, deleted); + if (errors != null) { + serializer.writeList($SCHEMA_ERRORS, errors, errors.size(), SharedSerde.ImportErrorListSerializer.INSTANCE); + } + } + + @Override + @SuppressWarnings("unchecked") + public T getMemberValue(Schema member) { + return switch (member.memberIndex()) { + case 0 -> (T) SchemaUtils.validateSameMember($SCHEMA_CREATED, member, created); + case 1 -> (T) SchemaUtils.validateSameMember($SCHEMA_UPDATED, member, updated); + case 2 -> (T) SchemaUtils.validateSameMember($SCHEMA_SKIPPED, member, skipped); + case 3 -> (T) SchemaUtils.validateSameMember($SCHEMA_DELETED, member, deleted); + case 4 -> (T) SchemaUtils.validateSameMember($SCHEMA_ERRORS, member, errors); + default -> throw new IllegalArgumentException("Attempted to get non-existent member: " + member.id()); + }; + } + + /** + * Create a new builder containing all the current property values of this object. + * + *

Note: This method performs only a shallow copy of the original properties. + * + * @return a builder for {@link ImportEntityReport}. + */ + public Builder toBuilder() { + var builder = new Builder(); + builder.created(this.created); + builder.updated(this.updated); + builder.skipped(this.skipped); + builder.deleted(this.deleted); + builder.errors(this.errors); + return builder; + } + + /** + * @return returns a new Builder. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder for {@link ImportEntityReport}. + */ + public static final class Builder implements ShapeBuilder { + private final PresenceTracker tracker = PresenceTracker.of($SCHEMA); + private int created; + private int updated; + private int skipped; + private int deleted; + private List errors; + + private Builder() {} + + @Override + public Schema schema() { + return $SCHEMA; + } + + /** + *

Required + * @return this builder. + */ + public Builder created(int created) { + this.created = created; + tracker.setMember($SCHEMA_CREATED); + return this; + } + + /** + *

Required + * @return this builder. + */ + public Builder updated(int updated) { + this.updated = updated; + tracker.setMember($SCHEMA_UPDATED); + return this; + } + + /** + *

Required + * @return this builder. + */ + public Builder skipped(int skipped) { + this.skipped = skipped; + tracker.setMember($SCHEMA_SKIPPED); + return this; + } + + /** + *

Required + * @return this builder. + */ + public Builder deleted(int deleted) { + this.deleted = deleted; + tracker.setMember($SCHEMA_DELETED); + return this; + } + + /** + * @return this builder. + */ + public Builder errors(List errors) { + this.errors = errors; + return this; + } + + @Override + public ImportEntityReport build() { + tracker.validate(); + return new ImportEntityReport(this); + } + + @Override + @SuppressWarnings("unchecked") + public void setMemberValue(Schema member, Object value) { + switch (member.memberIndex()) { + case 0 -> created((int) SchemaUtils.validateSameMember($SCHEMA_CREATED, member, value)); + case 1 -> updated((int) SchemaUtils.validateSameMember($SCHEMA_UPDATED, member, value)); + case 2 -> skipped((int) SchemaUtils.validateSameMember($SCHEMA_SKIPPED, member, value)); + case 3 -> deleted((int) SchemaUtils.validateSameMember($SCHEMA_DELETED, member, value)); + case 4 -> errors((List) SchemaUtils.validateSameMember($SCHEMA_ERRORS, member, value)); + default -> ShapeBuilder.super.setMemberValue(member, value); + } + } + + @Override + public ShapeBuilder errorCorrection() { + if (tracker.allSet()) { + return this; + } + if (!tracker.checkMember($SCHEMA_CREATED)) { + tracker.setMember($SCHEMA_CREATED); + } + if (!tracker.checkMember($SCHEMA_UPDATED)) { + tracker.setMember($SCHEMA_UPDATED); + } + if (!tracker.checkMember($SCHEMA_SKIPPED)) { + tracker.setMember($SCHEMA_SKIPPED); + } + if (!tracker.checkMember($SCHEMA_DELETED)) { + tracker.setMember($SCHEMA_DELETED); + } + return this; + } + + @Override + public Builder deserialize(ShapeDeserializer decoder) { + decoder.readStruct($SCHEMA, this, $InnerDeserializer.INSTANCE); + return this; + } + + @Override + public Builder deserializeMember(ShapeDeserializer decoder, Schema schema) { + decoder.readStruct(schema.assertMemberTargetIs($SCHEMA), this, $InnerDeserializer.INSTANCE); + return this; + } + + private static final class $InnerDeserializer implements ShapeDeserializer.StructMemberConsumer { + private static final $InnerDeserializer INSTANCE = new $InnerDeserializer(); + + @Override + public void accept(Builder builder, Schema member, ShapeDeserializer de) { + switch (member.memberIndex()) { + case 0 -> builder.created(de.readInteger(member)); + case 1 -> builder.updated(de.readInteger(member)); + case 2 -> builder.skipped(de.readInteger(member)); + case 3 -> builder.deleted(de.readInteger(member)); + case 4 -> builder.errors(SharedSerde.deserializeImportErrorList(member, de)); + default -> throw new IllegalArgumentException("Unexpected member: " + member.memberName()); + } + } + } + } +} + diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportErrorItem.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportErrorItem.java new file mode 100644 index 000000000..5b09ec0d0 --- /dev/null +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportErrorItem.java @@ -0,0 +1,204 @@ + +package io.juspay.superposition.model; + +import java.util.Objects; +import software.amazon.smithy.java.core.schema.PreludeSchemas; +import software.amazon.smithy.java.core.schema.PresenceTracker; +import software.amazon.smithy.java.core.schema.Schema; +import software.amazon.smithy.java.core.schema.SchemaUtils; +import software.amazon.smithy.java.core.schema.SerializableStruct; +import software.amazon.smithy.java.core.schema.ShapeBuilder; +import software.amazon.smithy.java.core.serde.ShapeDeserializer; +import software.amazon.smithy.java.core.serde.ShapeSerializer; +import software.amazon.smithy.java.core.serde.ToStringSerializer; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.RequiredTrait; +import software.amazon.smithy.utils.SmithyGenerated; + +@SmithyGenerated +public final class ImportErrorItem implements SerializableStruct { + public static final ShapeId $ID = ShapeId.from("io.superposition#ImportErrorItem"); + + public static final Schema $SCHEMA = Schema.structureBuilder($ID) + .putMember("id", PreludeSchemas.STRING, + new RequiredTrait()) + .putMember("error", PreludeSchemas.STRING, + new RequiredTrait()) + .build(); + + private static final Schema $SCHEMA_ID = $SCHEMA.member("id"); + private static final Schema $SCHEMA_ERROR = $SCHEMA.member("error"); + + private final transient String id; + private final transient String error; + + private ImportErrorItem(Builder builder) { + this.id = builder.id; + this.error = builder.error; + } + + public String id() { + return id; + } + + public String error() { + return error; + } + + @Override + public String toString() { + return ToStringSerializer.serialize(this); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + ImportErrorItem that = (ImportErrorItem) other; + return Objects.equals(this.id, that.id) + && Objects.equals(this.error, that.error); + } + + @Override + public int hashCode() { + return Objects.hash(id, error); + } + + @Override + public Schema schema() { + return $SCHEMA; + } + + @Override + public void serializeMembers(ShapeSerializer serializer) { + serializer.writeString($SCHEMA_ID, id); + serializer.writeString($SCHEMA_ERROR, error); + } + + @Override + @SuppressWarnings("unchecked") + public T getMemberValue(Schema member) { + return switch (member.memberIndex()) { + case 0 -> (T) SchemaUtils.validateSameMember($SCHEMA_ID, member, id); + case 1 -> (T) SchemaUtils.validateSameMember($SCHEMA_ERROR, member, error); + default -> throw new IllegalArgumentException("Attempted to get non-existent member: " + member.id()); + }; + } + + /** + * Create a new builder containing all the current property values of this object. + * + *

Note: This method performs only a shallow copy of the original properties. + * + * @return a builder for {@link ImportErrorItem}. + */ + public Builder toBuilder() { + var builder = new Builder(); + builder.id(this.id); + builder.error(this.error); + return builder; + } + + /** + * @return returns a new Builder. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder for {@link ImportErrorItem}. + */ + public static final class Builder implements ShapeBuilder { + private final PresenceTracker tracker = PresenceTracker.of($SCHEMA); + private String id; + private String error; + + private Builder() {} + + @Override + public Schema schema() { + return $SCHEMA; + } + + /** + *

Required + * @return this builder. + */ + public Builder id(String id) { + this.id = Objects.requireNonNull(id, "id cannot be null"); + tracker.setMember($SCHEMA_ID); + return this; + } + + /** + *

Required + * @return this builder. + */ + public Builder error(String error) { + this.error = Objects.requireNonNull(error, "error cannot be null"); + tracker.setMember($SCHEMA_ERROR); + return this; + } + + @Override + public ImportErrorItem build() { + tracker.validate(); + return new ImportErrorItem(this); + } + + @Override + @SuppressWarnings("unchecked") + public void setMemberValue(Schema member, Object value) { + switch (member.memberIndex()) { + case 0 -> id((String) SchemaUtils.validateSameMember($SCHEMA_ID, member, value)); + case 1 -> error((String) SchemaUtils.validateSameMember($SCHEMA_ERROR, member, value)); + default -> ShapeBuilder.super.setMemberValue(member, value); + } + } + + @Override + public ShapeBuilder errorCorrection() { + if (tracker.allSet()) { + return this; + } + if (!tracker.checkMember($SCHEMA_ID)) { + id(""); + } + if (!tracker.checkMember($SCHEMA_ERROR)) { + error(""); + } + return this; + } + + @Override + public Builder deserialize(ShapeDeserializer decoder) { + decoder.readStruct($SCHEMA, this, $InnerDeserializer.INSTANCE); + return this; + } + + @Override + public Builder deserializeMember(ShapeDeserializer decoder, Schema schema) { + decoder.readStruct(schema.assertMemberTargetIs($SCHEMA), this, $InnerDeserializer.INSTANCE); + return this; + } + + private static final class $InnerDeserializer implements ShapeDeserializer.StructMemberConsumer { + private static final $InnerDeserializer INSTANCE = new $InnerDeserializer(); + + @Override + public void accept(Builder builder, Schema member, ShapeDeserializer de) { + switch (member.memberIndex()) { + case 0 -> builder.id(de.readString(member)); + case 1 -> builder.error(de.readString(member)); + default -> throw new IllegalArgumentException("Unexpected member: " + member.memberName()); + } + } + } + } +} + diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportMode.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportMode.java new file mode 100644 index 000000000..6f541575f --- /dev/null +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportMode.java @@ -0,0 +1,165 @@ + +package io.juspay.superposition.model; + +import java.util.List; +import java.util.Objects; +import java.util.Set; +import software.amazon.smithy.java.core.schema.Schema; +import software.amazon.smithy.java.core.schema.SerializableShape; +import software.amazon.smithy.java.core.schema.ShapeBuilder; +import software.amazon.smithy.java.core.serde.ShapeDeserializer; +import software.amazon.smithy.java.core.serde.ShapeSerializer; +import software.amazon.smithy.java.core.serde.ToStringSerializer; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.utils.SmithyGenerated; + +/** + * How an import treats workspace entities that are not present in the imported file. + */ +@SmithyGenerated +public final class ImportMode implements SerializableShape { + public static final ShapeId $ID = ShapeId.from("io.superposition#ImportMode"); + /** + * Upsert the entities in the file and leave everything else untouched. + */ + public static final ImportMode MERGE = new ImportMode(Type.MERGE, "merge"); + /** + * Mirror the file: additionally delete dimensions, default-configs and contexts that are absent from + * it. + */ + public static final ImportMode REPLACE = new ImportMode(Type.REPLACE, "replace"); + private static final List $TYPES = List.of(MERGE, REPLACE); + + public static final Schema $SCHEMA = Schema.createEnum($ID, + Set.of(MERGE.value, REPLACE.value) + ); + + private final String value; + private final Type type; + + private ImportMode(Type type, String value) { + this.type = Objects.requireNonNull(type, "type cannot be null"); + this.value = Objects.requireNonNull(value, "value cannot be null"); + } + + /** + * Enum representing the possible variants of {@link ImportMode}. + */ + public enum Type { + $UNKNOWN, + MERGE, + REPLACE + } + + /** + * Value contained by this Enum. + */ + public String value() { + return value; + } + + /** + * Type of this Enum variant. + */ + public Type type() { + return type; + } + + /** + * Create an Enum of an {@link Type#$UNKNOWN} type containing a value. + * + * @param value value contained by unknown Enum. + */ + public static ImportMode unknown(String value) { + return new ImportMode(Type.$UNKNOWN, value); + } + + /** + * Returns an unmodifiable list containing the constants of this enum type, in the order declared. + */ + public static List values() { + return $TYPES; + } + + @Override + public void serialize(ShapeSerializer serializer) { + serializer.writeString($SCHEMA, this.value()); + } + + @Override + public String toString() { + return ToStringSerializer.serialize(this); + } + + /** + * Returns a {@link ImportMode} constant with the specified value. + * + * @param value value to create {@code ImportMode} from. + * @throws IllegalArgumentException if value does not match a known value. + */ + public static ImportMode from(String value) { + return switch (value) { + case "merge" -> MERGE; + case "replace" -> REPLACE; + default -> throw new IllegalArgumentException("Unknown value: " + value); + }; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + ImportMode that = (ImportMode) other; + return this.value.equals(that.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + /** + * @return returns a new Builder. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder for {@link ImportMode}. + */ + public static final class Builder implements ShapeBuilder { + private String value; + + private Builder() {} + + @Override + public Schema schema() { + return $SCHEMA; + } + + private Builder value(String value) { + this.value = Objects.requireNonNull(value, "Enum value cannot be null"); + return this; + } + + @Override + public ImportMode build() { + return switch (value) { + case "merge" -> MERGE; + case "replace" -> REPLACE; + default -> new ImportMode(Type.$UNKNOWN, value); + }; + } + + @Override + public Builder deserialize(ShapeDeserializer de) { + return value(de.readString($SCHEMA)); + } + } +} + diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportOnError.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportOnError.java new file mode 100644 index 000000000..1399faa01 --- /dev/null +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportOnError.java @@ -0,0 +1,164 @@ + +package io.juspay.superposition.model; + +import java.util.List; +import java.util.Objects; +import java.util.Set; +import software.amazon.smithy.java.core.schema.Schema; +import software.amazon.smithy.java.core.schema.SerializableShape; +import software.amazon.smithy.java.core.schema.ShapeBuilder; +import software.amazon.smithy.java.core.serde.ShapeDeserializer; +import software.amazon.smithy.java.core.serde.ShapeSerializer; +import software.amazon.smithy.java.core.serde.ToStringSerializer; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.utils.SmithyGenerated; + +/** + * How an import reacts when an individual entity fails to apply. + */ +@SmithyGenerated +public final class ImportOnError implements SerializableShape { + public static final ShapeId $ID = ShapeId.from("io.superposition#ImportOnError"); + /** + * Roll the whole import back on the first error. + */ + public static final ImportOnError ABORT = new ImportOnError(Type.ABORT, "abort"); + /** + * Apply everything that is valid and report per-entity errors. + */ + public static final ImportOnError CONTINUE = new ImportOnError(Type.CONTINUE, "continue"); + private static final List $TYPES = List.of(ABORT, CONTINUE); + + public static final Schema $SCHEMA = Schema.createEnum($ID, + Set.of(ABORT.value, CONTINUE.value) + ); + + private final String value; + private final Type type; + + private ImportOnError(Type type, String value) { + this.type = Objects.requireNonNull(type, "type cannot be null"); + this.value = Objects.requireNonNull(value, "value cannot be null"); + } + + /** + * Enum representing the possible variants of {@link ImportOnError}. + */ + public enum Type { + $UNKNOWN, + ABORT, + CONTINUE + } + + /** + * Value contained by this Enum. + */ + public String value() { + return value; + } + + /** + * Type of this Enum variant. + */ + public Type type() { + return type; + } + + /** + * Create an Enum of an {@link Type#$UNKNOWN} type containing a value. + * + * @param value value contained by unknown Enum. + */ + public static ImportOnError unknown(String value) { + return new ImportOnError(Type.$UNKNOWN, value); + } + + /** + * Returns an unmodifiable list containing the constants of this enum type, in the order declared. + */ + public static List values() { + return $TYPES; + } + + @Override + public void serialize(ShapeSerializer serializer) { + serializer.writeString($SCHEMA, this.value()); + } + + @Override + public String toString() { + return ToStringSerializer.serialize(this); + } + + /** + * Returns a {@link ImportOnError} constant with the specified value. + * + * @param value value to create {@code ImportOnError} from. + * @throws IllegalArgumentException if value does not match a known value. + */ + public static ImportOnError from(String value) { + return switch (value) { + case "abort" -> ABORT; + case "continue" -> CONTINUE; + default -> throw new IllegalArgumentException("Unknown value: " + value); + }; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + ImportOnError that = (ImportOnError) other; + return this.value.equals(that.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + /** + * @return returns a new Builder. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder for {@link ImportOnError}. + */ + public static final class Builder implements ShapeBuilder { + private String value; + + private Builder() {} + + @Override + public Schema schema() { + return $SCHEMA; + } + + private Builder value(String value) { + this.value = Objects.requireNonNull(value, "Enum value cannot be null"); + return this; + } + + @Override + public ImportOnError build() { + return switch (value) { + case "abort" -> ABORT; + case "continue" -> CONTINUE; + default -> new ImportOnError(Type.$UNKNOWN, value); + }; + } + + @Override + public Builder deserialize(ShapeDeserializer de) { + return value(de.readString($SCHEMA)); + } + } +} + diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/SharedSchemas.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/SharedSchemas.java index c28d3be76..5e9325de4 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/model/SharedSchemas.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/SharedSchemas.java @@ -164,6 +164,10 @@ final class SharedSchemas { .putMember("member", TypeTemplatesResponse.$SCHEMA) .build(); + static final Schema IMPORT_ERROR_LIST = Schema.listBuilder(ShapeId.from("io.superposition#ImportErrorList")) + .putMember("member", ImportErrorItem.$SCHEMA) + .build(); + static final Schema ORGANISATION_LIST = Schema.listBuilder(ShapeId.from("io.superposition#OrganisationList")) .putMember("member", OrganisationResponse.$SCHEMA) .build(); diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/SharedSerde.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/SharedSerde.java index 0cb0a253f..cb40ca724 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/model/SharedSerde.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/SharedSerde.java @@ -173,6 +173,37 @@ public void accept(List state, ShapeDeserializer deseriali } } + static final class ImportErrorListSerializer implements BiConsumer, ShapeSerializer> { + static final ImportErrorListSerializer INSTANCE = new ImportErrorListSerializer(); + + @Override + public void accept(List values, ShapeSerializer serializer) { + for (var value : values) { + serializer.writeStruct(SharedSchemas.IMPORT_ERROR_LIST.listMember(), value); + } + } + } + + static List deserializeImportErrorList(Schema schema, ShapeDeserializer deserializer) { + var size = deserializer.containerSize(); + List result = size == -1 ? new ArrayList<>() : new ArrayList<>(size); + deserializer.readList(schema, result, ImportErrorList$MemberDeserializer.INSTANCE); + return result; + } + + private static final class ImportErrorList$MemberDeserializer implements ShapeDeserializer.ListMemberConsumer> { + static final ImportErrorList$MemberDeserializer INSTANCE = new ImportErrorList$MemberDeserializer(); + + @Override + public void accept(List state, ShapeDeserializer deserializer) { + if (deserializer.isNull()) { + + return; + } + state.add(ImportErrorItem.builder().deserializeMember(deserializer, SharedSchemas.IMPORT_ERROR_LIST.listMember()).build()); + } + } + static final class TypeTemplatesListSerializer implements BiConsumer, ShapeSerializer> { static final TypeTemplatesListSerializer INSTANCE = new TypeTemplatesListSerializer(); diff --git a/clients/javascript/sdk/src/Superposition.ts b/clients/javascript/sdk/src/Superposition.ts index 57db6acd6..171dd6f31 100644 --- a/clients/javascript/sdk/src/Superposition.ts +++ b/clients/javascript/sdk/src/Superposition.ts @@ -253,6 +253,16 @@ import { GetWorkspaceCommandInput, GetWorkspaceCommandOutput, } from "./commands/GetWorkspaceCommand"; +import { + ImportConfigJsonCommand, + ImportConfigJsonCommandInput, + ImportConfigJsonCommandOutput, +} from "./commands/ImportConfigJsonCommand"; +import { + ImportConfigTomlCommand, + ImportConfigTomlCommandInput, + ImportConfigTomlCommandOutput, +} from "./commands/ImportConfigTomlCommand"; import { ListAuditLogsCommand, ListAuditLogsCommandInput, @@ -492,6 +502,8 @@ const commands = { GetWebhookCommand, GetWebhookByEventCommand, GetWorkspaceCommand, + ImportConfigJsonCommand, + ImportConfigTomlCommand, ListAuditLogsCommand, ListContextsCommand, ListDefaultConfigsCommand, @@ -1382,6 +1394,40 @@ export interface Superposition { cb: (err: any, data?: GetWorkspaceCommandOutput) => void ): void; + /** + * @see {@link ImportConfigJsonCommand} + */ + importConfigJson( + args: ImportConfigJsonCommandInput, + options?: __HttpHandlerOptions, + ): Promise; + importConfigJson( + args: ImportConfigJsonCommandInput, + cb: (err: any, data?: ImportConfigJsonCommandOutput) => void + ): void; + importConfigJson( + args: ImportConfigJsonCommandInput, + options: __HttpHandlerOptions, + cb: (err: any, data?: ImportConfigJsonCommandOutput) => void + ): void; + + /** + * @see {@link ImportConfigTomlCommand} + */ + importConfigToml( + args: ImportConfigTomlCommandInput, + options?: __HttpHandlerOptions, + ): Promise; + importConfigToml( + args: ImportConfigTomlCommandInput, + cb: (err: any, data?: ImportConfigTomlCommandOutput) => void + ): void; + importConfigToml( + args: ImportConfigTomlCommandInput, + options: __HttpHandlerOptions, + cb: (err: any, data?: ImportConfigTomlCommandOutput) => void + ): void; + /** * @see {@link ListAuditLogsCommand} */ diff --git a/clients/javascript/sdk/src/SuperpositionClient.ts b/clients/javascript/sdk/src/SuperpositionClient.ts index 0428476fa..3d15a387f 100644 --- a/clients/javascript/sdk/src/SuperpositionClient.ts +++ b/clients/javascript/sdk/src/SuperpositionClient.ts @@ -205,6 +205,14 @@ import { GetWorkspaceCommandInput, GetWorkspaceCommandOutput, } from "./commands/GetWorkspaceCommand"; +import { + ImportConfigJsonCommandInput, + ImportConfigJsonCommandOutput, +} from "./commands/ImportConfigJsonCommand"; +import { + ImportConfigTomlCommandInput, + ImportConfigTomlCommandOutput, +} from "./commands/ImportConfigTomlCommand"; import { ListAuditLogsCommandInput, ListAuditLogsCommandOutput, @@ -469,6 +477,8 @@ export type ServiceInputTypes = | GetWebhookByEventCommandInput | GetWebhookCommandInput | GetWorkspaceCommandInput + | ImportConfigJsonCommandInput + | ImportConfigTomlCommandInput | ListAuditLogsCommandInput | ListContextsCommandInput | ListDefaultConfigsCommandInput @@ -561,6 +571,8 @@ export type ServiceOutputTypes = | GetWebhookByEventCommandOutput | GetWebhookCommandOutput | GetWorkspaceCommandOutput + | ImportConfigJsonCommandOutput + | ImportConfigTomlCommandOutput | ListAuditLogsCommandOutput | ListContextsCommandOutput | ListDefaultConfigsCommandOutput diff --git a/clients/javascript/sdk/src/commands/ImportConfigJsonCommand.ts b/clients/javascript/sdk/src/commands/ImportConfigJsonCommand.ts new file mode 100644 index 000000000..1c2db7912 --- /dev/null +++ b/clients/javascript/sdk/src/commands/ImportConfigJsonCommand.ts @@ -0,0 +1,141 @@ +// smithy-typescript generated code +import { + ServiceInputTypes, + ServiceOutputTypes, + SuperpositionClientResolvedConfig, +} from "../SuperpositionClient"; +import { + ImportConfigJsonInput, + ImportConfigOutput, +} from "../models/models_0"; +import { + de_ImportConfigJsonCommand, + se_ImportConfigJsonCommand, +} from "../protocols/Aws_restJson1"; +import { getSerdePlugin } from "@smithy/middleware-serde"; +import { Command as $Command } from "@smithy/smithy-client"; +import { MetadataBearer as __MetadataBearer } from "@smithy/types"; + +/** + * @public + */ +export type { __MetadataBearer }; +export { $Command }; +/** + * @public + * + * The input for {@link ImportConfigJsonCommand}. + */ +export interface ImportConfigJsonCommandInput extends ImportConfigJsonInput {} +/** + * @public + * + * The output of {@link ImportConfigJsonCommand}. + */ +export interface ImportConfigJsonCommandOutput extends ImportConfigOutput, __MetadataBearer {} + +/** + * Imports a full config from a JSON document, persisting dimensions, default-configs and contexts in a single transaction after validating the document. + * @example + * Use a bare-bones client and the command you need to make an API call. + * ```javascript + * import { SuperpositionClient, ImportConfigJsonCommand } from "superposition-sdk"; // ES Modules import + * // const { SuperpositionClient, ImportConfigJsonCommand } = require("superposition-sdk"); // CommonJS import + * const client = new SuperpositionClient(config); + * const input = { // ImportConfigJsonInput + * workspace_id: "STRING_VALUE", // required + * org_id: "STRING_VALUE", // required + * mode: "merge" || "replace", + * overwrite: true || false, + * on_error: "abort" || "continue", + * dry_run: true || false, + * value_merge: true || false, + * config_tags: "STRING_VALUE", + * json_config: "STRING_VALUE", // required + * }; + * const command = new ImportConfigJsonCommand(input); + * const response = await client.send(command); + * // { // ImportConfigOutput + * // mode: "STRING_VALUE", // required + * // dry_run: true || false, // required + * // config_version: "STRING_VALUE", + * // dimensions: { // ImportEntityReport + * // created: Number("int"), // required + * // updated: Number("int"), // required + * // skipped: Number("int"), // required + * // deleted: Number("int"), // required + * // errors: [ // ImportErrorList + * // { // ImportErrorItem + * // id: "STRING_VALUE", // required + * // error: "STRING_VALUE", // required + * // }, + * // ], + * // }, + * // default_configs: { + * // created: Number("int"), // required + * // updated: Number("int"), // required + * // skipped: Number("int"), // required + * // deleted: Number("int"), // required + * // errors: [ + * // { + * // id: "STRING_VALUE", // required + * // error: "STRING_VALUE", // required + * // }, + * // ], + * // }, + * // contexts: { + * // created: Number("int"), // required + * // updated: Number("int"), // required + * // skipped: Number("int"), // required + * // deleted: Number("int"), // required + * // errors: [ + * // { + * // id: "STRING_VALUE", // required + * // error: "STRING_VALUE", // required + * // }, + * // ], + * // }, + * // }; + * + * ``` + * + * @param ImportConfigJsonCommandInput - {@link ImportConfigJsonCommandInput} + * @returns {@link ImportConfigJsonCommandOutput} + * @see {@link ImportConfigJsonCommandInput} for command's `input` shape. + * @see {@link ImportConfigJsonCommandOutput} for command's `response` shape. + * @see {@link SuperpositionClientResolvedConfig | config} for SuperpositionClient's `config` shape. + * + * @throws {@link InternalServerError} (server fault) + * + * @throws {@link SuperpositionServiceException} + *

Base exception class for all service exceptions from Superposition service.

+ * + * @public + */ +export class ImportConfigJsonCommand extends $Command.classBuilder() + .m(function (this: any, Command: any, cs: any, config: SuperpositionClientResolvedConfig, o: any) { + return [ + + getSerdePlugin(config, this.serialize, this.deserialize), + ]; + }) + .s("Superposition", "ImportConfigJson", { + + }) + .n("SuperpositionClient", "ImportConfigJsonCommand") + .f(void 0, void 0) + .ser(se_ImportConfigJsonCommand) + .de(de_ImportConfigJsonCommand) +.build() { +/** @internal type navigation helper, not in runtime. */ +declare protected static __types: { + api: { + input: ImportConfigJsonInput; + output: ImportConfigOutput; + }; + sdk: { + input: ImportConfigJsonCommandInput; + output: ImportConfigJsonCommandOutput; + }; +}; +} diff --git a/clients/javascript/sdk/src/commands/ImportConfigTomlCommand.ts b/clients/javascript/sdk/src/commands/ImportConfigTomlCommand.ts new file mode 100644 index 000000000..ee7130db5 --- /dev/null +++ b/clients/javascript/sdk/src/commands/ImportConfigTomlCommand.ts @@ -0,0 +1,141 @@ +// smithy-typescript generated code +import { + ServiceInputTypes, + ServiceOutputTypes, + SuperpositionClientResolvedConfig, +} from "../SuperpositionClient"; +import { + ImportConfigOutput, + ImportConfigTomlInput, +} from "../models/models_0"; +import { + de_ImportConfigTomlCommand, + se_ImportConfigTomlCommand, +} from "../protocols/Aws_restJson1"; +import { getSerdePlugin } from "@smithy/middleware-serde"; +import { Command as $Command } from "@smithy/smithy-client"; +import { MetadataBearer as __MetadataBearer } from "@smithy/types"; + +/** + * @public + */ +export type { __MetadataBearer }; +export { $Command }; +/** + * @public + * + * The input for {@link ImportConfigTomlCommand}. + */ +export interface ImportConfigTomlCommandInput extends ImportConfigTomlInput {} +/** + * @public + * + * The output of {@link ImportConfigTomlCommand}. + */ +export interface ImportConfigTomlCommandOutput extends ImportConfigOutput, __MetadataBearer {} + +/** + * Imports a full config from a TOML document, persisting dimensions, default-configs and contexts in a single transaction after validating the document. + * @example + * Use a bare-bones client and the command you need to make an API call. + * ```javascript + * import { SuperpositionClient, ImportConfigTomlCommand } from "superposition-sdk"; // ES Modules import + * // const { SuperpositionClient, ImportConfigTomlCommand } = require("superposition-sdk"); // CommonJS import + * const client = new SuperpositionClient(config); + * const input = { // ImportConfigTomlInput + * workspace_id: "STRING_VALUE", // required + * org_id: "STRING_VALUE", // required + * mode: "merge" || "replace", + * overwrite: true || false, + * on_error: "abort" || "continue", + * dry_run: true || false, + * value_merge: true || false, + * config_tags: "STRING_VALUE", + * toml_config: "STRING_VALUE", // required + * }; + * const command = new ImportConfigTomlCommand(input); + * const response = await client.send(command); + * // { // ImportConfigOutput + * // mode: "STRING_VALUE", // required + * // dry_run: true || false, // required + * // config_version: "STRING_VALUE", + * // dimensions: { // ImportEntityReport + * // created: Number("int"), // required + * // updated: Number("int"), // required + * // skipped: Number("int"), // required + * // deleted: Number("int"), // required + * // errors: [ // ImportErrorList + * // { // ImportErrorItem + * // id: "STRING_VALUE", // required + * // error: "STRING_VALUE", // required + * // }, + * // ], + * // }, + * // default_configs: { + * // created: Number("int"), // required + * // updated: Number("int"), // required + * // skipped: Number("int"), // required + * // deleted: Number("int"), // required + * // errors: [ + * // { + * // id: "STRING_VALUE", // required + * // error: "STRING_VALUE", // required + * // }, + * // ], + * // }, + * // contexts: { + * // created: Number("int"), // required + * // updated: Number("int"), // required + * // skipped: Number("int"), // required + * // deleted: Number("int"), // required + * // errors: [ + * // { + * // id: "STRING_VALUE", // required + * // error: "STRING_VALUE", // required + * // }, + * // ], + * // }, + * // }; + * + * ``` + * + * @param ImportConfigTomlCommandInput - {@link ImportConfigTomlCommandInput} + * @returns {@link ImportConfigTomlCommandOutput} + * @see {@link ImportConfigTomlCommandInput} for command's `input` shape. + * @see {@link ImportConfigTomlCommandOutput} for command's `response` shape. + * @see {@link SuperpositionClientResolvedConfig | config} for SuperpositionClient's `config` shape. + * + * @throws {@link InternalServerError} (server fault) + * + * @throws {@link SuperpositionServiceException} + *

Base exception class for all service exceptions from Superposition service.

+ * + * @public + */ +export class ImportConfigTomlCommand extends $Command.classBuilder() + .m(function (this: any, Command: any, cs: any, config: SuperpositionClientResolvedConfig, o: any) { + return [ + + getSerdePlugin(config, this.serialize, this.deserialize), + ]; + }) + .s("Superposition", "ImportConfigToml", { + + }) + .n("SuperpositionClient", "ImportConfigTomlCommand") + .f(void 0, void 0) + .ser(se_ImportConfigTomlCommand) + .de(de_ImportConfigTomlCommand) +.build() { +/** @internal type navigation helper, not in runtime. */ +declare protected static __types: { + api: { + input: ImportConfigTomlInput; + output: ImportConfigOutput; + }; + sdk: { + input: ImportConfigTomlCommandInput; + output: ImportConfigTomlCommandOutput; + }; +}; +} diff --git a/clients/javascript/sdk/src/commands/index.ts b/clients/javascript/sdk/src/commands/index.ts index cce57fa67..f655df324 100644 --- a/clients/javascript/sdk/src/commands/index.ts +++ b/clients/javascript/sdk/src/commands/index.ts @@ -49,6 +49,8 @@ export * from "./GetVersionCommand"; export * from "./GetWebhookCommand"; export * from "./GetWebhookByEventCommand"; export * from "./GetWorkspaceCommand"; +export * from "./ImportConfigJsonCommand"; +export * from "./ImportConfigTomlCommand"; export * from "./ListAuditLogsCommand"; export * from "./ListContextsCommand"; export * from "./ListDefaultConfigsCommand"; diff --git a/clients/javascript/sdk/src/models/models_0.ts b/clients/javascript/sdk/src/models/models_0.ts index 59ca94f27..ffdba0631 100644 --- a/clients/javascript/sdk/src/models/models_0.ts +++ b/clients/javascript/sdk/src/models/models_0.ts @@ -2751,6 +2751,171 @@ export interface GetWorkspaceInput { workspace_name: string | undefined; } +/** + * @public + * @enum + */ +export const ImportMode = { + /** + * Upsert the entities in the file and leave everything else untouched. + */ + MERGE: "merge", + /** + * Mirror the file: additionally delete dimensions, default-configs and contexts that are absent from it. + */ + REPLACE: "replace", +} as const +/** + * @public + */ +export type ImportMode = typeof ImportMode[keyof typeof ImportMode] + +/** + * @public + * @enum + */ +export const ImportOnError = { + /** + * Roll the whole import back on the first error. + */ + ABORT: "abort", + /** + * Apply everything that is valid and report per-entity errors. + */ + CONTINUE: "continue", +} as const +/** + * @public + */ +export type ImportOnError = typeof ImportOnError[keyof typeof ImportOnError] + +/** + * @public + */ +export interface ImportConfigJsonInput { + workspace_id: string | undefined; + org_id: string | undefined; + /** + * Whether to merge (default) or replace existing workspace config. + * @public + */ + mode?: ImportMode | undefined; + + /** + * When false, entities that already exist are skipped instead of updated. Defaults to true. + * @public + */ + overwrite?: boolean | undefined; + + /** + * Whether to abort (default) or continue on per-entity errors. + * @public + */ + on_error?: ImportOnError | undefined; + + /** + * When true, validates and summarises the import without persisting anything. Defaults to false. + * @public + */ + dry_run?: boolean | undefined; + + /** + * When true, deep-merges object-valued default-configs with the existing value. Defaults to false. + * @public + */ + value_merge?: boolean | undefined; + + config_tags?: string | undefined; + json_config: string | undefined; +} + +/** + * @public + */ +export interface ImportErrorItem { + id: string | undefined; + error: string | undefined; +} + +/** + * Per-entity outcome counts for an import. + * @public + */ +export interface ImportEntityReport { + created: number | undefined; + updated: number | undefined; + skipped: number | undefined; + deleted: number | undefined; + errors?: (ImportErrorItem)[] | undefined; +} + +/** + * Summary of what an import created, updated, skipped or deleted. + * @public + */ +export interface ImportConfigOutput { + mode: string | undefined; + dry_run: boolean | undefined; + config_version?: string | undefined; + /** + * Per-entity outcome counts for an import. + * @public + */ + dimensions: ImportEntityReport | undefined; + + /** + * Per-entity outcome counts for an import. + * @public + */ + default_configs: ImportEntityReport | undefined; + + /** + * Per-entity outcome counts for an import. + * @public + */ + contexts: ImportEntityReport | undefined; +} + +/** + * @public + */ +export interface ImportConfigTomlInput { + workspace_id: string | undefined; + org_id: string | undefined; + /** + * Whether to merge (default) or replace existing workspace config. + * @public + */ + mode?: ImportMode | undefined; + + /** + * When false, entities that already exist are skipped instead of updated. Defaults to true. + * @public + */ + overwrite?: boolean | undefined; + + /** + * Whether to abort (default) or continue on per-entity errors. + * @public + */ + on_error?: ImportOnError | undefined; + + /** + * When true, validates and summarises the import without persisting anything. Defaults to false. + * @public + */ + dry_run?: boolean | undefined; + + /** + * When true, deep-merges object-valued default-configs with the existing value. Defaults to false. + * @public + */ + value_merge?: boolean | undefined; + + config_tags?: string | undefined; + toml_config: string | undefined; +} + /** * @public */ diff --git a/clients/javascript/sdk/src/protocols/Aws_restJson1.ts b/clients/javascript/sdk/src/protocols/Aws_restJson1.ts index f593b2a38..2d4a84595 100644 --- a/clients/javascript/sdk/src/protocols/Aws_restJson1.ts +++ b/clients/javascript/sdk/src/protocols/Aws_restJson1.ts @@ -199,6 +199,14 @@ import { GetWorkspaceCommandInput, GetWorkspaceCommandOutput, } from "../commands/GetWorkspaceCommand"; +import { + ImportConfigJsonCommandInput, + ImportConfigJsonCommandOutput, +} from "../commands/ImportConfigJsonCommand"; +import { + ImportConfigTomlCommandInput, + ImportConfigTomlCommandOutput, +} from "../commands/ImportConfigTomlCommand"; import { ListAuditLogsCommandInput, ListAuditLogsCommandOutput, @@ -1685,6 +1693,66 @@ export const se_GetWorkspaceCommand = async( return b.build(); } +/** + * serializeAws_restJson1ImportConfigJsonCommand + */ +export const se_ImportConfigJsonCommand = async( + input: ImportConfigJsonCommandInput, + context: __SerdeContext +): Promise<__HttpRequest> => { + const b = rb(input, context); + const headers: any = map({}, isSerializableHeaderValue, { + 'content-type': 'text/plain', + [_xw]: input[_wi]!, + [_xoi]: input[_oi]!, + [_xim]: input[_m]!, + [_xio]: [() => isSerializableHeaderValue(input[_o]), () => input[_o]!.toString()], + [_xioe]: input[_oe]!, + [_xidr]: [() => isSerializableHeaderValue(input[_dr]), () => input[_dr]!.toString()], + [_xivm]: [() => isSerializableHeaderValue(input[_vm]), () => input[_vm]!.toString()], + [_xct]: input[_ct]!, + }); + b.bp("/config/json/import"); + let body: any; + if (input.json_config !== undefined) { + body = input.json_config; + } + b.m("POST") + .h(headers) + .b(body); + return b.build(); +} + +/** + * serializeAws_restJson1ImportConfigTomlCommand + */ +export const se_ImportConfigTomlCommand = async( + input: ImportConfigTomlCommandInput, + context: __SerdeContext +): Promise<__HttpRequest> => { + const b = rb(input, context); + const headers: any = map({}, isSerializableHeaderValue, { + 'content-type': 'text/plain', + [_xw]: input[_wi]!, + [_xoi]: input[_oi]!, + [_xim]: input[_m]!, + [_xio]: [() => isSerializableHeaderValue(input[_o]), () => input[_o]!.toString()], + [_xioe]: input[_oe]!, + [_xidr]: [() => isSerializableHeaderValue(input[_dr]), () => input[_dr]!.toString()], + [_xivm]: [() => isSerializableHeaderValue(input[_vm]), () => input[_vm]!.toString()], + [_xct]: input[_ct]!, + }); + b.bp("/config/toml/import"); + let body: any; + if (input.toml_config !== undefined) { + body = input.toml_config; + } + b.m("POST") + .h(headers) + .b(body); + return b.build(); +} + /** * serializeAws_restJson1ListAuditLogsCommand */ @@ -4144,6 +4212,58 @@ export const de_GetWorkspaceCommand = async( return contents; } +/** + * deserializeAws_restJson1ImportConfigJsonCommand + */ +export const de_ImportConfigJsonCommand = async( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + if (output.statusCode !== 200 && output.statusCode >= 300) { + return de_CommandError(output, context); + } + const contents: any = map({ + $metadata: deserializeMetadata(output), + }); + const data: Record = __expectNonNull((__expectObject(await parseBody(output.body, context))), "body"); + const doc = take(data, { + 'config_version': __expectString, + 'contexts': _json, + 'default_configs': _json, + 'dimensions': _json, + 'dry_run': __expectBoolean, + 'mode': __expectString, + }); + Object.assign(contents, doc); + return contents; +} + +/** + * deserializeAws_restJson1ImportConfigTomlCommand + */ +export const de_ImportConfigTomlCommand = async( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + if (output.statusCode !== 200 && output.statusCode >= 300) { + return de_CommandError(output, context); + } + const contents: any = map({ + $metadata: deserializeMetadata(output), + }); + const data: Record = __expectNonNull((__expectObject(await parseBody(output.body, context))), "body"); + const doc = take(data, { + 'config_version': __expectString, + 'contexts': _json, + 'default_configs': _json, + 'dimensions': _json, + 'dry_run': __expectBoolean, + 'mode': __expectString, + }); + Object.assign(contents, doc); + return contents; +} + /** * deserializeAws_restJson1ListAuditLogsCommand */ @@ -6005,6 +6125,12 @@ const de_CommandError = async( }) as any; } + // de_ImportEntityReport omitted. + + // de_ImportErrorItem omitted. + + // de_ImportErrorList omitted. + /** * deserializeAws_restJson1ListContextOut */ @@ -6473,6 +6599,7 @@ const de_CommandError = async( const _ci = "context_id"; const _ct = "config_tags"; const _dms = "dimension_match_strategy"; + const _dr = "dry_run"; const _egi = "experiment_group_ids"; const _ei = "experiment_ids"; const _en = "experiment_name"; @@ -6488,8 +6615,11 @@ const de_CommandError = async( const _lm = "last-modified"; const _lm_ = "last_modified"; const _lmb = "last_modified_by"; + const _m = "mode"; const _ms = "merge_strategy"; const _n = "name"; + const _o = "overwrite"; + const _oe = "on_error"; const _oi = "org_id"; const _p = "prefix"; const _pa = "page"; @@ -6504,10 +6634,16 @@ const de_CommandError = async( const _td = "to_date"; const _u = "username"; const _v = "version"; + const _vm = "value_merge"; const _wi = "workspace_id"; const _xai = "x-audit-id"; const _xct = "x-config-tags"; const _xcv = "x-config-version"; + const _xidr = "x-import-dry-run"; + const _xim = "x-import-mode"; + const _xio = "x-import-overwrite"; + const _xioe = "x-import-on-error"; + const _xivm = "x-import-value-merge"; const _xms = "x-merge-strategy"; const _xoi = "x-org-id"; const _xw = "x-workspace"; diff --git a/clients/python/sdk/superposition_sdk/_private/schemas.py b/clients/python/sdk/superposition_sdk/_private/schemas.py index d3b6d6abd..38551be0d 100644 --- a/clients/python/sdk/superposition_sdk/_private/schemas.py +++ b/clients/python/sdk/superposition_sdk/_private/schemas.py @@ -15308,6 +15308,519 @@ ) +IMPORT_MODE = Schema.collection( + id=ShapeID("io.superposition#ImportMode"), + shape_type=ShapeType.ENUM, + members={ + "MERGE": { + "target": UNIT, + "index": 0, + "traits": [ + Trait.new(id=ShapeID("smithy.api#enumValue"), value="merge"), + + ], + }, + + "REPLACE": { + "target": UNIT, + "index": 1, + "traits": [ + Trait.new(id=ShapeID("smithy.api#enumValue"), value="replace"), + + ], + }, + + } +) + +IMPORT_ON_ERROR = Schema.collection( + id=ShapeID("io.superposition#ImportOnError"), + shape_type=ShapeType.ENUM, + members={ + "ABORT": { + "target": UNIT, + "index": 0, + "traits": [ + Trait.new(id=ShapeID("smithy.api#enumValue"), value="abort"), + + ], + }, + + "CONTINUE": { + "target": UNIT, + "index": 1, + "traits": [ + Trait.new(id=ShapeID("smithy.api#enumValue"), value="continue"), + + ], + }, + + } +) + +IMPORT_CONFIG_JSON_INPUT = Schema.collection( + id=ShapeID("io.superposition#ImportConfigJsonInput"), + + traits=[ + Trait.new(id=ShapeID("smithy.api#input")), + + ], + members={ + "workspace_id": { + "target": STRING, + "index": 0, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-workspace"), + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + "org_id": { + "target": STRING, + "index": 1, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-org-id"), + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + "mode": { + "target": IMPORT_MODE, + "index": 2, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-import-mode"), + Trait.new(id=ShapeID("smithy.api#notProperty")), + + ], + }, + + "overwrite": { + "target": BOOLEAN, + "index": 3, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-import-overwrite"), + Trait.new(id=ShapeID("smithy.api#notProperty")), + + ], + }, + + "on_error": { + "target": IMPORT_ON_ERROR, + "index": 4, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-import-on-error"), + Trait.new(id=ShapeID("smithy.api#notProperty")), + + ], + }, + + "dry_run": { + "target": BOOLEAN, + "index": 5, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-import-dry-run"), + Trait.new(id=ShapeID("smithy.api#notProperty")), + + ], + }, + + "value_merge": { + "target": BOOLEAN, + "index": 6, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-import-value-merge"), + Trait.new(id=ShapeID("smithy.api#notProperty")), + + ], + }, + + "config_tags": { + "target": STRING, + "index": 7, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-config-tags"), + Trait.new(id=ShapeID("smithy.api#notProperty")), + + ], + }, + + "json_config": { + "target": STRING, + "index": 8, + "traits": [ + Trait.new(id=ShapeID("smithy.api#notProperty")), + Trait.new(id=ShapeID("smithy.api#required")), + Trait.new(id=ShapeID("smithy.api#httpPayload")), + + ], + }, + + } +) + +IMPORT_ERROR_ITEM = Schema.collection( + id=ShapeID("io.superposition#ImportErrorItem"), + + members={ + "id": { + "target": STRING, + "index": 0, + "traits": [ + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + "error": { + "target": STRING, + "index": 1, + "traits": [ + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + } +) + +IMPORT_ERROR_LIST = Schema.collection( + id=ShapeID("io.superposition#ImportErrorList"), + shape_type=ShapeType.LIST, + members={ + "member": { + "target": IMPORT_ERROR_ITEM, + "index": 0, + }, + + } +) + +IMPORT_ENTITY_REPORT = Schema.collection( + id=ShapeID("io.superposition#ImportEntityReport"), + + members={ + "created": { + "target": INTEGER, + "index": 0, + "traits": [ + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + "updated": { + "target": INTEGER, + "index": 1, + "traits": [ + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + "skipped": { + "target": INTEGER, + "index": 2, + "traits": [ + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + "deleted": { + "target": INTEGER, + "index": 3, + "traits": [ + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + "errors": { + "target": IMPORT_ERROR_LIST, + "index": 4, + }, + + } +) + +IMPORT_CONFIG_JSON_OUTPUT = Schema.collection( + id=ShapeID("io.superposition#ImportConfigJsonOutput"), + + traits=[ + Trait.new(id=ShapeID("smithy.synthetic#originalShapeId"), value="io.superposition#ImportConfigOutput"), + Trait.new(id=ShapeID("smithy.api#output")), + + ], + members={ + "mode": { + "target": STRING, + "index": 0, + "traits": [ + Trait.new(id=ShapeID("smithy.api#notProperty")), + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + "dry_run": { + "target": BOOLEAN, + "index": 1, + "traits": [ + Trait.new(id=ShapeID("smithy.api#notProperty")), + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + "config_version": { + "target": STRING, + "index": 2, + "traits": [ + Trait.new(id=ShapeID("smithy.api#notProperty")), + + ], + }, + + "dimensions": { + "target": IMPORT_ENTITY_REPORT, + "index": 3, + "traits": [ + Trait.new(id=ShapeID("smithy.api#notProperty")), + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + "default_configs": { + "target": IMPORT_ENTITY_REPORT, + "index": 4, + "traits": [ + Trait.new(id=ShapeID("smithy.api#notProperty")), + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + "contexts": { + "target": IMPORT_ENTITY_REPORT, + "index": 5, + "traits": [ + Trait.new(id=ShapeID("smithy.api#notProperty")), + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + } +) + +IMPORT_CONFIG_JSON = Schema( + id=ShapeID("io.superposition#ImportConfigJson"), + shape_type=ShapeType.OPERATION, + traits=[ + Trait.new(id=ShapeID("smithy.api#tags"), value=( + "Configuration Management", + )), + Trait.new(id=ShapeID("smithy.api#http"), value=MappingProxyType({ + "method": "POST", + "uri": "/config/json/import", + })), + + ], + +) + +IMPORT_CONFIG_TOML_INPUT = Schema.collection( + id=ShapeID("io.superposition#ImportConfigTomlInput"), + + traits=[ + Trait.new(id=ShapeID("smithy.api#input")), + + ], + members={ + "workspace_id": { + "target": STRING, + "index": 0, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-workspace"), + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + "org_id": { + "target": STRING, + "index": 1, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-org-id"), + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + "mode": { + "target": IMPORT_MODE, + "index": 2, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-import-mode"), + Trait.new(id=ShapeID("smithy.api#notProperty")), + + ], + }, + + "overwrite": { + "target": BOOLEAN, + "index": 3, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-import-overwrite"), + Trait.new(id=ShapeID("smithy.api#notProperty")), + + ], + }, + + "on_error": { + "target": IMPORT_ON_ERROR, + "index": 4, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-import-on-error"), + Trait.new(id=ShapeID("smithy.api#notProperty")), + + ], + }, + + "dry_run": { + "target": BOOLEAN, + "index": 5, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-import-dry-run"), + Trait.new(id=ShapeID("smithy.api#notProperty")), + + ], + }, + + "value_merge": { + "target": BOOLEAN, + "index": 6, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-import-value-merge"), + Trait.new(id=ShapeID("smithy.api#notProperty")), + + ], + }, + + "config_tags": { + "target": STRING, + "index": 7, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-config-tags"), + Trait.new(id=ShapeID("smithy.api#notProperty")), + + ], + }, + + "toml_config": { + "target": STRING, + "index": 8, + "traits": [ + Trait.new(id=ShapeID("smithy.api#notProperty")), + Trait.new(id=ShapeID("smithy.api#required")), + Trait.new(id=ShapeID("smithy.api#httpPayload")), + + ], + }, + + } +) + +IMPORT_CONFIG_TOML_OUTPUT = Schema.collection( + id=ShapeID("io.superposition#ImportConfigTomlOutput"), + + traits=[ + Trait.new(id=ShapeID("smithy.synthetic#originalShapeId"), value="io.superposition#ImportConfigOutput"), + Trait.new(id=ShapeID("smithy.api#output")), + + ], + members={ + "mode": { + "target": STRING, + "index": 0, + "traits": [ + Trait.new(id=ShapeID("smithy.api#notProperty")), + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + "dry_run": { + "target": BOOLEAN, + "index": 1, + "traits": [ + Trait.new(id=ShapeID("smithy.api#notProperty")), + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + "config_version": { + "target": STRING, + "index": 2, + "traits": [ + Trait.new(id=ShapeID("smithy.api#notProperty")), + + ], + }, + + "dimensions": { + "target": IMPORT_ENTITY_REPORT, + "index": 3, + "traits": [ + Trait.new(id=ShapeID("smithy.api#notProperty")), + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + "default_configs": { + "target": IMPORT_ENTITY_REPORT, + "index": 4, + "traits": [ + Trait.new(id=ShapeID("smithy.api#notProperty")), + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + "contexts": { + "target": IMPORT_ENTITY_REPORT, + "index": 5, + "traits": [ + Trait.new(id=ShapeID("smithy.api#notProperty")), + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + } +) + +IMPORT_CONFIG_TOML = Schema( + id=ShapeID("io.superposition#ImportConfigToml"), + shape_type=ShapeType.OPERATION, + traits=[ + Trait.new(id=ShapeID("smithy.api#tags"), value=( + "Configuration Management", + )), + Trait.new(id=ShapeID("smithy.api#http"), value=MappingProxyType({ + "method": "POST", + "uri": "/config/toml/import", + })), + + ], + +) + LIST_ORGANISATION_INPUT = Schema.collection( id=ShapeID("io.superposition#ListOrganisationInput"), diff --git a/clients/python/sdk/superposition_sdk/client.py b/clients/python/sdk/superposition_sdk/client.py index 687469a7b..f03fc7a28 100644 --- a/clients/python/sdk/superposition_sdk/client.py +++ b/clients/python/sdk/superposition_sdk/client.py @@ -84,6 +84,8 @@ _deserialize_get_webhook, _deserialize_get_webhook_by_event, _deserialize_get_workspace, + _deserialize_import_config_json, + _deserialize_import_config_toml, _deserialize_list_audit_logs, _deserialize_list_contexts, _deserialize_list_default_configs, @@ -273,6 +275,12 @@ GetWebhookOutput, GetWorkspaceInput, GetWorkspaceOutput, + IMPORT_CONFIG_JSON, + IMPORT_CONFIG_TOML, + ImportConfigJsonInput, + ImportConfigJsonOutput, + ImportConfigTomlInput, + ImportConfigTomlOutput, LIST_AUDIT_LOGS, LIST_CONTEXTS, LIST_DEFAULT_CONFIGS, @@ -437,6 +445,8 @@ _serialize_get_webhook, _serialize_get_webhook_by_event, _serialize_get_workspace, + _serialize_import_config_json, + _serialize_import_config_toml, _serialize_list_audit_logs, _serialize_list_contexts, _serialize_list_default_configs, @@ -1853,6 +1863,62 @@ async def get_workspace(self, input: GetWorkspaceInput, plugins: list[Plugin] | operation=GET_WORKSPACE, ) + async def import_config_json(self, input: ImportConfigJsonInput, plugins: list[Plugin] | None = None) -> ImportConfigJsonOutput: + """ + Imports a full config from a JSON document, persisting dimensions, + default-configs and contexts in a single transaction after validating the + document. + + :param input: The operation's input. + + :param plugins: A list of callables that modify the configuration dynamically. + Changes made by these plugins only apply for the duration of the operation + execution and will not affect any other operation invocations. + + """ + operation_plugins: list[Plugin] = [ + + ] + if plugins: + operation_plugins.extend(plugins) + + return await self._execute_operation( + input=input, + plugins=operation_plugins, + serialize=_serialize_import_config_json, + deserialize=_deserialize_import_config_json, + config=self._config, + operation=IMPORT_CONFIG_JSON, + ) + + async def import_config_toml(self, input: ImportConfigTomlInput, plugins: list[Plugin] | None = None) -> ImportConfigTomlOutput: + """ + Imports a full config from a TOML document, persisting dimensions, + default-configs and contexts in a single transaction after validating the + document. + + :param input: The operation's input. + + :param plugins: A list of callables that modify the configuration dynamically. + Changes made by these plugins only apply for the duration of the operation + execution and will not affect any other operation invocations. + + """ + operation_plugins: list[Plugin] = [ + + ] + if plugins: + operation_plugins.extend(plugins) + + return await self._execute_operation( + input=input, + plugins=operation_plugins, + serialize=_serialize_import_config_toml, + deserialize=_deserialize_import_config_toml, + config=self._config, + operation=IMPORT_CONFIG_TOML, + ) + async def list_audit_logs(self, input: ListAuditLogsInput, plugins: list[Plugin] | None = None) -> ListAuditLogsOutput: """ Retrieves a paginated list of audit logs with support for filtering by date diff --git a/clients/python/sdk/superposition_sdk/config.py b/clients/python/sdk/superposition_sdk/config.py index bcd3c6aaf..c99032bd7 100644 --- a/clients/python/sdk/superposition_sdk/config.py +++ b/clients/python/sdk/superposition_sdk/config.py @@ -116,6 +116,10 @@ GetWebhookOutput, GetWorkspaceInput, GetWorkspaceOutput, + ImportConfigJsonInput, + ImportConfigJsonOutput, + ImportConfigTomlInput, + ImportConfigTomlOutput, ListAuditLogsInput, ListAuditLogsOutput, ListContextsInput, @@ -193,7 +197,97 @@ ) -_ServiceInterceptor = Union[Interceptor[AddMembersToGroupInput, AddMembersToGroupOutput, Any, Any], Interceptor[ApplicableVariantsInput, ApplicableVariantsOutput, Any, Any], Interceptor[BulkOperationInput, BulkOperationOutput, Any, Any], Interceptor[ConcludeExperimentInput, ConcludeExperimentOutput, Any, Any], Interceptor[CreateContextInput, CreateContextOutput, Any, Any], Interceptor[CreateDefaultConfigInput, CreateDefaultConfigOutput, Any, Any], Interceptor[CreateDimensionInput, CreateDimensionOutput, Any, Any], Interceptor[CreateExperimentInput, CreateExperimentOutput, Any, Any], Interceptor[CreateExperimentGroupInput, CreateExperimentGroupOutput, Any, Any], Interceptor[CreateFunctionInput, CreateFunctionOutput, Any, Any], Interceptor[CreateOrganisationInput, CreateOrganisationOutput, Any, Any], Interceptor[CreateSecretInput, CreateSecretOutput, Any, Any], Interceptor[CreateTypeTemplatesInput, CreateTypeTemplatesOutput, Any, Any], Interceptor[CreateVariableInput, CreateVariableOutput, Any, Any], Interceptor[CreateWebhookInput, CreateWebhookOutput, Any, Any], Interceptor[CreateWorkspaceInput, CreateWorkspaceOutput, Any, Any], Interceptor[DeleteContextInput, DeleteContextOutput, Any, Any], Interceptor[DeleteDefaultConfigInput, DeleteDefaultConfigOutput, Any, Any], Interceptor[DeleteDimensionInput, DeleteDimensionOutput, Any, Any], Interceptor[DeleteExperimentGroupInput, DeleteExperimentGroupOutput, Any, Any], Interceptor[DeleteFunctionInput, DeleteFunctionOutput, Any, Any], Interceptor[DeleteSecretInput, DeleteSecretOutput, Any, Any], Interceptor[DeleteTypeTemplatesInput, DeleteTypeTemplatesOutput, Any, Any], Interceptor[DeleteVariableInput, DeleteVariableOutput, Any, Any], Interceptor[DeleteWebhookInput, DeleteWebhookOutput, Any, Any], Interceptor[DiscardExperimentInput, DiscardExperimentOutput, Any, Any], Interceptor[GetConfigInput, GetConfigOutput, Any, Any], Interceptor[GetConfigJsonInput, GetConfigJsonOutput, Any, Any], Interceptor[GetConfigTomlInput, GetConfigTomlOutput, Any, Any], Interceptor[GetContextInput, GetContextOutput, Any, Any], Interceptor[GetContextFromConditionInput, GetContextFromConditionOutput, Any, Any], Interceptor[GetDefaultConfigInput, GetDefaultConfigOutput, Any, Any], Interceptor[GetDetailedResolvedConfigInput, GetDetailedResolvedConfigOutput, Any, Any], Interceptor[GetDimensionInput, GetDimensionOutput, Any, Any], Interceptor[GetExperimentInput, GetExperimentOutput, Any, Any], Interceptor[GetExperimentConfigInput, GetExperimentConfigOutput, Any, Any], Interceptor[GetExperimentGroupInput, GetExperimentGroupOutput, Any, Any], Interceptor[GetFunctionInput, GetFunctionOutput, Any, Any], Interceptor[GetOrganisationInput, GetOrganisationOutput, Any, Any], Interceptor[GetResolvedConfigInput, GetResolvedConfigOutput, Any, Any], Interceptor[GetResolvedConfigExplanationInput, GetResolvedConfigExplanationOutput, Any, Any], Interceptor[GetResolvedConfigWithIdentifierInput, GetResolvedConfigWithIdentifierOutput, Any, Any], Interceptor[GetSecretInput, GetSecretOutput, Any, Any], Interceptor[GetTypeTemplateInput, GetTypeTemplateOutput, Any, Any], Interceptor[GetTypeTemplatesListInput, GetTypeTemplatesListOutput, Any, Any], Interceptor[GetVariableInput, GetVariableOutput, Any, Any], Interceptor[GetVersionInput, GetVersionOutput, Any, Any], Interceptor[GetWebhookInput, GetWebhookOutput, Any, Any], Interceptor[GetWebhookByEventInput, GetWebhookByEventOutput, Any, Any], Interceptor[GetWorkspaceInput, GetWorkspaceOutput, Any, Any], Interceptor[ListAuditLogsInput, ListAuditLogsOutput, Any, Any], Interceptor[ListContextsInput, ListContextsOutput, Any, Any], Interceptor[ListDefaultConfigsInput, ListDefaultConfigsOutput, Any, Any], Interceptor[ListDimensionsInput, ListDimensionsOutput, Any, Any], Interceptor[ListExperimentInput, ListExperimentOutput, Any, Any], Interceptor[ListExperimentGroupsInput, ListExperimentGroupsOutput, Any, Any], Interceptor[ListFunctionInput, ListFunctionOutput, Any, Any], Interceptor[ListOrganisationInput, ListOrganisationOutput, Any, Any], Interceptor[ListSecretsInput, ListSecretsOutput, Any, Any], Interceptor[ListVariablesInput, ListVariablesOutput, Any, Any], Interceptor[ListVersionsInput, ListVersionsOutput, Any, Any], Interceptor[ListWebhookInput, ListWebhookOutput, Any, Any], Interceptor[ListWorkspaceInput, ListWorkspaceOutput, Any, Any], Interceptor[MigrateWorkspaceSchemaInput, MigrateWorkspaceSchemaOutput, Any, Any], Interceptor[MoveContextInput, MoveContextOutput, Any, Any], Interceptor[PauseExperimentInput, PauseExperimentOutput, Any, Any], Interceptor[PublishInput, PublishOutput, Any, Any], Interceptor[RampExperimentInput, RampExperimentOutput, Any, Any], Interceptor[RemoveMembersFromGroupInput, RemoveMembersFromGroupOutput, Any, Any], Interceptor[ResumeExperimentInput, ResumeExperimentOutput, Any, Any], Interceptor[RotateMasterEncryptionKeyInput, RotateMasterEncryptionKeyOutput, Any, Any], Interceptor[RotateWorkspaceEncryptionKeyInput, RotateWorkspaceEncryptionKeyOutput, Any, Any], Interceptor[TestInput, TestOutput, Any, Any], Interceptor[UpdateDefaultConfigInput, UpdateDefaultConfigOutput, Any, Any], Interceptor[UpdateDimensionInput, UpdateDimensionOutput, Any, Any], Interceptor[UpdateExperimentGroupInput, UpdateExperimentGroupOutput, Any, Any], Interceptor[UpdateFunctionInput, UpdateFunctionOutput, Any, Any], Interceptor[UpdateOrganisationInput, UpdateOrganisationOutput, Any, Any], Interceptor[UpdateOverrideInput, UpdateOverrideOutput, Any, Any], Interceptor[UpdateOverridesExperimentInput, UpdateOverridesExperimentOutput, Any, Any], Interceptor[UpdateSecretInput, UpdateSecretOutput, Any, Any], Interceptor[UpdateTypeTemplatesInput, UpdateTypeTemplatesOutput, Any, Any], Interceptor[UpdateVariableInput, UpdateVariableOutput, Any, Any], Interceptor[UpdateWebhookInput, UpdateWebhookOutput, Any, Any], Interceptor[UpdateWorkspaceInput, UpdateWorkspaceOutput, Any, Any], Interceptor[ValidateContextInput, ValidateContextOutput, Any, Any], Interceptor[WeightRecomputeInput, WeightRecomputeOutput, Any, Any]] +_ServiceInterceptor = Union[ + Interceptor[AddMembersToGroupInput, AddMembersToGroupOutput, Any, Any], + Interceptor[ApplicableVariantsInput, ApplicableVariantsOutput, Any, Any], + Interceptor[BulkOperationInput, BulkOperationOutput, Any, Any], + Interceptor[ConcludeExperimentInput, ConcludeExperimentOutput, Any, Any], + Interceptor[CreateContextInput, CreateContextOutput, Any, Any], + Interceptor[CreateDefaultConfigInput, CreateDefaultConfigOutput, Any, Any], + Interceptor[CreateDimensionInput, CreateDimensionOutput, Any, Any], + Interceptor[CreateExperimentInput, CreateExperimentOutput, Any, Any], + Interceptor[CreateExperimentGroupInput, CreateExperimentGroupOutput, Any, Any], + Interceptor[CreateFunctionInput, CreateFunctionOutput, Any, Any], + Interceptor[CreateOrganisationInput, CreateOrganisationOutput, Any, Any], + Interceptor[CreateSecretInput, CreateSecretOutput, Any, Any], + Interceptor[CreateTypeTemplatesInput, CreateTypeTemplatesOutput, Any, Any], + Interceptor[CreateVariableInput, CreateVariableOutput, Any, Any], + Interceptor[CreateWebhookInput, CreateWebhookOutput, Any, Any], + Interceptor[CreateWorkspaceInput, CreateWorkspaceOutput, Any, Any], + Interceptor[DeleteContextInput, DeleteContextOutput, Any, Any], + Interceptor[DeleteDefaultConfigInput, DeleteDefaultConfigOutput, Any, Any], + Interceptor[DeleteDimensionInput, DeleteDimensionOutput, Any, Any], + Interceptor[DeleteExperimentGroupInput, DeleteExperimentGroupOutput, Any, Any], + Interceptor[DeleteFunctionInput, DeleteFunctionOutput, Any, Any], + Interceptor[DeleteSecretInput, DeleteSecretOutput, Any, Any], + Interceptor[DeleteTypeTemplatesInput, DeleteTypeTemplatesOutput, Any, Any], + Interceptor[DeleteVariableInput, DeleteVariableOutput, Any, Any], + Interceptor[DeleteWebhookInput, DeleteWebhookOutput, Any, Any], + Interceptor[DiscardExperimentInput, DiscardExperimentOutput, Any, Any], + Interceptor[GetConfigInput, GetConfigOutput, Any, Any], + Interceptor[GetConfigJsonInput, GetConfigJsonOutput, Any, Any], + Interceptor[GetConfigTomlInput, GetConfigTomlOutput, Any, Any], + Interceptor[GetContextInput, GetContextOutput, Any, Any], + Interceptor[GetContextFromConditionInput, GetContextFromConditionOutput, Any, Any], + Interceptor[GetDefaultConfigInput, GetDefaultConfigOutput, Any, Any], + Interceptor[GetDetailedResolvedConfigInput, GetDetailedResolvedConfigOutput, Any, Any], + Interceptor[GetDimensionInput, GetDimensionOutput, Any, Any], + Interceptor[GetExperimentInput, GetExperimentOutput, Any, Any], + Interceptor[GetExperimentConfigInput, GetExperimentConfigOutput, Any, Any], + Interceptor[GetExperimentGroupInput, GetExperimentGroupOutput, Any, Any], + Interceptor[GetFunctionInput, GetFunctionOutput, Any, Any], + Interceptor[GetOrganisationInput, GetOrganisationOutput, Any, Any], + Interceptor[GetResolvedConfigInput, GetResolvedConfigOutput, Any, Any], + Interceptor[GetResolvedConfigExplanationInput, GetResolvedConfigExplanationOutput, Any, Any], + Interceptor[GetResolvedConfigWithIdentifierInput, GetResolvedConfigWithIdentifierOutput, Any, Any], + Interceptor[GetSecretInput, GetSecretOutput, Any, Any], + Interceptor[GetTypeTemplateInput, GetTypeTemplateOutput, Any, Any], + Interceptor[GetTypeTemplatesListInput, GetTypeTemplatesListOutput, Any, Any], + Interceptor[GetVariableInput, GetVariableOutput, Any, Any], + Interceptor[GetVersionInput, GetVersionOutput, Any, Any], + Interceptor[GetWebhookInput, GetWebhookOutput, Any, Any], + Interceptor[GetWebhookByEventInput, GetWebhookByEventOutput, Any, Any], + Interceptor[GetWorkspaceInput, GetWorkspaceOutput, Any, Any], + Interceptor[ImportConfigJsonInput, ImportConfigJsonOutput, Any, Any], + Interceptor[ImportConfigTomlInput, ImportConfigTomlOutput, Any, Any], + Interceptor[ListAuditLogsInput, ListAuditLogsOutput, Any, Any], + Interceptor[ListContextsInput, ListContextsOutput, Any, Any], + Interceptor[ListDefaultConfigsInput, ListDefaultConfigsOutput, Any, Any], + Interceptor[ListDimensionsInput, ListDimensionsOutput, Any, Any], + Interceptor[ListExperimentInput, ListExperimentOutput, Any, Any], + Interceptor[ListExperimentGroupsInput, ListExperimentGroupsOutput, Any, Any], + Interceptor[ListFunctionInput, ListFunctionOutput, Any, Any], + Interceptor[ListOrganisationInput, ListOrganisationOutput, Any, Any], + Interceptor[ListSecretsInput, ListSecretsOutput, Any, Any], + Interceptor[ListVariablesInput, ListVariablesOutput, Any, Any], + Interceptor[ListVersionsInput, ListVersionsOutput, Any, Any], + Interceptor[ListWebhookInput, ListWebhookOutput, Any, Any], + Interceptor[ListWorkspaceInput, ListWorkspaceOutput, Any, Any], + Interceptor[MigrateWorkspaceSchemaInput, MigrateWorkspaceSchemaOutput, Any, Any], + Interceptor[MoveContextInput, MoveContextOutput, Any, Any], + Interceptor[PauseExperimentInput, PauseExperimentOutput, Any, Any], + Interceptor[PublishInput, PublishOutput, Any, Any], + Interceptor[RampExperimentInput, RampExperimentOutput, Any, Any], + Interceptor[RemoveMembersFromGroupInput, RemoveMembersFromGroupOutput, Any, Any], + Interceptor[ResumeExperimentInput, ResumeExperimentOutput, Any, Any], + Interceptor[RotateMasterEncryptionKeyInput, RotateMasterEncryptionKeyOutput, Any, Any], + Interceptor[RotateWorkspaceEncryptionKeyInput, RotateWorkspaceEncryptionKeyOutput, Any, Any], + Interceptor[TestInput, TestOutput, Any, Any], + Interceptor[UpdateDefaultConfigInput, UpdateDefaultConfigOutput, Any, Any], + Interceptor[UpdateDimensionInput, UpdateDimensionOutput, Any, Any], + Interceptor[UpdateExperimentGroupInput, UpdateExperimentGroupOutput, Any, Any], + Interceptor[UpdateFunctionInput, UpdateFunctionOutput, Any, Any], + Interceptor[UpdateOrganisationInput, UpdateOrganisationOutput, Any, Any], + Interceptor[UpdateOverrideInput, UpdateOverrideOutput, Any, Any], + Interceptor[UpdateOverridesExperimentInput, UpdateOverridesExperimentOutput, Any, Any], + Interceptor[UpdateSecretInput, UpdateSecretOutput, Any, Any], + Interceptor[UpdateTypeTemplatesInput, UpdateTypeTemplatesOutput, Any, Any], + Interceptor[UpdateVariableInput, UpdateVariableOutput, Any, Any], + Interceptor[UpdateWebhookInput, UpdateWebhookOutput, Any, Any], + Interceptor[UpdateWorkspaceInput, UpdateWorkspaceOutput, Any, Any], + Interceptor[ValidateContextInput, ValidateContextOutput, Any, Any], + Interceptor[WeightRecomputeInput, WeightRecomputeOutput, Any, Any], +] @dataclass(init=False) class Config: """Configuration for Superposition.""" diff --git a/clients/python/sdk/superposition_sdk/deserialize.py b/clients/python/sdk/superposition_sdk/deserialize.py index 7b9b65797..a46fdb8e9 100644 --- a/clients/python/sdk/superposition_sdk/deserialize.py +++ b/clients/python/sdk/superposition_sdk/deserialize.py @@ -65,6 +65,8 @@ GetWebhookByEventOutput, GetWebhookOutput, GetWorkspaceOutput, + ImportConfigJsonOutput, + ImportConfigTomlOutput, InternalServerError, ListAuditLogsOutput, ListContextsOutput, @@ -1545,6 +1547,56 @@ async def _deserialize_error_get_workspace(http_response: HTTPResponse, config: case _: return UnknownApiError(f"{code}: {message}") +async def _deserialize_import_config_json(http_response: HTTPResponse, config: Config) -> ImportConfigJsonOutput: + if http_response.status != 200 and http_response.status >= 300: + raise await _deserialize_error_import_config_json(http_response, config) + + kwargs: dict[str, Any] = {} + + body = await http_response.consume_body_async() + if body: + codec = JSONCodec(default_timestamp_format=TimestampFormat.EPOCH_SECONDS) + deserializer = codec.create_deserializer(body) + body_kwargs = ImportConfigJsonOutput.deserialize_kwargs(deserializer) + kwargs.update(body_kwargs) + + return ImportConfigJsonOutput(**kwargs) + +async def _deserialize_error_import_config_json(http_response: HTTPResponse, config: Config) -> ApiError: + code, message, parsed_body = await parse_rest_json_error_info(http_response) + + match code.lower(): + case "internalservererror": + return await _deserialize_error_internal_server_error(http_response, config, parsed_body, message) + + case _: + return UnknownApiError(f"{code}: {message}") + +async def _deserialize_import_config_toml(http_response: HTTPResponse, config: Config) -> ImportConfigTomlOutput: + if http_response.status != 200 and http_response.status >= 300: + raise await _deserialize_error_import_config_toml(http_response, config) + + kwargs: dict[str, Any] = {} + + body = await http_response.consume_body_async() + if body: + codec = JSONCodec(default_timestamp_format=TimestampFormat.EPOCH_SECONDS) + deserializer = codec.create_deserializer(body) + body_kwargs = ImportConfigTomlOutput.deserialize_kwargs(deserializer) + kwargs.update(body_kwargs) + + return ImportConfigTomlOutput(**kwargs) + +async def _deserialize_error_import_config_toml(http_response: HTTPResponse, config: Config) -> ApiError: + code, message, parsed_body = await parse_rest_json_error_info(http_response) + + match code.lower(): + case "internalservererror": + return await _deserialize_error_internal_server_error(http_response, config, parsed_body, message) + + case _: + return UnknownApiError(f"{code}: {message}") + async def _deserialize_list_audit_logs(http_response: HTTPResponse, config: Config) -> ListAuditLogsOutput: if http_response.status != 200 and http_response.status >= 300: raise await _deserialize_error_list_audit_logs(http_response, config) diff --git a/clients/python/sdk/superposition_sdk/models.py b/clients/python/sdk/superposition_sdk/models.py index 7787f10d3..54982aa34 100644 --- a/clients/python/sdk/superposition_sdk/models.py +++ b/clients/python/sdk/superposition_sdk/models.py @@ -186,6 +186,14 @@ GET_WORKSPACE as _SCHEMA_GET_WORKSPACE, GET_WORKSPACE_INPUT as _SCHEMA_GET_WORKSPACE_INPUT, GET_WORKSPACE_OUTPUT as _SCHEMA_GET_WORKSPACE_OUTPUT, + IMPORT_CONFIG_JSON as _SCHEMA_IMPORT_CONFIG_JSON, + IMPORT_CONFIG_JSON_INPUT as _SCHEMA_IMPORT_CONFIG_JSON_INPUT, + IMPORT_CONFIG_JSON_OUTPUT as _SCHEMA_IMPORT_CONFIG_JSON_OUTPUT, + IMPORT_CONFIG_TOML as _SCHEMA_IMPORT_CONFIG_TOML, + IMPORT_CONFIG_TOML_INPUT as _SCHEMA_IMPORT_CONFIG_TOML_INPUT, + IMPORT_CONFIG_TOML_OUTPUT as _SCHEMA_IMPORT_CONFIG_TOML_OUTPUT, + IMPORT_ENTITY_REPORT as _SCHEMA_IMPORT_ENTITY_REPORT, + IMPORT_ERROR_ITEM as _SCHEMA_IMPORT_ERROR_ITEM, INTERNAL_SERVER_ERROR as _SCHEMA_INTERNAL_SERVER_ERROR, LIST_AUDIT_LOGS as _SCHEMA_LIST_AUDIT_LOGS, LIST_AUDIT_LOGS_INPUT as _SCHEMA_LIST_AUDIT_LOGS_INPUT, @@ -15252,6 +15260,498 @@ def _consumer(schema: Schema, de: ShapeDeserializer) -> None: ] ) +class ImportMode(StrEnum): + """ + How an import treats workspace entities that are not present in the imported + file. + + """ + MERGE = "merge" + """ + Upsert the entities in the file and leave everything else untouched. + + """ + REPLACE = "replace" + """ + Mirror the file: additionally delete dimensions, default-configs and contexts + that are absent from it. + + """ + +class ImportOnError(StrEnum): + """ + How an import reacts when an individual entity fails to apply. + + """ + ABORT = "abort" + """ + Roll the whole import back on the first error. + + """ + CONTINUE_ = "continue" + """ + Apply everything that is valid and report per-entity errors. + + """ + +@dataclass(kw_only=True) +class ImportConfigJsonInput: + """ + + :param mode: + Whether to merge (default) or replace existing workspace config. + + :param overwrite: + When false, entities that already exist are skipped instead of updated. Defaults + to true. + + :param on_error: + Whether to abort (default) or continue on per-entity errors. + + :param dry_run: + When true, validates and summarises the import without persisting anything. + Defaults to false. + + :param value_merge: + When true, deep-merges object-valued default-configs with the existing value. + Defaults to false. + + """ + + workspace_id: str | None = None + org_id: str | None = None + mode: str | None = None + overwrite: bool | None = None + on_error: str | None = None + dry_run: bool | None = None + value_merge: bool | None = None + config_tags: str | None = None + json_config: str | None = None + + def serialize(self, serializer: ShapeSerializer): + serializer.write_struct(_SCHEMA_IMPORT_CONFIG_JSON_INPUT, self) + + def serialize_members(self, serializer: ShapeSerializer): + pass + + @classmethod + def deserialize(cls, deserializer: ShapeDeserializer) -> Self: + return cls(**cls.deserialize_kwargs(deserializer)) + + @classmethod + def deserialize_kwargs(cls, deserializer: ShapeDeserializer) -> dict[str, Any]: + kwargs: dict[str, Any] = {} + + def _consumer(schema: Schema, de: ShapeDeserializer) -> None: + match schema.expect_member_index(): + case 0: + kwargs["workspace_id"] = de.read_string(_SCHEMA_IMPORT_CONFIG_JSON_INPUT.members["workspace_id"]) + + case 1: + kwargs["org_id"] = de.read_string(_SCHEMA_IMPORT_CONFIG_JSON_INPUT.members["org_id"]) + + case 2: + kwargs["mode"] = de.read_string(_SCHEMA_IMPORT_CONFIG_JSON_INPUT.members["mode"]) + + case 3: + kwargs["overwrite"] = de.read_boolean(_SCHEMA_IMPORT_CONFIG_JSON_INPUT.members["overwrite"]) + + case 4: + kwargs["on_error"] = de.read_string(_SCHEMA_IMPORT_CONFIG_JSON_INPUT.members["on_error"]) + + case 5: + kwargs["dry_run"] = de.read_boolean(_SCHEMA_IMPORT_CONFIG_JSON_INPUT.members["dry_run"]) + + case 6: + kwargs["value_merge"] = de.read_boolean(_SCHEMA_IMPORT_CONFIG_JSON_INPUT.members["value_merge"]) + + case 7: + kwargs["config_tags"] = de.read_string(_SCHEMA_IMPORT_CONFIG_JSON_INPUT.members["config_tags"]) + + case 8: + kwargs["json_config"] = de.read_string(_SCHEMA_IMPORT_CONFIG_JSON_INPUT.members["json_config"]) + + case _: + logger.debug("Unexpected member schema: %s", schema) + + deserializer.read_struct(_SCHEMA_IMPORT_CONFIG_JSON_INPUT, consumer=_consumer) + return kwargs + +@dataclass(kw_only=True) +class ImportErrorItem: + + id: str + + error: str + + def serialize(self, serializer: ShapeSerializer): + serializer.write_struct(_SCHEMA_IMPORT_ERROR_ITEM, self) + + def serialize_members(self, serializer: ShapeSerializer): + serializer.write_string(_SCHEMA_IMPORT_ERROR_ITEM.members["id"], self.id) + serializer.write_string(_SCHEMA_IMPORT_ERROR_ITEM.members["error"], self.error) + + @classmethod + def deserialize(cls, deserializer: ShapeDeserializer) -> Self: + return cls(**cls.deserialize_kwargs(deserializer)) + + @classmethod + def deserialize_kwargs(cls, deserializer: ShapeDeserializer) -> dict[str, Any]: + kwargs: dict[str, Any] = {} + + def _consumer(schema: Schema, de: ShapeDeserializer) -> None: + match schema.expect_member_index(): + case 0: + kwargs["id"] = de.read_string(_SCHEMA_IMPORT_ERROR_ITEM.members["id"]) + + case 1: + kwargs["error"] = de.read_string(_SCHEMA_IMPORT_ERROR_ITEM.members["error"]) + + case _: + logger.debug("Unexpected member schema: %s", schema) + + deserializer.read_struct(_SCHEMA_IMPORT_ERROR_ITEM, consumer=_consumer) + return kwargs + +def _serialize_import_error_list(serializer: ShapeSerializer, schema: Schema, value: list[ImportErrorItem]) -> None: + member_schema = schema.members["member"] + with serializer.begin_list(schema, len(value)) as ls: + for e in value: + ls.write_struct(member_schema, e) + +def _deserialize_import_error_list(deserializer: ShapeDeserializer, schema: Schema) -> list[ImportErrorItem]: + result: list[ImportErrorItem] = [] + def _read_value(d: ShapeDeserializer): + if d.is_null(): + d.read_null() + + else: + result.append(ImportErrorItem.deserialize(d)) + deserializer.read_list(schema, _read_value) + return result + +@dataclass(kw_only=True) +class ImportEntityReport: + """ + Per-entity outcome counts for an import. + + """ + + created: int + + updated: int + + skipped: int + + deleted: int + + errors: list[ImportErrorItem] | None = None + + def serialize(self, serializer: ShapeSerializer): + serializer.write_struct(_SCHEMA_IMPORT_ENTITY_REPORT, self) + + def serialize_members(self, serializer: ShapeSerializer): + serializer.write_integer(_SCHEMA_IMPORT_ENTITY_REPORT.members["created"], self.created) + serializer.write_integer(_SCHEMA_IMPORT_ENTITY_REPORT.members["updated"], self.updated) + serializer.write_integer(_SCHEMA_IMPORT_ENTITY_REPORT.members["skipped"], self.skipped) + serializer.write_integer(_SCHEMA_IMPORT_ENTITY_REPORT.members["deleted"], self.deleted) + if self.errors is not None: + _serialize_import_error_list(serializer, _SCHEMA_IMPORT_ENTITY_REPORT.members["errors"], self.errors) + + @classmethod + def deserialize(cls, deserializer: ShapeDeserializer) -> Self: + return cls(**cls.deserialize_kwargs(deserializer)) + + @classmethod + def deserialize_kwargs(cls, deserializer: ShapeDeserializer) -> dict[str, Any]: + kwargs: dict[str, Any] = {} + + def _consumer(schema: Schema, de: ShapeDeserializer) -> None: + match schema.expect_member_index(): + case 0: + kwargs["created"] = de.read_integer(_SCHEMA_IMPORT_ENTITY_REPORT.members["created"]) + + case 1: + kwargs["updated"] = de.read_integer(_SCHEMA_IMPORT_ENTITY_REPORT.members["updated"]) + + case 2: + kwargs["skipped"] = de.read_integer(_SCHEMA_IMPORT_ENTITY_REPORT.members["skipped"]) + + case 3: + kwargs["deleted"] = de.read_integer(_SCHEMA_IMPORT_ENTITY_REPORT.members["deleted"]) + + case 4: + kwargs["errors"] = _deserialize_import_error_list(de, _SCHEMA_IMPORT_ENTITY_REPORT.members["errors"]) + + case _: + logger.debug("Unexpected member schema: %s", schema) + + deserializer.read_struct(_SCHEMA_IMPORT_ENTITY_REPORT, consumer=_consumer) + return kwargs + +@dataclass(kw_only=True) +class ImportConfigJsonOutput: + """ + Summary of what an import created, updated, skipped or deleted. + + :param dimensions: + **[Required]** - Per-entity outcome counts for an import. + + :param default_configs: + **[Required]** - Per-entity outcome counts for an import. + + :param contexts: + **[Required]** - Per-entity outcome counts for an import. + + """ + + mode: str + + dry_run: bool + + dimensions: ImportEntityReport + + default_configs: ImportEntityReport + + contexts: ImportEntityReport + + config_version: str | None = None + + def serialize(self, serializer: ShapeSerializer): + serializer.write_struct(_SCHEMA_IMPORT_CONFIG_JSON_OUTPUT, self) + + def serialize_members(self, serializer: ShapeSerializer): + serializer.write_string(_SCHEMA_IMPORT_CONFIG_JSON_OUTPUT.members["mode"], self.mode) + serializer.write_boolean(_SCHEMA_IMPORT_CONFIG_JSON_OUTPUT.members["dry_run"], self.dry_run) + if self.config_version is not None: + serializer.write_string(_SCHEMA_IMPORT_CONFIG_JSON_OUTPUT.members["config_version"], self.config_version) + + serializer.write_struct(_SCHEMA_IMPORT_CONFIG_JSON_OUTPUT.members["dimensions"], self.dimensions) + serializer.write_struct(_SCHEMA_IMPORT_CONFIG_JSON_OUTPUT.members["default_configs"], self.default_configs) + serializer.write_struct(_SCHEMA_IMPORT_CONFIG_JSON_OUTPUT.members["contexts"], self.contexts) + + @classmethod + def deserialize(cls, deserializer: ShapeDeserializer) -> Self: + return cls(**cls.deserialize_kwargs(deserializer)) + + @classmethod + def deserialize_kwargs(cls, deserializer: ShapeDeserializer) -> dict[str, Any]: + kwargs: dict[str, Any] = {} + + def _consumer(schema: Schema, de: ShapeDeserializer) -> None: + match schema.expect_member_index(): + case 0: + kwargs["mode"] = de.read_string(_SCHEMA_IMPORT_CONFIG_JSON_OUTPUT.members["mode"]) + + case 1: + kwargs["dry_run"] = de.read_boolean(_SCHEMA_IMPORT_CONFIG_JSON_OUTPUT.members["dry_run"]) + + case 2: + kwargs["config_version"] = de.read_string(_SCHEMA_IMPORT_CONFIG_JSON_OUTPUT.members["config_version"]) + + case 3: + kwargs["dimensions"] = ImportEntityReport.deserialize(de) + + case 4: + kwargs["default_configs"] = ImportEntityReport.deserialize(de) + + case 5: + kwargs["contexts"] = ImportEntityReport.deserialize(de) + + case _: + logger.debug("Unexpected member schema: %s", schema) + + deserializer.read_struct(_SCHEMA_IMPORT_CONFIG_JSON_OUTPUT, consumer=_consumer) + return kwargs + +IMPORT_CONFIG_JSON = APIOperation( + input = ImportConfigJsonInput, + output = ImportConfigJsonOutput, + schema = _SCHEMA_IMPORT_CONFIG_JSON, + input_schema = _SCHEMA_IMPORT_CONFIG_JSON_INPUT, + output_schema = _SCHEMA_IMPORT_CONFIG_JSON_OUTPUT, + error_registry = TypeRegistry({ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ + ShapeID("smithy.api#httpBasicAuth"), +ShapeID("smithy.api#httpBearerAuth") + ] +) + +@dataclass(kw_only=True) +class ImportConfigTomlInput: + """ + + :param mode: + Whether to merge (default) or replace existing workspace config. + + :param overwrite: + When false, entities that already exist are skipped instead of updated. Defaults + to true. + + :param on_error: + Whether to abort (default) or continue on per-entity errors. + + :param dry_run: + When true, validates and summarises the import without persisting anything. + Defaults to false. + + :param value_merge: + When true, deep-merges object-valued default-configs with the existing value. + Defaults to false. + + """ + + workspace_id: str | None = None + org_id: str | None = None + mode: str | None = None + overwrite: bool | None = None + on_error: str | None = None + dry_run: bool | None = None + value_merge: bool | None = None + config_tags: str | None = None + toml_config: str | None = None + + def serialize(self, serializer: ShapeSerializer): + serializer.write_struct(_SCHEMA_IMPORT_CONFIG_TOML_INPUT, self) + + def serialize_members(self, serializer: ShapeSerializer): + pass + + @classmethod + def deserialize(cls, deserializer: ShapeDeserializer) -> Self: + return cls(**cls.deserialize_kwargs(deserializer)) + + @classmethod + def deserialize_kwargs(cls, deserializer: ShapeDeserializer) -> dict[str, Any]: + kwargs: dict[str, Any] = {} + + def _consumer(schema: Schema, de: ShapeDeserializer) -> None: + match schema.expect_member_index(): + case 0: + kwargs["workspace_id"] = de.read_string(_SCHEMA_IMPORT_CONFIG_TOML_INPUT.members["workspace_id"]) + + case 1: + kwargs["org_id"] = de.read_string(_SCHEMA_IMPORT_CONFIG_TOML_INPUT.members["org_id"]) + + case 2: + kwargs["mode"] = de.read_string(_SCHEMA_IMPORT_CONFIG_TOML_INPUT.members["mode"]) + + case 3: + kwargs["overwrite"] = de.read_boolean(_SCHEMA_IMPORT_CONFIG_TOML_INPUT.members["overwrite"]) + + case 4: + kwargs["on_error"] = de.read_string(_SCHEMA_IMPORT_CONFIG_TOML_INPUT.members["on_error"]) + + case 5: + kwargs["dry_run"] = de.read_boolean(_SCHEMA_IMPORT_CONFIG_TOML_INPUT.members["dry_run"]) + + case 6: + kwargs["value_merge"] = de.read_boolean(_SCHEMA_IMPORT_CONFIG_TOML_INPUT.members["value_merge"]) + + case 7: + kwargs["config_tags"] = de.read_string(_SCHEMA_IMPORT_CONFIG_TOML_INPUT.members["config_tags"]) + + case 8: + kwargs["toml_config"] = de.read_string(_SCHEMA_IMPORT_CONFIG_TOML_INPUT.members["toml_config"]) + + case _: + logger.debug("Unexpected member schema: %s", schema) + + deserializer.read_struct(_SCHEMA_IMPORT_CONFIG_TOML_INPUT, consumer=_consumer) + return kwargs + +@dataclass(kw_only=True) +class ImportConfigTomlOutput: + """ + Summary of what an import created, updated, skipped or deleted. + + :param dimensions: + **[Required]** - Per-entity outcome counts for an import. + + :param default_configs: + **[Required]** - Per-entity outcome counts for an import. + + :param contexts: + **[Required]** - Per-entity outcome counts for an import. + + """ + + mode: str + + dry_run: bool + + dimensions: ImportEntityReport + + default_configs: ImportEntityReport + + contexts: ImportEntityReport + + config_version: str | None = None + + def serialize(self, serializer: ShapeSerializer): + serializer.write_struct(_SCHEMA_IMPORT_CONFIG_TOML_OUTPUT, self) + + def serialize_members(self, serializer: ShapeSerializer): + serializer.write_string(_SCHEMA_IMPORT_CONFIG_TOML_OUTPUT.members["mode"], self.mode) + serializer.write_boolean(_SCHEMA_IMPORT_CONFIG_TOML_OUTPUT.members["dry_run"], self.dry_run) + if self.config_version is not None: + serializer.write_string(_SCHEMA_IMPORT_CONFIG_TOML_OUTPUT.members["config_version"], self.config_version) + + serializer.write_struct(_SCHEMA_IMPORT_CONFIG_TOML_OUTPUT.members["dimensions"], self.dimensions) + serializer.write_struct(_SCHEMA_IMPORT_CONFIG_TOML_OUTPUT.members["default_configs"], self.default_configs) + serializer.write_struct(_SCHEMA_IMPORT_CONFIG_TOML_OUTPUT.members["contexts"], self.contexts) + + @classmethod + def deserialize(cls, deserializer: ShapeDeserializer) -> Self: + return cls(**cls.deserialize_kwargs(deserializer)) + + @classmethod + def deserialize_kwargs(cls, deserializer: ShapeDeserializer) -> dict[str, Any]: + kwargs: dict[str, Any] = {} + + def _consumer(schema: Schema, de: ShapeDeserializer) -> None: + match schema.expect_member_index(): + case 0: + kwargs["mode"] = de.read_string(_SCHEMA_IMPORT_CONFIG_TOML_OUTPUT.members["mode"]) + + case 1: + kwargs["dry_run"] = de.read_boolean(_SCHEMA_IMPORT_CONFIG_TOML_OUTPUT.members["dry_run"]) + + case 2: + kwargs["config_version"] = de.read_string(_SCHEMA_IMPORT_CONFIG_TOML_OUTPUT.members["config_version"]) + + case 3: + kwargs["dimensions"] = ImportEntityReport.deserialize(de) + + case 4: + kwargs["default_configs"] = ImportEntityReport.deserialize(de) + + case 5: + kwargs["contexts"] = ImportEntityReport.deserialize(de) + + case _: + logger.debug("Unexpected member schema: %s", schema) + + deserializer.read_struct(_SCHEMA_IMPORT_CONFIG_TOML_OUTPUT, consumer=_consumer) + return kwargs + +IMPORT_CONFIG_TOML = APIOperation( + input = ImportConfigTomlInput, + output = ImportConfigTomlOutput, + schema = _SCHEMA_IMPORT_CONFIG_TOML, + input_schema = _SCHEMA_IMPORT_CONFIG_TOML_INPUT, + output_schema = _SCHEMA_IMPORT_CONFIG_TOML_OUTPUT, + error_registry = TypeRegistry({ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ + ShapeID("smithy.api#httpBasicAuth"), +ShapeID("smithy.api#httpBearerAuth") + ] +) + @dataclass(kw_only=True) class ListOrganisationInput: """ @@ -16568,7 +17068,7 @@ def _consumer(schema: Schema, de: ShapeDeserializer) -> None: ShapeID("io.superposition#InternalServerError"): InternalServerError, }), effective_auth_schemes = [ - ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBasicAuth") ShapeID("smithy.api#httpBearerAuth") ] ) @@ -17032,7 +17532,7 @@ def _consumer(schema: Schema, de: ShapeDeserializer) -> None: ShapeID("io.superposition#InternalServerError"): InternalServerError, }), effective_auth_schemes = [ - ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBasicAuth") ShapeID("smithy.api#httpBearerAuth") ] ) diff --git a/clients/python/sdk/superposition_sdk/serialize.py b/clients/python/sdk/superposition_sdk/serialize.py index 6883b5c96..4d6a6db12 100644 --- a/clients/python/sdk/superposition_sdk/serialize.py +++ b/clients/python/sdk/superposition_sdk/serialize.py @@ -65,6 +65,8 @@ GetWebhookByEventInput, GetWebhookInput, GetWorkspaceInput, + ImportConfigJsonInput, + ImportConfigTomlInput, ListAuditLogsInput, ListContextsInput, ListDefaultConfigsInput, @@ -1917,6 +1919,98 @@ async def _serialize_get_workspace(input: GetWorkspaceInput, config: Config) -> body=body, ) +async def _serialize_import_config_json(input: ImportConfigJsonInput, config: Config) -> HTTPRequest: + path = "/config/json/import" + query: str = f'' + + body: AsyncIterable[bytes] = AsyncBytesReader(b'') + content_length: int = 0 + if input.json_config is not None: + content = input.json_config.encode('utf-8') + content_length = len(content) + body = SeekableAsyncBytesReader(content) + headers = Fields( + [ + Field(name="Content-Type", values=["text/plain"]), + Field(name="Content-Length", values=[str(content_length)]), + + ] + ) + + if input.workspace_id: + headers.extend(Fields([Field(name="x-workspace", values=[input.workspace_id])])) + if input.org_id: + headers.extend(Fields([Field(name="x-org-id", values=[input.org_id])])) + if input.mode: + headers.extend(Fields([Field(name="x-import-mode", values=[input.mode])])) + if input.overwrite is not None: + headers.extend(Fields([Field(name="x-import-overwrite", values=[('true' if input.overwrite else 'false')])])) + if input.on_error: + headers.extend(Fields([Field(name="x-import-on-error", values=[input.on_error])])) + if input.dry_run is not None: + headers.extend(Fields([Field(name="x-import-dry-run", values=[('true' if input.dry_run else 'false')])])) + if input.value_merge is not None: + headers.extend(Fields([Field(name="x-import-value-merge", values=[('true' if input.value_merge else 'false')])])) + if input.config_tags: + headers.extend(Fields([Field(name="x-config-tags", values=[input.config_tags])])) + return _HTTPRequest( + destination=_URI( + host="", + path=path, + scheme="https", + query=query, + ), + method="POST", + fields=headers, + body=body, + ) + +async def _serialize_import_config_toml(input: ImportConfigTomlInput, config: Config) -> HTTPRequest: + path = "/config/toml/import" + query: str = f'' + + body: AsyncIterable[bytes] = AsyncBytesReader(b'') + content_length: int = 0 + if input.toml_config is not None: + content = input.toml_config.encode('utf-8') + content_length = len(content) + body = SeekableAsyncBytesReader(content) + headers = Fields( + [ + Field(name="Content-Type", values=["text/plain"]), + Field(name="Content-Length", values=[str(content_length)]), + + ] + ) + + if input.workspace_id: + headers.extend(Fields([Field(name="x-workspace", values=[input.workspace_id])])) + if input.org_id: + headers.extend(Fields([Field(name="x-org-id", values=[input.org_id])])) + if input.mode: + headers.extend(Fields([Field(name="x-import-mode", values=[input.mode])])) + if input.overwrite is not None: + headers.extend(Fields([Field(name="x-import-overwrite", values=[('true' if input.overwrite else 'false')])])) + if input.on_error: + headers.extend(Fields([Field(name="x-import-on-error", values=[input.on_error])])) + if input.dry_run is not None: + headers.extend(Fields([Field(name="x-import-dry-run", values=[('true' if input.dry_run else 'false')])])) + if input.value_merge is not None: + headers.extend(Fields([Field(name="x-import-value-merge", values=[('true' if input.value_merge else 'false')])])) + if input.config_tags: + headers.extend(Fields([Field(name="x-config-tags", values=[input.config_tags])])) + return _HTTPRequest( + destination=_URI( + host="", + path=path, + scheme="https", + query=query, + ), + method="POST", + fields=headers, + body=body, + ) + async def _serialize_list_audit_logs(input: ListAuditLogsInput, config: Config) -> HTTPRequest: path = "/audit" query: str = f'' diff --git a/crates/superposition_sdk/src/client.rs b/crates/superposition_sdk/src/client.rs index 6ba35151b..a603931ff 100644 --- a/crates/superposition_sdk/src/client.rs +++ b/crates/superposition_sdk/src/client.rs @@ -231,6 +231,10 @@ mod get_webhook_by_event; mod get_workspace; +mod import_config_json; + +mod import_config_toml; + mod list_audit_logs; mod list_contexts; diff --git a/crates/superposition_sdk/src/client/import_config_json.rs b/crates/superposition_sdk/src/client/import_config_json.rs new file mode 100644 index 000000000..ca296b2c9 --- /dev/null +++ b/crates/superposition_sdk/src/client/import_config_json.rs @@ -0,0 +1,27 @@ +// Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT. +impl super::Client { + /// Constructs a fluent builder for the [`ImportConfigJson`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder) operation. + /// + /// - The fluent builder is configurable: + /// - [`workspace_id(impl Into)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::workspace_id) / [`set_workspace_id(Option)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::set_workspace_id):
required: **true**
(undocumented)
+ /// - [`org_id(impl Into)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::org_id) / [`set_org_id(Option)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::set_org_id):
required: **true**
(undocumented)
+ /// - [`mode(ImportMode)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::mode) / [`set_mode(Option)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::set_mode):
required: **false**
Whether to merge (default) or replace existing workspace config.
+ /// - [`overwrite(bool)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::overwrite) / [`set_overwrite(Option)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::set_overwrite):
required: **false**
When false, entities that already exist are skipped instead of updated. Defaults to true.
+ /// - [`on_error(ImportOnError)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::on_error) / [`set_on_error(Option)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::set_on_error):
required: **false**
Whether to abort (default) or continue on per-entity errors.
+ /// - [`dry_run(bool)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::dry_run) / [`set_dry_run(Option)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::set_dry_run):
required: **false**
When true, validates and summarises the import without persisting anything. Defaults to false.
+ /// - [`value_merge(bool)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::value_merge) / [`set_value_merge(Option)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::set_value_merge):
required: **false**
When true, deep-merges object-valued default-configs with the existing value. Defaults to false.
+ /// - [`config_tags(impl Into)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::config_tags) / [`set_config_tags(Option)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::set_config_tags):
required: **false**
(undocumented)
+ /// - [`json_config(impl Into)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::json_config) / [`set_json_config(Option)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::set_json_config):
required: **true**
(undocumented)
+ /// - On success, responds with [`ImportConfigJsonOutput`](crate::operation::import_config_json::ImportConfigJsonOutput) with field(s): + /// - [`mode(String)`](crate::operation::import_config_json::ImportConfigJsonOutput::mode): (undocumented) + /// - [`dry_run(bool)`](crate::operation::import_config_json::ImportConfigJsonOutput::dry_run): (undocumented) + /// - [`config_version(Option)`](crate::operation::import_config_json::ImportConfigJsonOutput::config_version): (undocumented) + /// - [`dimensions(ImportEntityReport)`](crate::operation::import_config_json::ImportConfigJsonOutput::dimensions): Per-entity outcome counts for an import. + /// - [`default_configs(ImportEntityReport)`](crate::operation::import_config_json::ImportConfigJsonOutput::default_configs): Per-entity outcome counts for an import. + /// - [`contexts(ImportEntityReport)`](crate::operation::import_config_json::ImportConfigJsonOutput::contexts): Per-entity outcome counts for an import. + /// - On failure, responds with [`SdkError`](crate::operation::import_config_json::ImportConfigJsonError) + pub fn import_config_json(&self) -> crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder { + crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::new(self.handle.clone()) + } +} + diff --git a/crates/superposition_sdk/src/client/import_config_toml.rs b/crates/superposition_sdk/src/client/import_config_toml.rs new file mode 100644 index 000000000..8acfedd4d --- /dev/null +++ b/crates/superposition_sdk/src/client/import_config_toml.rs @@ -0,0 +1,27 @@ +// Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT. +impl super::Client { + /// Constructs a fluent builder for the [`ImportConfigToml`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder) operation. + /// + /// - The fluent builder is configurable: + /// - [`workspace_id(impl Into)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::workspace_id) / [`set_workspace_id(Option)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::set_workspace_id):
required: **true**
(undocumented)
+ /// - [`org_id(impl Into)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::org_id) / [`set_org_id(Option)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::set_org_id):
required: **true**
(undocumented)
+ /// - [`mode(ImportMode)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::mode) / [`set_mode(Option)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::set_mode):
required: **false**
Whether to merge (default) or replace existing workspace config.
+ /// - [`overwrite(bool)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::overwrite) / [`set_overwrite(Option)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::set_overwrite):
required: **false**
When false, entities that already exist are skipped instead of updated. Defaults to true.
+ /// - [`on_error(ImportOnError)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::on_error) / [`set_on_error(Option)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::set_on_error):
required: **false**
Whether to abort (default) or continue on per-entity errors.
+ /// - [`dry_run(bool)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::dry_run) / [`set_dry_run(Option)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::set_dry_run):
required: **false**
When true, validates and summarises the import without persisting anything. Defaults to false.
+ /// - [`value_merge(bool)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::value_merge) / [`set_value_merge(Option)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::set_value_merge):
required: **false**
When true, deep-merges object-valued default-configs with the existing value. Defaults to false.
+ /// - [`config_tags(impl Into)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::config_tags) / [`set_config_tags(Option)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::set_config_tags):
required: **false**
(undocumented)
+ /// - [`toml_config(impl Into)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::toml_config) / [`set_toml_config(Option)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::set_toml_config):
required: **true**
(undocumented)
+ /// - On success, responds with [`ImportConfigTomlOutput`](crate::operation::import_config_toml::ImportConfigTomlOutput) with field(s): + /// - [`mode(String)`](crate::operation::import_config_toml::ImportConfigTomlOutput::mode): (undocumented) + /// - [`dry_run(bool)`](crate::operation::import_config_toml::ImportConfigTomlOutput::dry_run): (undocumented) + /// - [`config_version(Option)`](crate::operation::import_config_toml::ImportConfigTomlOutput::config_version): (undocumented) + /// - [`dimensions(ImportEntityReport)`](crate::operation::import_config_toml::ImportConfigTomlOutput::dimensions): Per-entity outcome counts for an import. + /// - [`default_configs(ImportEntityReport)`](crate::operation::import_config_toml::ImportConfigTomlOutput::default_configs): Per-entity outcome counts for an import. + /// - [`contexts(ImportEntityReport)`](crate::operation::import_config_toml::ImportConfigTomlOutput::contexts): Per-entity outcome counts for an import. + /// - On failure, responds with [`SdkError`](crate::operation::import_config_toml::ImportConfigTomlError) + pub fn import_config_toml(&self) -> crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder { + crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::new(self.handle.clone()) + } +} + diff --git a/crates/superposition_sdk/src/error_meta.rs b/crates/superposition_sdk/src/error_meta.rs index c2f3eb96b..ad8dfcf9b 100644 --- a/crates/superposition_sdk/src/error_meta.rs +++ b/crates/superposition_sdk/src/error_meta.rs @@ -1142,6 +1142,48 @@ impl From for Error { } } } +impl From<::aws_smithy_runtime_api::client::result::SdkError> for Error where R: Send + Sync + std::fmt::Debug + 'static { + fn from(err: ::aws_smithy_runtime_api::client::result::SdkError) -> Self { + match err { + ::aws_smithy_runtime_api::client::result::SdkError::ServiceError(context) => Self::from(context.into_err()), + _ => Error::Unhandled( + crate::error::sealed_unhandled::Unhandled { + meta: ::aws_smithy_types::error::metadata::ProvideErrorMetadata::meta(&err).clone(), + source: err.into(), + } + ), + } + } +} +impl From for Error { + fn from(err: crate::operation::import_config_json::ImportConfigJsonError) -> Self { + match err { + crate::operation::import_config_json::ImportConfigJsonError::InternalServerError(inner) => Error::InternalServerError(inner), + crate::operation::import_config_json::ImportConfigJsonError::Unhandled(inner) => Error::Unhandled(inner), + } + } +} +impl From<::aws_smithy_runtime_api::client::result::SdkError> for Error where R: Send + Sync + std::fmt::Debug + 'static { + fn from(err: ::aws_smithy_runtime_api::client::result::SdkError) -> Self { + match err { + ::aws_smithy_runtime_api::client::result::SdkError::ServiceError(context) => Self::from(context.into_err()), + _ => Error::Unhandled( + crate::error::sealed_unhandled::Unhandled { + meta: ::aws_smithy_types::error::metadata::ProvideErrorMetadata::meta(&err).clone(), + source: err.into(), + } + ), + } + } +} +impl From for Error { + fn from(err: crate::operation::import_config_toml::ImportConfigTomlError) -> Self { + match err { + crate::operation::import_config_toml::ImportConfigTomlError::InternalServerError(inner) => Error::InternalServerError(inner), + crate::operation::import_config_toml::ImportConfigTomlError::Unhandled(inner) => Error::Unhandled(inner), + } + } +} impl From<::aws_smithy_runtime_api::client::result::SdkError> for Error where R: Send + Sync + std::fmt::Debug + 'static { fn from(err: ::aws_smithy_runtime_api::client::result::SdkError) -> Self { match err { diff --git a/crates/superposition_sdk/src/operation.rs b/crates/superposition_sdk/src/operation.rs index 2381c0260..037f520ba 100644 --- a/crates/superposition_sdk/src/operation.rs +++ b/crates/superposition_sdk/src/operation.rs @@ -150,6 +150,12 @@ pub mod get_webhook_by_event; /// Types for the `GetWorkspace` operation. pub mod get_workspace; +/// Types for the `ImportConfigJson` operation. +pub mod import_config_json; + +/// Types for the `ImportConfigToml` operation. +pub mod import_config_toml; + /// Types for the `ListAuditLogs` operation. pub mod list_audit_logs; diff --git a/crates/superposition_sdk/src/operation/import_config_json.rs b/crates/superposition_sdk/src/operation/import_config_json.rs new file mode 100644 index 000000000..110d6040b --- /dev/null +++ b/crates/superposition_sdk/src/operation/import_config_json.rs @@ -0,0 +1,299 @@ +// Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT. +/// Orchestration and serialization glue logic for `ImportConfigJson`. +#[derive(::std::clone::Clone, ::std::default::Default, ::std::fmt::Debug)] +#[non_exhaustive] +pub struct ImportConfigJson; +impl ImportConfigJson { + /// Creates a new `ImportConfigJson` + pub fn new() -> Self { + Self + } + pub(crate) async fn orchestrate( + runtime_plugins: &::aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugins, + input: crate::operation::import_config_json::ImportConfigJsonInput, + ) -> ::std::result::Result> { + let map_err = |err: ::aws_smithy_runtime_api::client::result::SdkError<::aws_smithy_runtime_api::client::interceptors::context::Error, ::aws_smithy_runtime_api::client::orchestrator::HttpResponse>| { + err.map_service_error(|err| { + err.downcast::().expect("correct error type") + }) + }; + let context = Self::orchestrate_with_stop_point(runtime_plugins, input, ::aws_smithy_runtime::client::orchestrator::StopPoint::None) + .await + .map_err(map_err)?; + let output = context.finalize().map_err(map_err)?; + ::std::result::Result::Ok(output.downcast::().expect("correct output type")) + } + + pub(crate) async fn orchestrate_with_stop_point( + runtime_plugins: &::aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugins, + input: crate::operation::import_config_json::ImportConfigJsonInput, + stop_point: ::aws_smithy_runtime::client::orchestrator::StopPoint, + ) -> ::std::result::Result<::aws_smithy_runtime_api::client::interceptors::context::InterceptorContext, ::aws_smithy_runtime_api::client::result::SdkError<::aws_smithy_runtime_api::client::interceptors::context::Error, ::aws_smithy_runtime_api::client::orchestrator::HttpResponse>> { + let input = ::aws_smithy_runtime_api::client::interceptors::context::Input::erase(input); + use ::tracing::Instrument; + ::aws_smithy_runtime::client::orchestrator::invoke_with_stop_point( + "Superposition", + "ImportConfigJson", + input, + runtime_plugins, + stop_point + ) + // Create a parent span for the entire operation. Includes a random, internal-only, + // seven-digit ID for the operation orchestration so that it can be correlated in the logs. + .instrument(::tracing::debug_span!( + "Superposition.ImportConfigJson", + "rpc.service" = "Superposition", + "rpc.method" = "ImportConfigJson", + "sdk_invocation_id" = ::fastrand::u32(1_000_000..10_000_000), + + )) + .await + } + + pub(crate) fn operation_runtime_plugins( + client_runtime_plugins: ::aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugins, + client_config: &crate::config::Config, + config_override: ::std::option::Option, + ) -> ::aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugins { + let mut runtime_plugins = client_runtime_plugins.with_operation_plugin(Self::new()); + runtime_plugins = runtime_plugins + .with_client_plugin(crate::auth_plugin::DefaultAuthOptionsPlugin::new(vec![::aws_smithy_runtime_api::client::auth::http::HTTP_BASIC_AUTH_SCHEME_ID + , ::aws_smithy_runtime_api::client::auth::http::HTTP_BEARER_AUTH_SCHEME_ID])); + if let ::std::option::Option::Some(config_override) = config_override { + for plugin in config_override.runtime_plugins.iter().cloned() { + runtime_plugins = runtime_plugins.with_operation_plugin(plugin); + } + runtime_plugins = runtime_plugins.with_operation_plugin( + crate::config::ConfigOverrideRuntimePlugin::new(config_override, client_config.config.clone(), &client_config.runtime_components) + ); + } + runtime_plugins + } +} +impl ::aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin for ImportConfigJson { + fn config(&self) -> ::std::option::Option<::aws_smithy_types::config_bag::FrozenLayer> { + let mut cfg = ::aws_smithy_types::config_bag::Layer::new("ImportConfigJson"); + + cfg.store_put(::aws_smithy_runtime_api::client::ser_de::SharedRequestSerializer::new(ImportConfigJsonRequestSerializer)); + cfg.store_put(::aws_smithy_runtime_api::client::ser_de::SharedResponseDeserializer::new(ImportConfigJsonResponseDeserializer)); + + + cfg.store_put(::aws_smithy_runtime_api::client::auth::AuthSchemeOptionResolverParams::new(::aws_smithy_runtime_api::client::auth::static_resolver::StaticAuthSchemeOptionResolverParams::new())); + + cfg.store_put(::aws_smithy_runtime_api::client::orchestrator::Metadata::new( + "ImportConfigJson", + "Superposition", + )); + + ::std::option::Option::Some(cfg.freeze()) + } + + fn runtime_components(&self, _: &::aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder) -> ::std::borrow::Cow<'_, ::aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder> { + #[allow(unused_mut)] + let mut rcb = ::aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder::new("ImportConfigJson") + .with_interceptor(::aws_smithy_runtime::client::stalled_stream_protection::StalledStreamProtectionInterceptor::default()) +.with_interceptor(ImportConfigJsonEndpointParamsInterceptor) + .with_retry_classifier(::aws_smithy_runtime::client::retries::classifiers::TransientErrorClassifier::::new()) +.with_retry_classifier(::aws_smithy_runtime::client::retries::classifiers::ModeledAsRetryableClassifier::::new()); + + ::std::borrow::Cow::Owned(rcb) + } + } + + +#[derive(Debug)] + struct ImportConfigJsonResponseDeserializer; + impl ::aws_smithy_runtime_api::client::ser_de::DeserializeResponse for ImportConfigJsonResponseDeserializer { + + + fn deserialize_nonstreaming(&self, response: &::aws_smithy_runtime_api::client::orchestrator::HttpResponse) -> ::aws_smithy_runtime_api::client::interceptors::context::OutputOrError { + let (success, status) = (response.status().is_success(), response.status().as_u16()); + let headers = response.headers(); + let body = response.body().bytes().expect("body loaded"); + #[allow(unused_mut)] + let mut force_error = false; + + let parse_result = if !success && status != 200 || force_error { + crate::protocol_serde::shape_import_config_json::de_import_config_json_http_error(status, headers, body) + } else { + crate::protocol_serde::shape_import_config_json::de_import_config_json_http_response(status, headers, body) + }; + crate::protocol_serde::type_erase_result(parse_result) + } + } +#[derive(Debug)] + struct ImportConfigJsonRequestSerializer; + impl ::aws_smithy_runtime_api::client::ser_de::SerializeRequest for ImportConfigJsonRequestSerializer { + #[allow(unused_mut, clippy::let_and_return, clippy::needless_borrow, clippy::useless_conversion)] + fn serialize_input(&self, input: ::aws_smithy_runtime_api::client::interceptors::context::Input, _cfg: &mut ::aws_smithy_types::config_bag::ConfigBag) -> ::std::result::Result<::aws_smithy_runtime_api::client::orchestrator::HttpRequest, ::aws_smithy_runtime_api::box_error::BoxError> { + let input = input.downcast::().expect("correct type"); + let _header_serialization_settings = _cfg.load::().cloned().unwrap_or_default(); + let mut request_builder = { + fn uri_base(_input: &crate::operation::import_config_json::ImportConfigJsonInput, output: &mut ::std::string::String) -> ::std::result::Result<(), ::aws_smithy_types::error::operation::BuildError> { + use ::std::fmt::Write as _; + ::std::write!(output, "/config/json/import").expect("formatting should succeed"); + ::std::result::Result::Ok(()) +} +#[allow(clippy::unnecessary_wraps)] +fn update_http_builder( + input: &crate::operation::import_config_json::ImportConfigJsonInput, + builder: ::http::request::Builder + ) -> ::std::result::Result<::http::request::Builder, ::aws_smithy_types::error::operation::BuildError> { + let mut uri = ::std::string::String::new(); + uri_base(input, &mut uri)?; + let builder = crate::protocol_serde::shape_import_config_json::ser_import_config_json_headers(input, builder)?; + ::std::result::Result::Ok(builder.method("POST").uri(uri)) +} +let mut builder = update_http_builder(&input, ::http::request::Builder::new())?; +builder = _header_serialization_settings.set_default_header(builder, ::http::header::CONTENT_TYPE, "text/plain"); +builder + }; + let body = ::aws_smithy_types::body::SdkBody::from(crate::protocol_serde::shape_import_config_json_input::ser_json_config_http_payload( input.json_config)?); + if let Some(content_length) = body.content_length() { + let content_length = content_length.to_string(); + request_builder = _header_serialization_settings.set_default_header(request_builder, ::http::header::CONTENT_LENGTH, &content_length); + } + ::std::result::Result::Ok(request_builder.body(body).expect("valid request").try_into().unwrap()) + } + } +#[derive(Debug)] + struct ImportConfigJsonEndpointParamsInterceptor; + + impl ::aws_smithy_runtime_api::client::interceptors::Intercept for ImportConfigJsonEndpointParamsInterceptor { + fn name(&self) -> &'static str { + "ImportConfigJsonEndpointParamsInterceptor" + } + + fn read_before_execution( + &self, + context: &::aws_smithy_runtime_api::client::interceptors::context::BeforeSerializationInterceptorContextRef<'_, ::aws_smithy_runtime_api::client::interceptors::context::Input, ::aws_smithy_runtime_api::client::interceptors::context::Output, ::aws_smithy_runtime_api::client::interceptors::context::Error>, + cfg: &mut ::aws_smithy_types::config_bag::ConfigBag, + ) -> ::std::result::Result<(), ::aws_smithy_runtime_api::box_error::BoxError> { + let _input = context.input() + .downcast_ref::() + .ok_or("failed to downcast to ImportConfigJsonInput")?; + + + + let params = crate::config::endpoint::Params::builder() + + .build() + .map_err(|err| ::aws_smithy_runtime_api::client::interceptors::error::ContextAttachedError::new("endpoint params could not be built", err))?; + cfg.interceptor_state().store_put(::aws_smithy_runtime_api::client::endpoint::EndpointResolverParams::new(params)); + ::std::result::Result::Ok(()) + } + } + + // The get_* functions below are generated from JMESPath expressions in the + // operationContextParams trait. They target the operation's input shape. + + + +/// Error type for the `ImportConfigJsonError` operation. +#[non_exhaustive] +#[derive(::std::fmt::Debug)] +pub enum ImportConfigJsonError { + #[allow(missing_docs)] // documentation missing in model + InternalServerError(crate::types::error::InternalServerError), + /// An unexpected error occurred (e.g., invalid JSON returned by the service or an unknown error code). + #[deprecated(note = "Matching `Unhandled` directly is not forwards compatible. Instead, match using a \ + variable wildcard pattern and check `.code()`: + \ +    `err if err.code() == Some(\"SpecificExceptionCode\") => { /* handle the error */ }` + \ + See [`ProvideErrorMetadata`](#impl-ProvideErrorMetadata-for-ImportConfigJsonError) for what information is available for the error.")] + Unhandled(crate::error::sealed_unhandled::Unhandled), +} +impl ImportConfigJsonError { + /// Creates the `ImportConfigJsonError::Unhandled` variant from any error type. + pub fn unhandled(err: impl ::std::convert::Into<::std::boxed::Box>) -> Self { + Self::Unhandled(crate::error::sealed_unhandled::Unhandled { source: err.into(), meta: ::std::default::Default::default() }) + } + + /// Creates the `ImportConfigJsonError::Unhandled` variant from an [`ErrorMetadata`](::aws_smithy_types::error::ErrorMetadata). + pub fn generic(err: ::aws_smithy_types::error::ErrorMetadata) -> Self { + Self::Unhandled(crate::error::sealed_unhandled::Unhandled { source: err.clone().into(), meta: err }) + } + /// + /// Returns error metadata, which includes the error code, message, + /// request ID, and potentially additional information. + /// + pub fn meta(&self) -> &::aws_smithy_types::error::ErrorMetadata { + match self { + Self::InternalServerError(e) => ::aws_smithy_types::error::metadata::ProvideErrorMetadata::meta(e), + Self::Unhandled(e) => &e.meta, + } + } + /// Returns `true` if the error kind is `ImportConfigJsonError::InternalServerError`. + pub fn is_internal_server_error(&self) -> bool { + matches!(self, Self::InternalServerError(_)) + } +} +impl ::std::error::Error for ImportConfigJsonError { + fn source(&self) -> ::std::option::Option<&(dyn ::std::error::Error + 'static)> { + match self { + Self::InternalServerError(_inner) => + ::std::option::Option::Some(_inner) + , + Self::Unhandled(_inner) => { + ::std::option::Option::Some(&*_inner.source) + } + } + } +} +impl ::std::fmt::Display for ImportConfigJsonError { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match self { + Self::InternalServerError(_inner) => + _inner.fmt(f) + , + Self::Unhandled(_inner) => { + if let ::std::option::Option::Some(code) = ::aws_smithy_types::error::metadata::ProvideErrorMetadata::code(self) { + write!(f, "unhandled error ({code})") + } else { + f.write_str("unhandled error") + } + } + } + } +} +impl ::aws_smithy_types::retry::ProvideErrorKind for ImportConfigJsonError { + fn code(&self) -> ::std::option::Option<&str> { + ::aws_smithy_types::error::metadata::ProvideErrorMetadata::code(self) + } + fn retryable_error_kind(&self) -> ::std::option::Option<::aws_smithy_types::retry::ErrorKind> { + ::std::option::Option::None + } +} +impl ::aws_smithy_types::error::metadata::ProvideErrorMetadata for ImportConfigJsonError { + fn meta(&self) -> &::aws_smithy_types::error::ErrorMetadata { + match self { + Self::InternalServerError(_inner) => + ::aws_smithy_types::error::metadata::ProvideErrorMetadata::meta(_inner) + , + Self::Unhandled(_inner) => { + &_inner.meta + } + } + } +} +impl ::aws_smithy_runtime_api::client::result::CreateUnhandledError for ImportConfigJsonError { + fn create_unhandled_error( + source: ::std::boxed::Box, + meta: ::std::option::Option<::aws_smithy_types::error::ErrorMetadata> + ) -> Self { + Self::Unhandled(crate::error::sealed_unhandled::Unhandled { source, meta: meta.unwrap_or_default() }) + } +} + +pub use crate::operation::import_config_json::_import_config_json_output::ImportConfigJsonOutput; + +pub use crate::operation::import_config_json::_import_config_json_input::ImportConfigJsonInput; + +mod _import_config_json_input; + +mod _import_config_json_output; + +/// Builders +pub mod builders; + diff --git a/crates/superposition_sdk/src/operation/import_config_json/_import_config_json_input.rs b/crates/superposition_sdk/src/operation/import_config_json/_import_config_json_input.rs new file mode 100644 index 000000000..04e9b4b09 --- /dev/null +++ b/crates/superposition_sdk/src/operation/import_config_json/_import_config_json_input.rs @@ -0,0 +1,231 @@ +// Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT. +#[allow(missing_docs)] // documentation missing in model +#[non_exhaustive] +#[derive(::std::clone::Clone, ::std::cmp::PartialEq, ::std::fmt::Debug)] +pub struct ImportConfigJsonInput { + #[allow(missing_docs)] // documentation missing in model + pub workspace_id: ::std::option::Option<::std::string::String>, + #[allow(missing_docs)] // documentation missing in model + pub org_id: ::std::option::Option<::std::string::String>, + /// Whether to merge (default) or replace existing workspace config. + pub mode: ::std::option::Option, + /// When false, entities that already exist are skipped instead of updated. Defaults to true. + pub overwrite: ::std::option::Option, + /// Whether to abort (default) or continue on per-entity errors. + pub on_error: ::std::option::Option, + /// When true, validates and summarises the import without persisting anything. Defaults to false. + pub dry_run: ::std::option::Option, + /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. + pub value_merge: ::std::option::Option, + #[allow(missing_docs)] // documentation missing in model + pub config_tags: ::std::option::Option<::std::string::String>, + #[allow(missing_docs)] // documentation missing in model + pub json_config: ::std::option::Option<::std::string::String>, +} +impl ImportConfigJsonInput { + #[allow(missing_docs)] // documentation missing in model + pub fn workspace_id(&self) -> ::std::option::Option<&str> { + self.workspace_id.as_deref() + } + #[allow(missing_docs)] // documentation missing in model + pub fn org_id(&self) -> ::std::option::Option<&str> { + self.org_id.as_deref() + } + /// Whether to merge (default) or replace existing workspace config. + pub fn mode(&self) -> ::std::option::Option<&crate::types::ImportMode> { + self.mode.as_ref() + } + /// When false, entities that already exist are skipped instead of updated. Defaults to true. + pub fn overwrite(&self) -> ::std::option::Option { + self.overwrite + } + /// Whether to abort (default) or continue on per-entity errors. + pub fn on_error(&self) -> ::std::option::Option<&crate::types::ImportOnError> { + self.on_error.as_ref() + } + /// When true, validates and summarises the import without persisting anything. Defaults to false. + pub fn dry_run(&self) -> ::std::option::Option { + self.dry_run + } + /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. + pub fn value_merge(&self) -> ::std::option::Option { + self.value_merge + } + #[allow(missing_docs)] // documentation missing in model + pub fn config_tags(&self) -> ::std::option::Option<&str> { + self.config_tags.as_deref() + } + #[allow(missing_docs)] // documentation missing in model + pub fn json_config(&self) -> ::std::option::Option<&str> { + self.json_config.as_deref() + } +} +impl ImportConfigJsonInput { + /// Creates a new builder-style object to manufacture [`ImportConfigJsonInput`](crate::operation::import_config_json::ImportConfigJsonInput). + pub fn builder() -> crate::operation::import_config_json::builders::ImportConfigJsonInputBuilder { + crate::operation::import_config_json::builders::ImportConfigJsonInputBuilder::default() + } +} + +/// A builder for [`ImportConfigJsonInput`](crate::operation::import_config_json::ImportConfigJsonInput). +#[derive(::std::clone::Clone, ::std::cmp::PartialEq, ::std::default::Default, ::std::fmt::Debug)] +#[non_exhaustive] +pub struct ImportConfigJsonInputBuilder { + pub(crate) workspace_id: ::std::option::Option<::std::string::String>, + pub(crate) org_id: ::std::option::Option<::std::string::String>, + pub(crate) mode: ::std::option::Option, + pub(crate) overwrite: ::std::option::Option, + pub(crate) on_error: ::std::option::Option, + pub(crate) dry_run: ::std::option::Option, + pub(crate) value_merge: ::std::option::Option, + pub(crate) config_tags: ::std::option::Option<::std::string::String>, + pub(crate) json_config: ::std::option::Option<::std::string::String>, +} +impl ImportConfigJsonInputBuilder { + #[allow(missing_docs)] // documentation missing in model + /// This field is required. + pub fn workspace_id(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { + self.workspace_id = ::std::option::Option::Some(input.into()); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_workspace_id(mut self, input: ::std::option::Option<::std::string::String>) -> Self { + self.workspace_id = input; self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_workspace_id(&self) -> &::std::option::Option<::std::string::String> { + &self.workspace_id + } + #[allow(missing_docs)] // documentation missing in model + /// This field is required. + pub fn org_id(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { + self.org_id = ::std::option::Option::Some(input.into()); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_org_id(mut self, input: ::std::option::Option<::std::string::String>) -> Self { + self.org_id = input; self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_org_id(&self) -> &::std::option::Option<::std::string::String> { + &self.org_id + } + /// Whether to merge (default) or replace existing workspace config. + pub fn mode(mut self, input: crate::types::ImportMode) -> Self { + self.mode = ::std::option::Option::Some(input); + self + } + /// Whether to merge (default) or replace existing workspace config. + pub fn set_mode(mut self, input: ::std::option::Option) -> Self { + self.mode = input; self + } + /// Whether to merge (default) or replace existing workspace config. + pub fn get_mode(&self) -> &::std::option::Option { + &self.mode + } + /// When false, entities that already exist are skipped instead of updated. Defaults to true. + pub fn overwrite(mut self, input: bool) -> Self { + self.overwrite = ::std::option::Option::Some(input); + self + } + /// When false, entities that already exist are skipped instead of updated. Defaults to true. + pub fn set_overwrite(mut self, input: ::std::option::Option) -> Self { + self.overwrite = input; self + } + /// When false, entities that already exist are skipped instead of updated. Defaults to true. + pub fn get_overwrite(&self) -> &::std::option::Option { + &self.overwrite + } + /// Whether to abort (default) or continue on per-entity errors. + pub fn on_error(mut self, input: crate::types::ImportOnError) -> Self { + self.on_error = ::std::option::Option::Some(input); + self + } + /// Whether to abort (default) or continue on per-entity errors. + pub fn set_on_error(mut self, input: ::std::option::Option) -> Self { + self.on_error = input; self + } + /// Whether to abort (default) or continue on per-entity errors. + pub fn get_on_error(&self) -> &::std::option::Option { + &self.on_error + } + /// When true, validates and summarises the import without persisting anything. Defaults to false. + pub fn dry_run(mut self, input: bool) -> Self { + self.dry_run = ::std::option::Option::Some(input); + self + } + /// When true, validates and summarises the import without persisting anything. Defaults to false. + pub fn set_dry_run(mut self, input: ::std::option::Option) -> Self { + self.dry_run = input; self + } + /// When true, validates and summarises the import without persisting anything. Defaults to false. + pub fn get_dry_run(&self) -> &::std::option::Option { + &self.dry_run + } + /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. + pub fn value_merge(mut self, input: bool) -> Self { + self.value_merge = ::std::option::Option::Some(input); + self + } + /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. + pub fn set_value_merge(mut self, input: ::std::option::Option) -> Self { + self.value_merge = input; self + } + /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. + pub fn get_value_merge(&self) -> &::std::option::Option { + &self.value_merge + } + #[allow(missing_docs)] // documentation missing in model + pub fn config_tags(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { + self.config_tags = ::std::option::Option::Some(input.into()); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_config_tags(mut self, input: ::std::option::Option<::std::string::String>) -> Self { + self.config_tags = input; self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_config_tags(&self) -> &::std::option::Option<::std::string::String> { + &self.config_tags + } + #[allow(missing_docs)] // documentation missing in model + /// This field is required. + pub fn json_config(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { + self.json_config = ::std::option::Option::Some(input.into()); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_json_config(mut self, input: ::std::option::Option<::std::string::String>) -> Self { + self.json_config = input; self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_json_config(&self) -> &::std::option::Option<::std::string::String> { + &self.json_config + } + /// Consumes the builder and constructs a [`ImportConfigJsonInput`](crate::operation::import_config_json::ImportConfigJsonInput). + pub fn build(self) -> ::std::result::Result { + ::std::result::Result::Ok( + crate::operation::import_config_json::ImportConfigJsonInput { + workspace_id: self.workspace_id + , + org_id: self.org_id + , + mode: self.mode + , + overwrite: self.overwrite + , + on_error: self.on_error + , + dry_run: self.dry_run + , + value_merge: self.value_merge + , + config_tags: self.config_tags + , + json_config: self.json_config + , + } + ) + } +} + diff --git a/crates/superposition_sdk/src/operation/import_config_json/_import_config_json_output.rs b/crates/superposition_sdk/src/operation/import_config_json/_import_config_json_output.rs new file mode 100644 index 000000000..545eb9796 --- /dev/null +++ b/crates/superposition_sdk/src/operation/import_config_json/_import_config_json_output.rs @@ -0,0 +1,189 @@ +// Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT. + +/// Summary of what an import created, updated, skipped or deleted. +#[non_exhaustive] +#[derive(::std::clone::Clone, ::std::cmp::PartialEq, ::std::fmt::Debug)] +pub struct ImportConfigJsonOutput { + #[allow(missing_docs)] // documentation missing in model + pub mode: ::std::string::String, + #[allow(missing_docs)] // documentation missing in model + pub dry_run: bool, + #[allow(missing_docs)] // documentation missing in model + pub config_version: ::std::option::Option<::std::string::String>, + /// Per-entity outcome counts for an import. + pub dimensions: crate::types::ImportEntityReport, + /// Per-entity outcome counts for an import. + pub default_configs: crate::types::ImportEntityReport, + /// Per-entity outcome counts for an import. + pub contexts: crate::types::ImportEntityReport, +} +impl ImportConfigJsonOutput { + #[allow(missing_docs)] // documentation missing in model + pub fn mode(&self) -> &str { + use std::ops::Deref; self.mode.deref() + } + #[allow(missing_docs)] // documentation missing in model + pub fn dry_run(&self) -> bool { + self.dry_run + } + #[allow(missing_docs)] // documentation missing in model + pub fn config_version(&self) -> ::std::option::Option<&str> { + self.config_version.as_deref() + } + /// Per-entity outcome counts for an import. + pub fn dimensions(&self) -> &crate::types::ImportEntityReport { + &self.dimensions + } + /// Per-entity outcome counts for an import. + pub fn default_configs(&self) -> &crate::types::ImportEntityReport { + &self.default_configs + } + /// Per-entity outcome counts for an import. + pub fn contexts(&self) -> &crate::types::ImportEntityReport { + &self.contexts + } +} +impl ImportConfigJsonOutput { + /// Creates a new builder-style object to manufacture [`ImportConfigJsonOutput`](crate::operation::import_config_json::ImportConfigJsonOutput). + pub fn builder() -> crate::operation::import_config_json::builders::ImportConfigJsonOutputBuilder { + crate::operation::import_config_json::builders::ImportConfigJsonOutputBuilder::default() + } +} + +/// A builder for [`ImportConfigJsonOutput`](crate::operation::import_config_json::ImportConfigJsonOutput). +#[derive(::std::clone::Clone, ::std::cmp::PartialEq, ::std::default::Default, ::std::fmt::Debug)] +#[non_exhaustive] +pub struct ImportConfigJsonOutputBuilder { + pub(crate) mode: ::std::option::Option<::std::string::String>, + pub(crate) dry_run: ::std::option::Option, + pub(crate) config_version: ::std::option::Option<::std::string::String>, + pub(crate) dimensions: ::std::option::Option, + pub(crate) default_configs: ::std::option::Option, + pub(crate) contexts: ::std::option::Option, +} +impl ImportConfigJsonOutputBuilder { + #[allow(missing_docs)] // documentation missing in model + /// This field is required. + pub fn mode(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { + self.mode = ::std::option::Option::Some(input.into()); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_mode(mut self, input: ::std::option::Option<::std::string::String>) -> Self { + self.mode = input; self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_mode(&self) -> &::std::option::Option<::std::string::String> { + &self.mode + } + #[allow(missing_docs)] // documentation missing in model + /// This field is required. + pub fn dry_run(mut self, input: bool) -> Self { + self.dry_run = ::std::option::Option::Some(input); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_dry_run(mut self, input: ::std::option::Option) -> Self { + self.dry_run = input; self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_dry_run(&self) -> &::std::option::Option { + &self.dry_run + } + #[allow(missing_docs)] // documentation missing in model + pub fn config_version(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { + self.config_version = ::std::option::Option::Some(input.into()); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_config_version(mut self, input: ::std::option::Option<::std::string::String>) -> Self { + self.config_version = input; self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_config_version(&self) -> &::std::option::Option<::std::string::String> { + &self.config_version + } + /// Per-entity outcome counts for an import. + /// This field is required. + pub fn dimensions(mut self, input: crate::types::ImportEntityReport) -> Self { + self.dimensions = ::std::option::Option::Some(input); + self + } + /// Per-entity outcome counts for an import. + pub fn set_dimensions(mut self, input: ::std::option::Option) -> Self { + self.dimensions = input; self + } + /// Per-entity outcome counts for an import. + pub fn get_dimensions(&self) -> &::std::option::Option { + &self.dimensions + } + /// Per-entity outcome counts for an import. + /// This field is required. + pub fn default_configs(mut self, input: crate::types::ImportEntityReport) -> Self { + self.default_configs = ::std::option::Option::Some(input); + self + } + /// Per-entity outcome counts for an import. + pub fn set_default_configs(mut self, input: ::std::option::Option) -> Self { + self.default_configs = input; self + } + /// Per-entity outcome counts for an import. + pub fn get_default_configs(&self) -> &::std::option::Option { + &self.default_configs + } + /// Per-entity outcome counts for an import. + /// This field is required. + pub fn contexts(mut self, input: crate::types::ImportEntityReport) -> Self { + self.contexts = ::std::option::Option::Some(input); + self + } + /// Per-entity outcome counts for an import. + pub fn set_contexts(mut self, input: ::std::option::Option) -> Self { + self.contexts = input; self + } + /// Per-entity outcome counts for an import. + pub fn get_contexts(&self) -> &::std::option::Option { + &self.contexts + } + /// Consumes the builder and constructs a [`ImportConfigJsonOutput`](crate::operation::import_config_json::ImportConfigJsonOutput). + /// This method will fail if any of the following fields are not set: + /// - [`mode`](crate::operation::import_config_json::builders::ImportConfigJsonOutputBuilder::mode) + /// - [`dry_run`](crate::operation::import_config_json::builders::ImportConfigJsonOutputBuilder::dry_run) + /// - [`dimensions`](crate::operation::import_config_json::builders::ImportConfigJsonOutputBuilder::dimensions) + /// - [`default_configs`](crate::operation::import_config_json::builders::ImportConfigJsonOutputBuilder::default_configs) + /// - [`contexts`](crate::operation::import_config_json::builders::ImportConfigJsonOutputBuilder::contexts) + pub fn build(self) -> ::std::result::Result { + ::std::result::Result::Ok( + crate::operation::import_config_json::ImportConfigJsonOutput { + mode: self.mode + .ok_or_else(|| + ::aws_smithy_types::error::operation::BuildError::missing_field("mode", "mode was not specified but it is required when building ImportConfigJsonOutput") + )? + , + dry_run: self.dry_run + .ok_or_else(|| + ::aws_smithy_types::error::operation::BuildError::missing_field("dry_run", "dry_run was not specified but it is required when building ImportConfigJsonOutput") + )? + , + config_version: self.config_version + , + dimensions: self.dimensions + .ok_or_else(|| + ::aws_smithy_types::error::operation::BuildError::missing_field("dimensions", "dimensions was not specified but it is required when building ImportConfigJsonOutput") + )? + , + default_configs: self.default_configs + .ok_or_else(|| + ::aws_smithy_types::error::operation::BuildError::missing_field("default_configs", "default_configs was not specified but it is required when building ImportConfigJsonOutput") + )? + , + contexts: self.contexts + .ok_or_else(|| + ::aws_smithy_types::error::operation::BuildError::missing_field("contexts", "contexts was not specified but it is required when building ImportConfigJsonOutput") + )? + , + } + ) + } +} + diff --git a/crates/superposition_sdk/src/operation/import_config_json/builders.rs b/crates/superposition_sdk/src/operation/import_config_json/builders.rs new file mode 100644 index 000000000..49a29a460 --- /dev/null +++ b/crates/superposition_sdk/src/operation/import_config_json/builders.rs @@ -0,0 +1,226 @@ +// Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT. +pub use crate::operation::import_config_json::_import_config_json_output::ImportConfigJsonOutputBuilder; + +pub use crate::operation::import_config_json::_import_config_json_input::ImportConfigJsonInputBuilder; + +impl crate::operation::import_config_json::builders::ImportConfigJsonInputBuilder { + /// Sends a request with this input using the given client. + pub async fn send_with(self, client: &crate::Client) -> ::std::result::Result< + crate::operation::import_config_json::ImportConfigJsonOutput, + ::aws_smithy_runtime_api::client::result::SdkError< + crate::operation::import_config_json::ImportConfigJsonError, + ::aws_smithy_runtime_api::client::orchestrator::HttpResponse + > + > { + let mut fluent_builder = client.import_config_json(); + fluent_builder.inner = self; + fluent_builder.send().await + } + } +/// Fluent builder constructing a request to `ImportConfigJson`. +/// +/// Imports a full config from a JSON document, persisting dimensions, default-configs and contexts in a single transaction after validating the document. +#[derive(::std::clone::Clone, ::std::fmt::Debug)] +pub struct ImportConfigJsonFluentBuilder { + handle: ::std::sync::Arc, + inner: crate::operation::import_config_json::builders::ImportConfigJsonInputBuilder, +config_override: ::std::option::Option, + } +impl + crate::client::customize::internal::CustomizableSend< + crate::operation::import_config_json::ImportConfigJsonOutput, + crate::operation::import_config_json::ImportConfigJsonError, + > for ImportConfigJsonFluentBuilder + { + fn send( + self, + config_override: crate::config::Builder, + ) -> crate::client::customize::internal::BoxFuture< + crate::client::customize::internal::SendResult< + crate::operation::import_config_json::ImportConfigJsonOutput, + crate::operation::import_config_json::ImportConfigJsonError, + >, + > { + ::std::boxed::Box::pin(async move { self.config_override(config_override).send().await }) + } + } +impl ImportConfigJsonFluentBuilder { + /// Creates a new `ImportConfigJsonFluentBuilder`. + pub(crate) fn new(handle: ::std::sync::Arc) -> Self { + Self { + handle, + inner: ::std::default::Default::default(), + config_override: ::std::option::Option::None, + } + } + /// Access the ImportConfigJson as a reference. + pub fn as_input(&self) -> &crate::operation::import_config_json::builders::ImportConfigJsonInputBuilder { + &self.inner + } + /// Sends the request and returns the response. + /// + /// If an error occurs, an `SdkError` will be returned with additional details that + /// can be matched against. + /// + /// By default, any retryable failures will be retried twice. Retry behavior + /// is configurable with the [RetryConfig](aws_smithy_types::retry::RetryConfig), which can be + /// set when configuring the client. + pub async fn send(self) -> ::std::result::Result> { + let input = self.inner.build().map_err(::aws_smithy_runtime_api::client::result::SdkError::construction_failure)?; + let runtime_plugins = crate::operation::import_config_json::ImportConfigJson::operation_runtime_plugins( + self.handle.runtime_plugins.clone(), + &self.handle.conf, + self.config_override, + ); + crate::operation::import_config_json::ImportConfigJson::orchestrate(&runtime_plugins, input).await + } + + /// Consumes this builder, creating a customizable operation that can be modified before being sent. + pub fn customize( + self, + ) -> crate::client::customize::CustomizableOperation { + crate::client::customize::CustomizableOperation::new(self) + } + pub(crate) fn config_override( + mut self, + config_override: impl ::std::convert::Into, + ) -> Self { + self.set_config_override(::std::option::Option::Some(config_override.into())); + self + } + + pub(crate) fn set_config_override( + &mut self, + config_override: ::std::option::Option, + ) -> &mut Self { + self.config_override = config_override; + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn workspace_id(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { + self.inner = self.inner.workspace_id(input.into()); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_workspace_id(mut self, input: ::std::option::Option<::std::string::String>) -> Self { + self.inner = self.inner.set_workspace_id(input); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_workspace_id(&self) -> &::std::option::Option<::std::string::String> { + self.inner.get_workspace_id() + } + #[allow(missing_docs)] // documentation missing in model + pub fn org_id(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { + self.inner = self.inner.org_id(input.into()); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_org_id(mut self, input: ::std::option::Option<::std::string::String>) -> Self { + self.inner = self.inner.set_org_id(input); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_org_id(&self) -> &::std::option::Option<::std::string::String> { + self.inner.get_org_id() + } + /// Whether to merge (default) or replace existing workspace config. + pub fn mode(mut self, input: crate::types::ImportMode) -> Self { + self.inner = self.inner.mode(input); + self + } + /// Whether to merge (default) or replace existing workspace config. + pub fn set_mode(mut self, input: ::std::option::Option) -> Self { + self.inner = self.inner.set_mode(input); + self + } + /// Whether to merge (default) or replace existing workspace config. + pub fn get_mode(&self) -> &::std::option::Option { + self.inner.get_mode() + } + /// When false, entities that already exist are skipped instead of updated. Defaults to true. + pub fn overwrite(mut self, input: bool) -> Self { + self.inner = self.inner.overwrite(input); + self + } + /// When false, entities that already exist are skipped instead of updated. Defaults to true. + pub fn set_overwrite(mut self, input: ::std::option::Option) -> Self { + self.inner = self.inner.set_overwrite(input); + self + } + /// When false, entities that already exist are skipped instead of updated. Defaults to true. + pub fn get_overwrite(&self) -> &::std::option::Option { + self.inner.get_overwrite() + } + /// Whether to abort (default) or continue on per-entity errors. + pub fn on_error(mut self, input: crate::types::ImportOnError) -> Self { + self.inner = self.inner.on_error(input); + self + } + /// Whether to abort (default) or continue on per-entity errors. + pub fn set_on_error(mut self, input: ::std::option::Option) -> Self { + self.inner = self.inner.set_on_error(input); + self + } + /// Whether to abort (default) or continue on per-entity errors. + pub fn get_on_error(&self) -> &::std::option::Option { + self.inner.get_on_error() + } + /// When true, validates and summarises the import without persisting anything. Defaults to false. + pub fn dry_run(mut self, input: bool) -> Self { + self.inner = self.inner.dry_run(input); + self + } + /// When true, validates and summarises the import without persisting anything. Defaults to false. + pub fn set_dry_run(mut self, input: ::std::option::Option) -> Self { + self.inner = self.inner.set_dry_run(input); + self + } + /// When true, validates and summarises the import without persisting anything. Defaults to false. + pub fn get_dry_run(&self) -> &::std::option::Option { + self.inner.get_dry_run() + } + /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. + pub fn value_merge(mut self, input: bool) -> Self { + self.inner = self.inner.value_merge(input); + self + } + /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. + pub fn set_value_merge(mut self, input: ::std::option::Option) -> Self { + self.inner = self.inner.set_value_merge(input); + self + } + /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. + pub fn get_value_merge(&self) -> &::std::option::Option { + self.inner.get_value_merge() + } + #[allow(missing_docs)] // documentation missing in model + pub fn config_tags(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { + self.inner = self.inner.config_tags(input.into()); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_config_tags(mut self, input: ::std::option::Option<::std::string::String>) -> Self { + self.inner = self.inner.set_config_tags(input); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_config_tags(&self) -> &::std::option::Option<::std::string::String> { + self.inner.get_config_tags() + } + #[allow(missing_docs)] // documentation missing in model + pub fn json_config(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { + self.inner = self.inner.json_config(input.into()); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_json_config(mut self, input: ::std::option::Option<::std::string::String>) -> Self { + self.inner = self.inner.set_json_config(input); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_json_config(&self) -> &::std::option::Option<::std::string::String> { + self.inner.get_json_config() + } +} + diff --git a/crates/superposition_sdk/src/operation/import_config_toml.rs b/crates/superposition_sdk/src/operation/import_config_toml.rs new file mode 100644 index 000000000..f3caefea4 --- /dev/null +++ b/crates/superposition_sdk/src/operation/import_config_toml.rs @@ -0,0 +1,299 @@ +// Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT. +/// Orchestration and serialization glue logic for `ImportConfigToml`. +#[derive(::std::clone::Clone, ::std::default::Default, ::std::fmt::Debug)] +#[non_exhaustive] +pub struct ImportConfigToml; +impl ImportConfigToml { + /// Creates a new `ImportConfigToml` + pub fn new() -> Self { + Self + } + pub(crate) async fn orchestrate( + runtime_plugins: &::aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugins, + input: crate::operation::import_config_toml::ImportConfigTomlInput, + ) -> ::std::result::Result> { + let map_err = |err: ::aws_smithy_runtime_api::client::result::SdkError<::aws_smithy_runtime_api::client::interceptors::context::Error, ::aws_smithy_runtime_api::client::orchestrator::HttpResponse>| { + err.map_service_error(|err| { + err.downcast::().expect("correct error type") + }) + }; + let context = Self::orchestrate_with_stop_point(runtime_plugins, input, ::aws_smithy_runtime::client::orchestrator::StopPoint::None) + .await + .map_err(map_err)?; + let output = context.finalize().map_err(map_err)?; + ::std::result::Result::Ok(output.downcast::().expect("correct output type")) + } + + pub(crate) async fn orchestrate_with_stop_point( + runtime_plugins: &::aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugins, + input: crate::operation::import_config_toml::ImportConfigTomlInput, + stop_point: ::aws_smithy_runtime::client::orchestrator::StopPoint, + ) -> ::std::result::Result<::aws_smithy_runtime_api::client::interceptors::context::InterceptorContext, ::aws_smithy_runtime_api::client::result::SdkError<::aws_smithy_runtime_api::client::interceptors::context::Error, ::aws_smithy_runtime_api::client::orchestrator::HttpResponse>> { + let input = ::aws_smithy_runtime_api::client::interceptors::context::Input::erase(input); + use ::tracing::Instrument; + ::aws_smithy_runtime::client::orchestrator::invoke_with_stop_point( + "Superposition", + "ImportConfigToml", + input, + runtime_plugins, + stop_point + ) + // Create a parent span for the entire operation. Includes a random, internal-only, + // seven-digit ID for the operation orchestration so that it can be correlated in the logs. + .instrument(::tracing::debug_span!( + "Superposition.ImportConfigToml", + "rpc.service" = "Superposition", + "rpc.method" = "ImportConfigToml", + "sdk_invocation_id" = ::fastrand::u32(1_000_000..10_000_000), + + )) + .await + } + + pub(crate) fn operation_runtime_plugins( + client_runtime_plugins: ::aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugins, + client_config: &crate::config::Config, + config_override: ::std::option::Option, + ) -> ::aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugins { + let mut runtime_plugins = client_runtime_plugins.with_operation_plugin(Self::new()); + runtime_plugins = runtime_plugins + .with_client_plugin(crate::auth_plugin::DefaultAuthOptionsPlugin::new(vec![::aws_smithy_runtime_api::client::auth::http::HTTP_BASIC_AUTH_SCHEME_ID + , ::aws_smithy_runtime_api::client::auth::http::HTTP_BEARER_AUTH_SCHEME_ID])); + if let ::std::option::Option::Some(config_override) = config_override { + for plugin in config_override.runtime_plugins.iter().cloned() { + runtime_plugins = runtime_plugins.with_operation_plugin(plugin); + } + runtime_plugins = runtime_plugins.with_operation_plugin( + crate::config::ConfigOverrideRuntimePlugin::new(config_override, client_config.config.clone(), &client_config.runtime_components) + ); + } + runtime_plugins + } +} +impl ::aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin for ImportConfigToml { + fn config(&self) -> ::std::option::Option<::aws_smithy_types::config_bag::FrozenLayer> { + let mut cfg = ::aws_smithy_types::config_bag::Layer::new("ImportConfigToml"); + + cfg.store_put(::aws_smithy_runtime_api::client::ser_de::SharedRequestSerializer::new(ImportConfigTomlRequestSerializer)); + cfg.store_put(::aws_smithy_runtime_api::client::ser_de::SharedResponseDeserializer::new(ImportConfigTomlResponseDeserializer)); + + + cfg.store_put(::aws_smithy_runtime_api::client::auth::AuthSchemeOptionResolverParams::new(::aws_smithy_runtime_api::client::auth::static_resolver::StaticAuthSchemeOptionResolverParams::new())); + + cfg.store_put(::aws_smithy_runtime_api::client::orchestrator::Metadata::new( + "ImportConfigToml", + "Superposition", + )); + + ::std::option::Option::Some(cfg.freeze()) + } + + fn runtime_components(&self, _: &::aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder) -> ::std::borrow::Cow<'_, ::aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder> { + #[allow(unused_mut)] + let mut rcb = ::aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder::new("ImportConfigToml") + .with_interceptor(::aws_smithy_runtime::client::stalled_stream_protection::StalledStreamProtectionInterceptor::default()) +.with_interceptor(ImportConfigTomlEndpointParamsInterceptor) + .with_retry_classifier(::aws_smithy_runtime::client::retries::classifiers::TransientErrorClassifier::::new()) +.with_retry_classifier(::aws_smithy_runtime::client::retries::classifiers::ModeledAsRetryableClassifier::::new()); + + ::std::borrow::Cow::Owned(rcb) + } + } + + +#[derive(Debug)] + struct ImportConfigTomlResponseDeserializer; + impl ::aws_smithy_runtime_api::client::ser_de::DeserializeResponse for ImportConfigTomlResponseDeserializer { + + + fn deserialize_nonstreaming(&self, response: &::aws_smithy_runtime_api::client::orchestrator::HttpResponse) -> ::aws_smithy_runtime_api::client::interceptors::context::OutputOrError { + let (success, status) = (response.status().is_success(), response.status().as_u16()); + let headers = response.headers(); + let body = response.body().bytes().expect("body loaded"); + #[allow(unused_mut)] + let mut force_error = false; + + let parse_result = if !success && status != 200 || force_error { + crate::protocol_serde::shape_import_config_toml::de_import_config_toml_http_error(status, headers, body) + } else { + crate::protocol_serde::shape_import_config_toml::de_import_config_toml_http_response(status, headers, body) + }; + crate::protocol_serde::type_erase_result(parse_result) + } + } +#[derive(Debug)] + struct ImportConfigTomlRequestSerializer; + impl ::aws_smithy_runtime_api::client::ser_de::SerializeRequest for ImportConfigTomlRequestSerializer { + #[allow(unused_mut, clippy::let_and_return, clippy::needless_borrow, clippy::useless_conversion)] + fn serialize_input(&self, input: ::aws_smithy_runtime_api::client::interceptors::context::Input, _cfg: &mut ::aws_smithy_types::config_bag::ConfigBag) -> ::std::result::Result<::aws_smithy_runtime_api::client::orchestrator::HttpRequest, ::aws_smithy_runtime_api::box_error::BoxError> { + let input = input.downcast::().expect("correct type"); + let _header_serialization_settings = _cfg.load::().cloned().unwrap_or_default(); + let mut request_builder = { + fn uri_base(_input: &crate::operation::import_config_toml::ImportConfigTomlInput, output: &mut ::std::string::String) -> ::std::result::Result<(), ::aws_smithy_types::error::operation::BuildError> { + use ::std::fmt::Write as _; + ::std::write!(output, "/config/toml/import").expect("formatting should succeed"); + ::std::result::Result::Ok(()) +} +#[allow(clippy::unnecessary_wraps)] +fn update_http_builder( + input: &crate::operation::import_config_toml::ImportConfigTomlInput, + builder: ::http::request::Builder + ) -> ::std::result::Result<::http::request::Builder, ::aws_smithy_types::error::operation::BuildError> { + let mut uri = ::std::string::String::new(); + uri_base(input, &mut uri)?; + let builder = crate::protocol_serde::shape_import_config_toml::ser_import_config_toml_headers(input, builder)?; + ::std::result::Result::Ok(builder.method("POST").uri(uri)) +} +let mut builder = update_http_builder(&input, ::http::request::Builder::new())?; +builder = _header_serialization_settings.set_default_header(builder, ::http::header::CONTENT_TYPE, "text/plain"); +builder + }; + let body = ::aws_smithy_types::body::SdkBody::from(crate::protocol_serde::shape_import_config_toml_input::ser_toml_config_http_payload( input.toml_config)?); + if let Some(content_length) = body.content_length() { + let content_length = content_length.to_string(); + request_builder = _header_serialization_settings.set_default_header(request_builder, ::http::header::CONTENT_LENGTH, &content_length); + } + ::std::result::Result::Ok(request_builder.body(body).expect("valid request").try_into().unwrap()) + } + } +#[derive(Debug)] + struct ImportConfigTomlEndpointParamsInterceptor; + + impl ::aws_smithy_runtime_api::client::interceptors::Intercept for ImportConfigTomlEndpointParamsInterceptor { + fn name(&self) -> &'static str { + "ImportConfigTomlEndpointParamsInterceptor" + } + + fn read_before_execution( + &self, + context: &::aws_smithy_runtime_api::client::interceptors::context::BeforeSerializationInterceptorContextRef<'_, ::aws_smithy_runtime_api::client::interceptors::context::Input, ::aws_smithy_runtime_api::client::interceptors::context::Output, ::aws_smithy_runtime_api::client::interceptors::context::Error>, + cfg: &mut ::aws_smithy_types::config_bag::ConfigBag, + ) -> ::std::result::Result<(), ::aws_smithy_runtime_api::box_error::BoxError> { + let _input = context.input() + .downcast_ref::() + .ok_or("failed to downcast to ImportConfigTomlInput")?; + + + + let params = crate::config::endpoint::Params::builder() + + .build() + .map_err(|err| ::aws_smithy_runtime_api::client::interceptors::error::ContextAttachedError::new("endpoint params could not be built", err))?; + cfg.interceptor_state().store_put(::aws_smithy_runtime_api::client::endpoint::EndpointResolverParams::new(params)); + ::std::result::Result::Ok(()) + } + } + + // The get_* functions below are generated from JMESPath expressions in the + // operationContextParams trait. They target the operation's input shape. + + + +/// Error type for the `ImportConfigTomlError` operation. +#[non_exhaustive] +#[derive(::std::fmt::Debug)] +pub enum ImportConfigTomlError { + #[allow(missing_docs)] // documentation missing in model + InternalServerError(crate::types::error::InternalServerError), + /// An unexpected error occurred (e.g., invalid JSON returned by the service or an unknown error code). + #[deprecated(note = "Matching `Unhandled` directly is not forwards compatible. Instead, match using a \ + variable wildcard pattern and check `.code()`: + \ +    `err if err.code() == Some(\"SpecificExceptionCode\") => { /* handle the error */ }` + \ + See [`ProvideErrorMetadata`](#impl-ProvideErrorMetadata-for-ImportConfigTomlError) for what information is available for the error.")] + Unhandled(crate::error::sealed_unhandled::Unhandled), +} +impl ImportConfigTomlError { + /// Creates the `ImportConfigTomlError::Unhandled` variant from any error type. + pub fn unhandled(err: impl ::std::convert::Into<::std::boxed::Box>) -> Self { + Self::Unhandled(crate::error::sealed_unhandled::Unhandled { source: err.into(), meta: ::std::default::Default::default() }) + } + + /// Creates the `ImportConfigTomlError::Unhandled` variant from an [`ErrorMetadata`](::aws_smithy_types::error::ErrorMetadata). + pub fn generic(err: ::aws_smithy_types::error::ErrorMetadata) -> Self { + Self::Unhandled(crate::error::sealed_unhandled::Unhandled { source: err.clone().into(), meta: err }) + } + /// + /// Returns error metadata, which includes the error code, message, + /// request ID, and potentially additional information. + /// + pub fn meta(&self) -> &::aws_smithy_types::error::ErrorMetadata { + match self { + Self::InternalServerError(e) => ::aws_smithy_types::error::metadata::ProvideErrorMetadata::meta(e), + Self::Unhandled(e) => &e.meta, + } + } + /// Returns `true` if the error kind is `ImportConfigTomlError::InternalServerError`. + pub fn is_internal_server_error(&self) -> bool { + matches!(self, Self::InternalServerError(_)) + } +} +impl ::std::error::Error for ImportConfigTomlError { + fn source(&self) -> ::std::option::Option<&(dyn ::std::error::Error + 'static)> { + match self { + Self::InternalServerError(_inner) => + ::std::option::Option::Some(_inner) + , + Self::Unhandled(_inner) => { + ::std::option::Option::Some(&*_inner.source) + } + } + } +} +impl ::std::fmt::Display for ImportConfigTomlError { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match self { + Self::InternalServerError(_inner) => + _inner.fmt(f) + , + Self::Unhandled(_inner) => { + if let ::std::option::Option::Some(code) = ::aws_smithy_types::error::metadata::ProvideErrorMetadata::code(self) { + write!(f, "unhandled error ({code})") + } else { + f.write_str("unhandled error") + } + } + } + } +} +impl ::aws_smithy_types::retry::ProvideErrorKind for ImportConfigTomlError { + fn code(&self) -> ::std::option::Option<&str> { + ::aws_smithy_types::error::metadata::ProvideErrorMetadata::code(self) + } + fn retryable_error_kind(&self) -> ::std::option::Option<::aws_smithy_types::retry::ErrorKind> { + ::std::option::Option::None + } +} +impl ::aws_smithy_types::error::metadata::ProvideErrorMetadata for ImportConfigTomlError { + fn meta(&self) -> &::aws_smithy_types::error::ErrorMetadata { + match self { + Self::InternalServerError(_inner) => + ::aws_smithy_types::error::metadata::ProvideErrorMetadata::meta(_inner) + , + Self::Unhandled(_inner) => { + &_inner.meta + } + } + } +} +impl ::aws_smithy_runtime_api::client::result::CreateUnhandledError for ImportConfigTomlError { + fn create_unhandled_error( + source: ::std::boxed::Box, + meta: ::std::option::Option<::aws_smithy_types::error::ErrorMetadata> + ) -> Self { + Self::Unhandled(crate::error::sealed_unhandled::Unhandled { source, meta: meta.unwrap_or_default() }) + } +} + +pub use crate::operation::import_config_toml::_import_config_toml_output::ImportConfigTomlOutput; + +pub use crate::operation::import_config_toml::_import_config_toml_input::ImportConfigTomlInput; + +mod _import_config_toml_input; + +mod _import_config_toml_output; + +/// Builders +pub mod builders; + diff --git a/crates/superposition_sdk/src/operation/import_config_toml/_import_config_toml_input.rs b/crates/superposition_sdk/src/operation/import_config_toml/_import_config_toml_input.rs new file mode 100644 index 000000000..f14f1fcf1 --- /dev/null +++ b/crates/superposition_sdk/src/operation/import_config_toml/_import_config_toml_input.rs @@ -0,0 +1,231 @@ +// Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT. +#[allow(missing_docs)] // documentation missing in model +#[non_exhaustive] +#[derive(::std::clone::Clone, ::std::cmp::PartialEq, ::std::fmt::Debug)] +pub struct ImportConfigTomlInput { + #[allow(missing_docs)] // documentation missing in model + pub workspace_id: ::std::option::Option<::std::string::String>, + #[allow(missing_docs)] // documentation missing in model + pub org_id: ::std::option::Option<::std::string::String>, + /// Whether to merge (default) or replace existing workspace config. + pub mode: ::std::option::Option, + /// When false, entities that already exist are skipped instead of updated. Defaults to true. + pub overwrite: ::std::option::Option, + /// Whether to abort (default) or continue on per-entity errors. + pub on_error: ::std::option::Option, + /// When true, validates and summarises the import without persisting anything. Defaults to false. + pub dry_run: ::std::option::Option, + /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. + pub value_merge: ::std::option::Option, + #[allow(missing_docs)] // documentation missing in model + pub config_tags: ::std::option::Option<::std::string::String>, + #[allow(missing_docs)] // documentation missing in model + pub toml_config: ::std::option::Option<::std::string::String>, +} +impl ImportConfigTomlInput { + #[allow(missing_docs)] // documentation missing in model + pub fn workspace_id(&self) -> ::std::option::Option<&str> { + self.workspace_id.as_deref() + } + #[allow(missing_docs)] // documentation missing in model + pub fn org_id(&self) -> ::std::option::Option<&str> { + self.org_id.as_deref() + } + /// Whether to merge (default) or replace existing workspace config. + pub fn mode(&self) -> ::std::option::Option<&crate::types::ImportMode> { + self.mode.as_ref() + } + /// When false, entities that already exist are skipped instead of updated. Defaults to true. + pub fn overwrite(&self) -> ::std::option::Option { + self.overwrite + } + /// Whether to abort (default) or continue on per-entity errors. + pub fn on_error(&self) -> ::std::option::Option<&crate::types::ImportOnError> { + self.on_error.as_ref() + } + /// When true, validates and summarises the import without persisting anything. Defaults to false. + pub fn dry_run(&self) -> ::std::option::Option { + self.dry_run + } + /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. + pub fn value_merge(&self) -> ::std::option::Option { + self.value_merge + } + #[allow(missing_docs)] // documentation missing in model + pub fn config_tags(&self) -> ::std::option::Option<&str> { + self.config_tags.as_deref() + } + #[allow(missing_docs)] // documentation missing in model + pub fn toml_config(&self) -> ::std::option::Option<&str> { + self.toml_config.as_deref() + } +} +impl ImportConfigTomlInput { + /// Creates a new builder-style object to manufacture [`ImportConfigTomlInput`](crate::operation::import_config_toml::ImportConfigTomlInput). + pub fn builder() -> crate::operation::import_config_toml::builders::ImportConfigTomlInputBuilder { + crate::operation::import_config_toml::builders::ImportConfigTomlInputBuilder::default() + } +} + +/// A builder for [`ImportConfigTomlInput`](crate::operation::import_config_toml::ImportConfigTomlInput). +#[derive(::std::clone::Clone, ::std::cmp::PartialEq, ::std::default::Default, ::std::fmt::Debug)] +#[non_exhaustive] +pub struct ImportConfigTomlInputBuilder { + pub(crate) workspace_id: ::std::option::Option<::std::string::String>, + pub(crate) org_id: ::std::option::Option<::std::string::String>, + pub(crate) mode: ::std::option::Option, + pub(crate) overwrite: ::std::option::Option, + pub(crate) on_error: ::std::option::Option, + pub(crate) dry_run: ::std::option::Option, + pub(crate) value_merge: ::std::option::Option, + pub(crate) config_tags: ::std::option::Option<::std::string::String>, + pub(crate) toml_config: ::std::option::Option<::std::string::String>, +} +impl ImportConfigTomlInputBuilder { + #[allow(missing_docs)] // documentation missing in model + /// This field is required. + pub fn workspace_id(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { + self.workspace_id = ::std::option::Option::Some(input.into()); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_workspace_id(mut self, input: ::std::option::Option<::std::string::String>) -> Self { + self.workspace_id = input; self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_workspace_id(&self) -> &::std::option::Option<::std::string::String> { + &self.workspace_id + } + #[allow(missing_docs)] // documentation missing in model + /// This field is required. + pub fn org_id(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { + self.org_id = ::std::option::Option::Some(input.into()); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_org_id(mut self, input: ::std::option::Option<::std::string::String>) -> Self { + self.org_id = input; self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_org_id(&self) -> &::std::option::Option<::std::string::String> { + &self.org_id + } + /// Whether to merge (default) or replace existing workspace config. + pub fn mode(mut self, input: crate::types::ImportMode) -> Self { + self.mode = ::std::option::Option::Some(input); + self + } + /// Whether to merge (default) or replace existing workspace config. + pub fn set_mode(mut self, input: ::std::option::Option) -> Self { + self.mode = input; self + } + /// Whether to merge (default) or replace existing workspace config. + pub fn get_mode(&self) -> &::std::option::Option { + &self.mode + } + /// When false, entities that already exist are skipped instead of updated. Defaults to true. + pub fn overwrite(mut self, input: bool) -> Self { + self.overwrite = ::std::option::Option::Some(input); + self + } + /// When false, entities that already exist are skipped instead of updated. Defaults to true. + pub fn set_overwrite(mut self, input: ::std::option::Option) -> Self { + self.overwrite = input; self + } + /// When false, entities that already exist are skipped instead of updated. Defaults to true. + pub fn get_overwrite(&self) -> &::std::option::Option { + &self.overwrite + } + /// Whether to abort (default) or continue on per-entity errors. + pub fn on_error(mut self, input: crate::types::ImportOnError) -> Self { + self.on_error = ::std::option::Option::Some(input); + self + } + /// Whether to abort (default) or continue on per-entity errors. + pub fn set_on_error(mut self, input: ::std::option::Option) -> Self { + self.on_error = input; self + } + /// Whether to abort (default) or continue on per-entity errors. + pub fn get_on_error(&self) -> &::std::option::Option { + &self.on_error + } + /// When true, validates and summarises the import without persisting anything. Defaults to false. + pub fn dry_run(mut self, input: bool) -> Self { + self.dry_run = ::std::option::Option::Some(input); + self + } + /// When true, validates and summarises the import without persisting anything. Defaults to false. + pub fn set_dry_run(mut self, input: ::std::option::Option) -> Self { + self.dry_run = input; self + } + /// When true, validates and summarises the import without persisting anything. Defaults to false. + pub fn get_dry_run(&self) -> &::std::option::Option { + &self.dry_run + } + /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. + pub fn value_merge(mut self, input: bool) -> Self { + self.value_merge = ::std::option::Option::Some(input); + self + } + /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. + pub fn set_value_merge(mut self, input: ::std::option::Option) -> Self { + self.value_merge = input; self + } + /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. + pub fn get_value_merge(&self) -> &::std::option::Option { + &self.value_merge + } + #[allow(missing_docs)] // documentation missing in model + pub fn config_tags(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { + self.config_tags = ::std::option::Option::Some(input.into()); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_config_tags(mut self, input: ::std::option::Option<::std::string::String>) -> Self { + self.config_tags = input; self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_config_tags(&self) -> &::std::option::Option<::std::string::String> { + &self.config_tags + } + #[allow(missing_docs)] // documentation missing in model + /// This field is required. + pub fn toml_config(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { + self.toml_config = ::std::option::Option::Some(input.into()); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_toml_config(mut self, input: ::std::option::Option<::std::string::String>) -> Self { + self.toml_config = input; self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_toml_config(&self) -> &::std::option::Option<::std::string::String> { + &self.toml_config + } + /// Consumes the builder and constructs a [`ImportConfigTomlInput`](crate::operation::import_config_toml::ImportConfigTomlInput). + pub fn build(self) -> ::std::result::Result { + ::std::result::Result::Ok( + crate::operation::import_config_toml::ImportConfigTomlInput { + workspace_id: self.workspace_id + , + org_id: self.org_id + , + mode: self.mode + , + overwrite: self.overwrite + , + on_error: self.on_error + , + dry_run: self.dry_run + , + value_merge: self.value_merge + , + config_tags: self.config_tags + , + toml_config: self.toml_config + , + } + ) + } +} + diff --git a/crates/superposition_sdk/src/operation/import_config_toml/_import_config_toml_output.rs b/crates/superposition_sdk/src/operation/import_config_toml/_import_config_toml_output.rs new file mode 100644 index 000000000..36a83f51d --- /dev/null +++ b/crates/superposition_sdk/src/operation/import_config_toml/_import_config_toml_output.rs @@ -0,0 +1,189 @@ +// Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT. + +/// Summary of what an import created, updated, skipped or deleted. +#[non_exhaustive] +#[derive(::std::clone::Clone, ::std::cmp::PartialEq, ::std::fmt::Debug)] +pub struct ImportConfigTomlOutput { + #[allow(missing_docs)] // documentation missing in model + pub mode: ::std::string::String, + #[allow(missing_docs)] // documentation missing in model + pub dry_run: bool, + #[allow(missing_docs)] // documentation missing in model + pub config_version: ::std::option::Option<::std::string::String>, + /// Per-entity outcome counts for an import. + pub dimensions: crate::types::ImportEntityReport, + /// Per-entity outcome counts for an import. + pub default_configs: crate::types::ImportEntityReport, + /// Per-entity outcome counts for an import. + pub contexts: crate::types::ImportEntityReport, +} +impl ImportConfigTomlOutput { + #[allow(missing_docs)] // documentation missing in model + pub fn mode(&self) -> &str { + use std::ops::Deref; self.mode.deref() + } + #[allow(missing_docs)] // documentation missing in model + pub fn dry_run(&self) -> bool { + self.dry_run + } + #[allow(missing_docs)] // documentation missing in model + pub fn config_version(&self) -> ::std::option::Option<&str> { + self.config_version.as_deref() + } + /// Per-entity outcome counts for an import. + pub fn dimensions(&self) -> &crate::types::ImportEntityReport { + &self.dimensions + } + /// Per-entity outcome counts for an import. + pub fn default_configs(&self) -> &crate::types::ImportEntityReport { + &self.default_configs + } + /// Per-entity outcome counts for an import. + pub fn contexts(&self) -> &crate::types::ImportEntityReport { + &self.contexts + } +} +impl ImportConfigTomlOutput { + /// Creates a new builder-style object to manufacture [`ImportConfigTomlOutput`](crate::operation::import_config_toml::ImportConfigTomlOutput). + pub fn builder() -> crate::operation::import_config_toml::builders::ImportConfigTomlOutputBuilder { + crate::operation::import_config_toml::builders::ImportConfigTomlOutputBuilder::default() + } +} + +/// A builder for [`ImportConfigTomlOutput`](crate::operation::import_config_toml::ImportConfigTomlOutput). +#[derive(::std::clone::Clone, ::std::cmp::PartialEq, ::std::default::Default, ::std::fmt::Debug)] +#[non_exhaustive] +pub struct ImportConfigTomlOutputBuilder { + pub(crate) mode: ::std::option::Option<::std::string::String>, + pub(crate) dry_run: ::std::option::Option, + pub(crate) config_version: ::std::option::Option<::std::string::String>, + pub(crate) dimensions: ::std::option::Option, + pub(crate) default_configs: ::std::option::Option, + pub(crate) contexts: ::std::option::Option, +} +impl ImportConfigTomlOutputBuilder { + #[allow(missing_docs)] // documentation missing in model + /// This field is required. + pub fn mode(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { + self.mode = ::std::option::Option::Some(input.into()); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_mode(mut self, input: ::std::option::Option<::std::string::String>) -> Self { + self.mode = input; self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_mode(&self) -> &::std::option::Option<::std::string::String> { + &self.mode + } + #[allow(missing_docs)] // documentation missing in model + /// This field is required. + pub fn dry_run(mut self, input: bool) -> Self { + self.dry_run = ::std::option::Option::Some(input); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_dry_run(mut self, input: ::std::option::Option) -> Self { + self.dry_run = input; self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_dry_run(&self) -> &::std::option::Option { + &self.dry_run + } + #[allow(missing_docs)] // documentation missing in model + pub fn config_version(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { + self.config_version = ::std::option::Option::Some(input.into()); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_config_version(mut self, input: ::std::option::Option<::std::string::String>) -> Self { + self.config_version = input; self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_config_version(&self) -> &::std::option::Option<::std::string::String> { + &self.config_version + } + /// Per-entity outcome counts for an import. + /// This field is required. + pub fn dimensions(mut self, input: crate::types::ImportEntityReport) -> Self { + self.dimensions = ::std::option::Option::Some(input); + self + } + /// Per-entity outcome counts for an import. + pub fn set_dimensions(mut self, input: ::std::option::Option) -> Self { + self.dimensions = input; self + } + /// Per-entity outcome counts for an import. + pub fn get_dimensions(&self) -> &::std::option::Option { + &self.dimensions + } + /// Per-entity outcome counts for an import. + /// This field is required. + pub fn default_configs(mut self, input: crate::types::ImportEntityReport) -> Self { + self.default_configs = ::std::option::Option::Some(input); + self + } + /// Per-entity outcome counts for an import. + pub fn set_default_configs(mut self, input: ::std::option::Option) -> Self { + self.default_configs = input; self + } + /// Per-entity outcome counts for an import. + pub fn get_default_configs(&self) -> &::std::option::Option { + &self.default_configs + } + /// Per-entity outcome counts for an import. + /// This field is required. + pub fn contexts(mut self, input: crate::types::ImportEntityReport) -> Self { + self.contexts = ::std::option::Option::Some(input); + self + } + /// Per-entity outcome counts for an import. + pub fn set_contexts(mut self, input: ::std::option::Option) -> Self { + self.contexts = input; self + } + /// Per-entity outcome counts for an import. + pub fn get_contexts(&self) -> &::std::option::Option { + &self.contexts + } + /// Consumes the builder and constructs a [`ImportConfigTomlOutput`](crate::operation::import_config_toml::ImportConfigTomlOutput). + /// This method will fail if any of the following fields are not set: + /// - [`mode`](crate::operation::import_config_toml::builders::ImportConfigTomlOutputBuilder::mode) + /// - [`dry_run`](crate::operation::import_config_toml::builders::ImportConfigTomlOutputBuilder::dry_run) + /// - [`dimensions`](crate::operation::import_config_toml::builders::ImportConfigTomlOutputBuilder::dimensions) + /// - [`default_configs`](crate::operation::import_config_toml::builders::ImportConfigTomlOutputBuilder::default_configs) + /// - [`contexts`](crate::operation::import_config_toml::builders::ImportConfigTomlOutputBuilder::contexts) + pub fn build(self) -> ::std::result::Result { + ::std::result::Result::Ok( + crate::operation::import_config_toml::ImportConfigTomlOutput { + mode: self.mode + .ok_or_else(|| + ::aws_smithy_types::error::operation::BuildError::missing_field("mode", "mode was not specified but it is required when building ImportConfigTomlOutput") + )? + , + dry_run: self.dry_run + .ok_or_else(|| + ::aws_smithy_types::error::operation::BuildError::missing_field("dry_run", "dry_run was not specified but it is required when building ImportConfigTomlOutput") + )? + , + config_version: self.config_version + , + dimensions: self.dimensions + .ok_or_else(|| + ::aws_smithy_types::error::operation::BuildError::missing_field("dimensions", "dimensions was not specified but it is required when building ImportConfigTomlOutput") + )? + , + default_configs: self.default_configs + .ok_or_else(|| + ::aws_smithy_types::error::operation::BuildError::missing_field("default_configs", "default_configs was not specified but it is required when building ImportConfigTomlOutput") + )? + , + contexts: self.contexts + .ok_or_else(|| + ::aws_smithy_types::error::operation::BuildError::missing_field("contexts", "contexts was not specified but it is required when building ImportConfigTomlOutput") + )? + , + } + ) + } +} + diff --git a/crates/superposition_sdk/src/operation/import_config_toml/builders.rs b/crates/superposition_sdk/src/operation/import_config_toml/builders.rs new file mode 100644 index 000000000..169656a72 --- /dev/null +++ b/crates/superposition_sdk/src/operation/import_config_toml/builders.rs @@ -0,0 +1,226 @@ +// Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT. +pub use crate::operation::import_config_toml::_import_config_toml_output::ImportConfigTomlOutputBuilder; + +pub use crate::operation::import_config_toml::_import_config_toml_input::ImportConfigTomlInputBuilder; + +impl crate::operation::import_config_toml::builders::ImportConfigTomlInputBuilder { + /// Sends a request with this input using the given client. + pub async fn send_with(self, client: &crate::Client) -> ::std::result::Result< + crate::operation::import_config_toml::ImportConfigTomlOutput, + ::aws_smithy_runtime_api::client::result::SdkError< + crate::operation::import_config_toml::ImportConfigTomlError, + ::aws_smithy_runtime_api::client::orchestrator::HttpResponse + > + > { + let mut fluent_builder = client.import_config_toml(); + fluent_builder.inner = self; + fluent_builder.send().await + } + } +/// Fluent builder constructing a request to `ImportConfigToml`. +/// +/// Imports a full config from a TOML document, persisting dimensions, default-configs and contexts in a single transaction after validating the document. +#[derive(::std::clone::Clone, ::std::fmt::Debug)] +pub struct ImportConfigTomlFluentBuilder { + handle: ::std::sync::Arc, + inner: crate::operation::import_config_toml::builders::ImportConfigTomlInputBuilder, +config_override: ::std::option::Option, + } +impl + crate::client::customize::internal::CustomizableSend< + crate::operation::import_config_toml::ImportConfigTomlOutput, + crate::operation::import_config_toml::ImportConfigTomlError, + > for ImportConfigTomlFluentBuilder + { + fn send( + self, + config_override: crate::config::Builder, + ) -> crate::client::customize::internal::BoxFuture< + crate::client::customize::internal::SendResult< + crate::operation::import_config_toml::ImportConfigTomlOutput, + crate::operation::import_config_toml::ImportConfigTomlError, + >, + > { + ::std::boxed::Box::pin(async move { self.config_override(config_override).send().await }) + } + } +impl ImportConfigTomlFluentBuilder { + /// Creates a new `ImportConfigTomlFluentBuilder`. + pub(crate) fn new(handle: ::std::sync::Arc) -> Self { + Self { + handle, + inner: ::std::default::Default::default(), + config_override: ::std::option::Option::None, + } + } + /// Access the ImportConfigToml as a reference. + pub fn as_input(&self) -> &crate::operation::import_config_toml::builders::ImportConfigTomlInputBuilder { + &self.inner + } + /// Sends the request and returns the response. + /// + /// If an error occurs, an `SdkError` will be returned with additional details that + /// can be matched against. + /// + /// By default, any retryable failures will be retried twice. Retry behavior + /// is configurable with the [RetryConfig](aws_smithy_types::retry::RetryConfig), which can be + /// set when configuring the client. + pub async fn send(self) -> ::std::result::Result> { + let input = self.inner.build().map_err(::aws_smithy_runtime_api::client::result::SdkError::construction_failure)?; + let runtime_plugins = crate::operation::import_config_toml::ImportConfigToml::operation_runtime_plugins( + self.handle.runtime_plugins.clone(), + &self.handle.conf, + self.config_override, + ); + crate::operation::import_config_toml::ImportConfigToml::orchestrate(&runtime_plugins, input).await + } + + /// Consumes this builder, creating a customizable operation that can be modified before being sent. + pub fn customize( + self, + ) -> crate::client::customize::CustomizableOperation { + crate::client::customize::CustomizableOperation::new(self) + } + pub(crate) fn config_override( + mut self, + config_override: impl ::std::convert::Into, + ) -> Self { + self.set_config_override(::std::option::Option::Some(config_override.into())); + self + } + + pub(crate) fn set_config_override( + &mut self, + config_override: ::std::option::Option, + ) -> &mut Self { + self.config_override = config_override; + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn workspace_id(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { + self.inner = self.inner.workspace_id(input.into()); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_workspace_id(mut self, input: ::std::option::Option<::std::string::String>) -> Self { + self.inner = self.inner.set_workspace_id(input); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_workspace_id(&self) -> &::std::option::Option<::std::string::String> { + self.inner.get_workspace_id() + } + #[allow(missing_docs)] // documentation missing in model + pub fn org_id(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { + self.inner = self.inner.org_id(input.into()); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_org_id(mut self, input: ::std::option::Option<::std::string::String>) -> Self { + self.inner = self.inner.set_org_id(input); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_org_id(&self) -> &::std::option::Option<::std::string::String> { + self.inner.get_org_id() + } + /// Whether to merge (default) or replace existing workspace config. + pub fn mode(mut self, input: crate::types::ImportMode) -> Self { + self.inner = self.inner.mode(input); + self + } + /// Whether to merge (default) or replace existing workspace config. + pub fn set_mode(mut self, input: ::std::option::Option) -> Self { + self.inner = self.inner.set_mode(input); + self + } + /// Whether to merge (default) or replace existing workspace config. + pub fn get_mode(&self) -> &::std::option::Option { + self.inner.get_mode() + } + /// When false, entities that already exist are skipped instead of updated. Defaults to true. + pub fn overwrite(mut self, input: bool) -> Self { + self.inner = self.inner.overwrite(input); + self + } + /// When false, entities that already exist are skipped instead of updated. Defaults to true. + pub fn set_overwrite(mut self, input: ::std::option::Option) -> Self { + self.inner = self.inner.set_overwrite(input); + self + } + /// When false, entities that already exist are skipped instead of updated. Defaults to true. + pub fn get_overwrite(&self) -> &::std::option::Option { + self.inner.get_overwrite() + } + /// Whether to abort (default) or continue on per-entity errors. + pub fn on_error(mut self, input: crate::types::ImportOnError) -> Self { + self.inner = self.inner.on_error(input); + self + } + /// Whether to abort (default) or continue on per-entity errors. + pub fn set_on_error(mut self, input: ::std::option::Option) -> Self { + self.inner = self.inner.set_on_error(input); + self + } + /// Whether to abort (default) or continue on per-entity errors. + pub fn get_on_error(&self) -> &::std::option::Option { + self.inner.get_on_error() + } + /// When true, validates and summarises the import without persisting anything. Defaults to false. + pub fn dry_run(mut self, input: bool) -> Self { + self.inner = self.inner.dry_run(input); + self + } + /// When true, validates and summarises the import without persisting anything. Defaults to false. + pub fn set_dry_run(mut self, input: ::std::option::Option) -> Self { + self.inner = self.inner.set_dry_run(input); + self + } + /// When true, validates and summarises the import without persisting anything. Defaults to false. + pub fn get_dry_run(&self) -> &::std::option::Option { + self.inner.get_dry_run() + } + /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. + pub fn value_merge(mut self, input: bool) -> Self { + self.inner = self.inner.value_merge(input); + self + } + /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. + pub fn set_value_merge(mut self, input: ::std::option::Option) -> Self { + self.inner = self.inner.set_value_merge(input); + self + } + /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. + pub fn get_value_merge(&self) -> &::std::option::Option { + self.inner.get_value_merge() + } + #[allow(missing_docs)] // documentation missing in model + pub fn config_tags(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { + self.inner = self.inner.config_tags(input.into()); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_config_tags(mut self, input: ::std::option::Option<::std::string::String>) -> Self { + self.inner = self.inner.set_config_tags(input); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_config_tags(&self) -> &::std::option::Option<::std::string::String> { + self.inner.get_config_tags() + } + #[allow(missing_docs)] // documentation missing in model + pub fn toml_config(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { + self.inner = self.inner.toml_config(input.into()); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_toml_config(mut self, input: ::std::option::Option<::std::string::String>) -> Self { + self.inner = self.inner.set_toml_config(input); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_toml_config(&self) -> &::std::option::Option<::std::string::String> { + self.inner.get_toml_config() + } +} + diff --git a/crates/superposition_sdk/src/protocol_serde.rs b/crates/superposition_sdk/src/protocol_serde.rs index a17bc9030..1c0f45da8 100644 --- a/crates/superposition_sdk/src/protocol_serde.rs +++ b/crates/superposition_sdk/src/protocol_serde.rs @@ -127,6 +127,14 @@ pub(crate) mod shape_get_webhook_by_event; pub(crate) mod shape_get_workspace; +pub(crate) mod shape_import_config_json; + +pub(crate) mod shape_import_config_json_input; + +pub(crate) mod shape_import_config_toml; + +pub(crate) mod shape_import_config_toml_input; + pub(crate) mod shape_list_audit_logs; pub(crate) mod shape_list_contexts; @@ -365,6 +373,8 @@ pub(crate) mod shape_function_execution_request; pub(crate) mod shape_function_list_response; +pub(crate) mod shape_import_entity_report; + pub(crate) mod shape_list_context_out; pub(crate) mod shape_list_default_config_out; @@ -439,6 +449,8 @@ pub(crate) mod shape_experiment_response; pub(crate) mod shape_function_response; +pub(crate) mod shape_import_error_list; + pub(crate) mod shape_list_versions_member; pub(crate) mod shape_organisation_response; @@ -459,6 +471,8 @@ pub(crate) mod shape_weight_recompute_response; pub(crate) mod shape_workspace_response; +pub(crate) mod shape_import_error_item; + pub(crate) mod shape_override_with_keys; pub(crate) mod shape_resolve_explanation_timeline; diff --git a/crates/superposition_sdk/src/protocol_serde/shape_import_config_json.rs b/crates/superposition_sdk/src/protocol_serde/shape_import_config_json.rs new file mode 100644 index 000000000..6d48cd5af --- /dev/null +++ b/crates/superposition_sdk/src/protocol_serde/shape_import_config_json.rs @@ -0,0 +1,208 @@ +// Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT. +#[allow(clippy::unnecessary_wraps)] +pub fn de_import_config_json_http_error(_response_status: u16, _response_headers: &::aws_smithy_runtime_api::http::Headers, _response_body: &[u8]) -> std::result::Result { + #[allow(unused_mut)] + let mut generic_builder = crate::protocol_serde::parse_http_error_metadata(_response_status, _response_headers, _response_body).map_err(crate::operation::import_config_json::ImportConfigJsonError::unhandled)?; + let generic = generic_builder.build(); + let error_code = match generic.code() { + Some(code) => code, + None => return Err(crate::operation::import_config_json::ImportConfigJsonError::unhandled(generic)) + }; + + let _error_message = generic.message().map(|msg|msg.to_owned()); + Err(match error_code { + "InternalServerError" => crate::operation::import_config_json::ImportConfigJsonError::InternalServerError({ + #[allow(unused_mut)] + let mut tmp = + { + #[allow(unused_mut)] + let mut output = crate::types::error::builders::InternalServerErrorBuilder::default(); + output = crate::protocol_serde::shape_internal_server_error::de_internal_server_error_json_err(_response_body, output).map_err(crate::operation::import_config_json::ImportConfigJsonError::unhandled)?; + let output = output.meta(generic); + output.build() + } + ; + if tmp.message.is_none() { + tmp.message = _error_message; + } + tmp + }), + _ => crate::operation::import_config_json::ImportConfigJsonError::generic(generic) + }) +} + +#[allow(clippy::unnecessary_wraps)] +pub fn de_import_config_json_http_response(_response_status: u16, _response_headers: &::aws_smithy_runtime_api::http::Headers, _response_body: &[u8]) -> std::result::Result { + Ok({ + #[allow(unused_mut)] + let mut output = crate::operation::import_config_json::builders::ImportConfigJsonOutputBuilder::default(); + output = crate::protocol_serde::shape_import_config_json::de_import_config_json(_response_body, output).map_err(crate::operation::import_config_json::ImportConfigJsonError::unhandled)?; + crate::serde_util::import_config_json_output_output_correct_errors(output).build().map_err(crate::operation::import_config_json::ImportConfigJsonError::unhandled)? + }) +} + +pub fn ser_import_config_json_headers( + input: &crate::operation::import_config_json::ImportConfigJsonInput, + mut builder: ::http::request::Builder + ) -> std::result::Result<::http::request::Builder, ::aws_smithy_types::error::operation::BuildError> { + if let ::std::option::Option::Some(inner_1) = &input.workspace_id { + let formatted_2 = inner_1.as_str(); + let header_value = formatted_2; + let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { + ::aws_smithy_types::error::operation::BuildError::invalid_field("workspace_id", format!( + "`{}` cannot be used as a header value: {}", + &header_value, + err + )) + })?; + builder = builder.header("x-workspace", header_value); + } + if let ::std::option::Option::Some(inner_3) = &input.org_id { + let formatted_4 = inner_3.as_str(); + let header_value = formatted_4; + let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { + ::aws_smithy_types::error::operation::BuildError::invalid_field("org_id", format!( + "`{}` cannot be used as a header value: {}", + &header_value, + err + )) + })?; + builder = builder.header("x-org-id", header_value); + } + if let ::std::option::Option::Some(inner_5) = &input.mode { + let formatted_6 = inner_5.as_str(); + let header_value = formatted_6; + let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { + ::aws_smithy_types::error::operation::BuildError::invalid_field("mode", format!( + "`{}` cannot be used as a header value: {}", + &header_value, + err + )) + })?; + builder = builder.header("x-import-mode", header_value); + } + if let ::std::option::Option::Some(inner_7) = &input.overwrite { + let mut encoder = ::aws_smithy_types::primitive::Encoder::from(*inner_7); + let formatted_8 = encoder.encode(); + let header_value = formatted_8; + let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { + ::aws_smithy_types::error::operation::BuildError::invalid_field("overwrite", format!( + "`{}` cannot be used as a header value: {}", + &header_value, + err + )) + })?; + builder = builder.header("x-import-overwrite", header_value); + } + if let ::std::option::Option::Some(inner_9) = &input.on_error { + let formatted_10 = inner_9.as_str(); + let header_value = formatted_10; + let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { + ::aws_smithy_types::error::operation::BuildError::invalid_field("on_error", format!( + "`{}` cannot be used as a header value: {}", + &header_value, + err + )) + })?; + builder = builder.header("x-import-on-error", header_value); + } + if let ::std::option::Option::Some(inner_11) = &input.dry_run { + let mut encoder = ::aws_smithy_types::primitive::Encoder::from(*inner_11); + let formatted_12 = encoder.encode(); + let header_value = formatted_12; + let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { + ::aws_smithy_types::error::operation::BuildError::invalid_field("dry_run", format!( + "`{}` cannot be used as a header value: {}", + &header_value, + err + )) + })?; + builder = builder.header("x-import-dry-run", header_value); + } + if let ::std::option::Option::Some(inner_13) = &input.value_merge { + let mut encoder = ::aws_smithy_types::primitive::Encoder::from(*inner_13); + let formatted_14 = encoder.encode(); + let header_value = formatted_14; + let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { + ::aws_smithy_types::error::operation::BuildError::invalid_field("value_merge", format!( + "`{}` cannot be used as a header value: {}", + &header_value, + err + )) + })?; + builder = builder.header("x-import-value-merge", header_value); + } + if let ::std::option::Option::Some(inner_15) = &input.config_tags { + let formatted_16 = inner_15.as_str(); + let header_value = formatted_16; + let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { + ::aws_smithy_types::error::operation::BuildError::invalid_field("config_tags", format!( + "`{}` cannot be used as a header value: {}", + &header_value, + err + )) + })?; + builder = builder.header("x-config-tags", header_value); + } + Ok(builder) +} + +pub(crate) fn de_import_config_json(value: &[u8], mut builder: crate::operation::import_config_json::builders::ImportConfigJsonOutputBuilder) -> ::std::result::Result { + let mut tokens_owned = ::aws_smithy_json::deserialize::json_token_iter(crate::protocol_serde::or_empty_doc(value)).peekable(); + let tokens = &mut tokens_owned; + ::aws_smithy_json::deserialize::token::expect_start_object(tokens.next())?; + loop { + match tokens.next().transpose()? { + Some(::aws_smithy_json::deserialize::Token::EndObject { .. }) => break, + Some(::aws_smithy_json::deserialize::Token::ObjectKey { key, .. }) => { + match key.to_unescaped()?.as_ref() { + "config_version" => { + builder = builder.set_config_version( + ::aws_smithy_json::deserialize::token::expect_string_or_null(tokens.next())?.map(|s| + s.to_unescaped().map(|u| + u.into_owned() + ) + ).transpose()? + ); + } + "contexts" => { + builder = builder.set_contexts( + crate::protocol_serde::shape_import_entity_report::de_import_entity_report(tokens)? + ); + } + "default_configs" => { + builder = builder.set_default_configs( + crate::protocol_serde::shape_import_entity_report::de_import_entity_report(tokens)? + ); + } + "dimensions" => { + builder = builder.set_dimensions( + crate::protocol_serde::shape_import_entity_report::de_import_entity_report(tokens)? + ); + } + "dry_run" => { + builder = builder.set_dry_run( + ::aws_smithy_json::deserialize::token::expect_bool_or_null(tokens.next())? + ); + } + "mode" => { + builder = builder.set_mode( + ::aws_smithy_json::deserialize::token::expect_string_or_null(tokens.next())?.map(|s| + s.to_unescaped().map(|u| + u.into_owned() + ) + ).transpose()? + ); + } + _ => ::aws_smithy_json::deserialize::token::skip_value(tokens)? + } + } + other => return Err(::aws_smithy_json::deserialize::error::DeserializeError::custom(format!("expected object key or end object, found: {:?}", other))) + } + } + if tokens.next().is_some() { + return Err(::aws_smithy_json::deserialize::error::DeserializeError::custom("found more JSON tokens after completing parsing")); + } + Ok(builder) +} + diff --git a/crates/superposition_sdk/src/protocol_serde/shape_import_config_json_input.rs b/crates/superposition_sdk/src/protocol_serde/shape_import_config_json_input.rs new file mode 100644 index 000000000..08154789b --- /dev/null +++ b/crates/superposition_sdk/src/protocol_serde/shape_import_config_json_input.rs @@ -0,0 +1,12 @@ +// Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT. +pub fn ser_json_config_http_payload(payload: ::std::option::Option<::std::string::String>) -> ::std::result::Result<::std::vec::Vec, ::aws_smithy_types::error::operation::BuildError> { + let payload = match payload { + Some(t) => t, + None => return Ok( + Vec::new() + )}; + Ok( + payload.into_bytes() + ) +} + diff --git a/crates/superposition_sdk/src/protocol_serde/shape_import_config_toml.rs b/crates/superposition_sdk/src/protocol_serde/shape_import_config_toml.rs new file mode 100644 index 000000000..00cea09fa --- /dev/null +++ b/crates/superposition_sdk/src/protocol_serde/shape_import_config_toml.rs @@ -0,0 +1,208 @@ +// Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT. +#[allow(clippy::unnecessary_wraps)] +pub fn de_import_config_toml_http_error(_response_status: u16, _response_headers: &::aws_smithy_runtime_api::http::Headers, _response_body: &[u8]) -> std::result::Result { + #[allow(unused_mut)] + let mut generic_builder = crate::protocol_serde::parse_http_error_metadata(_response_status, _response_headers, _response_body).map_err(crate::operation::import_config_toml::ImportConfigTomlError::unhandled)?; + let generic = generic_builder.build(); + let error_code = match generic.code() { + Some(code) => code, + None => return Err(crate::operation::import_config_toml::ImportConfigTomlError::unhandled(generic)) + }; + + let _error_message = generic.message().map(|msg|msg.to_owned()); + Err(match error_code { + "InternalServerError" => crate::operation::import_config_toml::ImportConfigTomlError::InternalServerError({ + #[allow(unused_mut)] + let mut tmp = + { + #[allow(unused_mut)] + let mut output = crate::types::error::builders::InternalServerErrorBuilder::default(); + output = crate::protocol_serde::shape_internal_server_error::de_internal_server_error_json_err(_response_body, output).map_err(crate::operation::import_config_toml::ImportConfigTomlError::unhandled)?; + let output = output.meta(generic); + output.build() + } + ; + if tmp.message.is_none() { + tmp.message = _error_message; + } + tmp + }), + _ => crate::operation::import_config_toml::ImportConfigTomlError::generic(generic) + }) +} + +#[allow(clippy::unnecessary_wraps)] +pub fn de_import_config_toml_http_response(_response_status: u16, _response_headers: &::aws_smithy_runtime_api::http::Headers, _response_body: &[u8]) -> std::result::Result { + Ok({ + #[allow(unused_mut)] + let mut output = crate::operation::import_config_toml::builders::ImportConfigTomlOutputBuilder::default(); + output = crate::protocol_serde::shape_import_config_toml::de_import_config_toml(_response_body, output).map_err(crate::operation::import_config_toml::ImportConfigTomlError::unhandled)?; + crate::serde_util::import_config_toml_output_output_correct_errors(output).build().map_err(crate::operation::import_config_toml::ImportConfigTomlError::unhandled)? + }) +} + +pub fn ser_import_config_toml_headers( + input: &crate::operation::import_config_toml::ImportConfigTomlInput, + mut builder: ::http::request::Builder + ) -> std::result::Result<::http::request::Builder, ::aws_smithy_types::error::operation::BuildError> { + if let ::std::option::Option::Some(inner_1) = &input.workspace_id { + let formatted_2 = inner_1.as_str(); + let header_value = formatted_2; + let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { + ::aws_smithy_types::error::operation::BuildError::invalid_field("workspace_id", format!( + "`{}` cannot be used as a header value: {}", + &header_value, + err + )) + })?; + builder = builder.header("x-workspace", header_value); + } + if let ::std::option::Option::Some(inner_3) = &input.org_id { + let formatted_4 = inner_3.as_str(); + let header_value = formatted_4; + let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { + ::aws_smithy_types::error::operation::BuildError::invalid_field("org_id", format!( + "`{}` cannot be used as a header value: {}", + &header_value, + err + )) + })?; + builder = builder.header("x-org-id", header_value); + } + if let ::std::option::Option::Some(inner_5) = &input.mode { + let formatted_6 = inner_5.as_str(); + let header_value = formatted_6; + let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { + ::aws_smithy_types::error::operation::BuildError::invalid_field("mode", format!( + "`{}` cannot be used as a header value: {}", + &header_value, + err + )) + })?; + builder = builder.header("x-import-mode", header_value); + } + if let ::std::option::Option::Some(inner_7) = &input.overwrite { + let mut encoder = ::aws_smithy_types::primitive::Encoder::from(*inner_7); + let formatted_8 = encoder.encode(); + let header_value = formatted_8; + let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { + ::aws_smithy_types::error::operation::BuildError::invalid_field("overwrite", format!( + "`{}` cannot be used as a header value: {}", + &header_value, + err + )) + })?; + builder = builder.header("x-import-overwrite", header_value); + } + if let ::std::option::Option::Some(inner_9) = &input.on_error { + let formatted_10 = inner_9.as_str(); + let header_value = formatted_10; + let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { + ::aws_smithy_types::error::operation::BuildError::invalid_field("on_error", format!( + "`{}` cannot be used as a header value: {}", + &header_value, + err + )) + })?; + builder = builder.header("x-import-on-error", header_value); + } + if let ::std::option::Option::Some(inner_11) = &input.dry_run { + let mut encoder = ::aws_smithy_types::primitive::Encoder::from(*inner_11); + let formatted_12 = encoder.encode(); + let header_value = formatted_12; + let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { + ::aws_smithy_types::error::operation::BuildError::invalid_field("dry_run", format!( + "`{}` cannot be used as a header value: {}", + &header_value, + err + )) + })?; + builder = builder.header("x-import-dry-run", header_value); + } + if let ::std::option::Option::Some(inner_13) = &input.value_merge { + let mut encoder = ::aws_smithy_types::primitive::Encoder::from(*inner_13); + let formatted_14 = encoder.encode(); + let header_value = formatted_14; + let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { + ::aws_smithy_types::error::operation::BuildError::invalid_field("value_merge", format!( + "`{}` cannot be used as a header value: {}", + &header_value, + err + )) + })?; + builder = builder.header("x-import-value-merge", header_value); + } + if let ::std::option::Option::Some(inner_15) = &input.config_tags { + let formatted_16 = inner_15.as_str(); + let header_value = formatted_16; + let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { + ::aws_smithy_types::error::operation::BuildError::invalid_field("config_tags", format!( + "`{}` cannot be used as a header value: {}", + &header_value, + err + )) + })?; + builder = builder.header("x-config-tags", header_value); + } + Ok(builder) +} + +pub(crate) fn de_import_config_toml(value: &[u8], mut builder: crate::operation::import_config_toml::builders::ImportConfigTomlOutputBuilder) -> ::std::result::Result { + let mut tokens_owned = ::aws_smithy_json::deserialize::json_token_iter(crate::protocol_serde::or_empty_doc(value)).peekable(); + let tokens = &mut tokens_owned; + ::aws_smithy_json::deserialize::token::expect_start_object(tokens.next())?; + loop { + match tokens.next().transpose()? { + Some(::aws_smithy_json::deserialize::Token::EndObject { .. }) => break, + Some(::aws_smithy_json::deserialize::Token::ObjectKey { key, .. }) => { + match key.to_unescaped()?.as_ref() { + "config_version" => { + builder = builder.set_config_version( + ::aws_smithy_json::deserialize::token::expect_string_or_null(tokens.next())?.map(|s| + s.to_unescaped().map(|u| + u.into_owned() + ) + ).transpose()? + ); + } + "contexts" => { + builder = builder.set_contexts( + crate::protocol_serde::shape_import_entity_report::de_import_entity_report(tokens)? + ); + } + "default_configs" => { + builder = builder.set_default_configs( + crate::protocol_serde::shape_import_entity_report::de_import_entity_report(tokens)? + ); + } + "dimensions" => { + builder = builder.set_dimensions( + crate::protocol_serde::shape_import_entity_report::de_import_entity_report(tokens)? + ); + } + "dry_run" => { + builder = builder.set_dry_run( + ::aws_smithy_json::deserialize::token::expect_bool_or_null(tokens.next())? + ); + } + "mode" => { + builder = builder.set_mode( + ::aws_smithy_json::deserialize::token::expect_string_or_null(tokens.next())?.map(|s| + s.to_unescaped().map(|u| + u.into_owned() + ) + ).transpose()? + ); + } + _ => ::aws_smithy_json::deserialize::token::skip_value(tokens)? + } + } + other => return Err(::aws_smithy_json::deserialize::error::DeserializeError::custom(format!("expected object key or end object, found: {:?}", other))) + } + } + if tokens.next().is_some() { + return Err(::aws_smithy_json::deserialize::error::DeserializeError::custom("found more JSON tokens after completing parsing")); + } + Ok(builder) +} + diff --git a/crates/superposition_sdk/src/protocol_serde/shape_import_config_toml_input.rs b/crates/superposition_sdk/src/protocol_serde/shape_import_config_toml_input.rs new file mode 100644 index 000000000..918968b0e --- /dev/null +++ b/crates/superposition_sdk/src/protocol_serde/shape_import_config_toml_input.rs @@ -0,0 +1,12 @@ +// Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT. +pub fn ser_toml_config_http_payload(payload: ::std::option::Option<::std::string::String>) -> ::std::result::Result<::std::vec::Vec, ::aws_smithy_types::error::operation::BuildError> { + let payload = match payload { + Some(t) => t, + None => return Ok( + Vec::new() + )}; + Ok( + payload.into_bytes() + ) +} + diff --git a/crates/superposition_sdk/src/protocol_serde/shape_import_entity_report.rs b/crates/superposition_sdk/src/protocol_serde/shape_import_entity_report.rs new file mode 100644 index 000000000..b8a1be7cc --- /dev/null +++ b/crates/superposition_sdk/src/protocol_serde/shape_import_entity_report.rs @@ -0,0 +1,60 @@ +// Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT. +pub(crate) fn de_import_entity_report<'a, I>(tokens: &mut ::std::iter::Peekable) -> ::std::result::Result, ::aws_smithy_json::deserialize::error::DeserializeError> + where I: Iterator, ::aws_smithy_json::deserialize::error::DeserializeError>> { + match tokens.next().transpose()? { + Some(::aws_smithy_json::deserialize::Token::ValueNull { .. }) => Ok(None), + Some(::aws_smithy_json::deserialize::Token::StartObject { .. }) => { + #[allow(unused_mut)] + let mut builder = crate::types::builders::ImportEntityReportBuilder::default(); + loop { + match tokens.next().transpose()? { + Some(::aws_smithy_json::deserialize::Token::EndObject { .. }) => break, + Some(::aws_smithy_json::deserialize::Token::ObjectKey { key, .. }) => { + match key.to_unescaped()?.as_ref() { + "created" => { + builder = builder.set_created( + ::aws_smithy_json::deserialize::token::expect_number_or_null(tokens.next())? + .map(i32::try_from) + .transpose()? + ); + } + "updated" => { + builder = builder.set_updated( + ::aws_smithy_json::deserialize::token::expect_number_or_null(tokens.next())? + .map(i32::try_from) + .transpose()? + ); + } + "skipped" => { + builder = builder.set_skipped( + ::aws_smithy_json::deserialize::token::expect_number_or_null(tokens.next())? + .map(i32::try_from) + .transpose()? + ); + } + "deleted" => { + builder = builder.set_deleted( + ::aws_smithy_json::deserialize::token::expect_number_or_null(tokens.next())? + .map(i32::try_from) + .transpose()? + ); + } + "errors" => { + builder = builder.set_errors( + crate::protocol_serde::shape_import_error_list::de_import_error_list(tokens)? + ); + } + _ => ::aws_smithy_json::deserialize::token::skip_value(tokens)? + } + } + other => return Err(::aws_smithy_json::deserialize::error::DeserializeError::custom(format!("expected object key or end object, found: {:?}", other))) + } + } + Ok(Some(crate::serde_util::import_entity_report_correct_errors(builder).build().map_err(|err|::aws_smithy_json::deserialize::error::DeserializeError::custom_source("Response was invalid", err))?)) + } + _ => { + Err(::aws_smithy_json::deserialize::error::DeserializeError::custom("expected start object or null")) + } + } +} + diff --git a/crates/superposition_sdk/src/protocol_serde/shape_import_error_item.rs b/crates/superposition_sdk/src/protocol_serde/shape_import_error_item.rs new file mode 100644 index 000000000..7e83007d0 --- /dev/null +++ b/crates/superposition_sdk/src/protocol_serde/shape_import_error_item.rs @@ -0,0 +1,45 @@ +// Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT. +pub(crate) fn de_import_error_item<'a, I>(tokens: &mut ::std::iter::Peekable) -> ::std::result::Result, ::aws_smithy_json::deserialize::error::DeserializeError> + where I: Iterator, ::aws_smithy_json::deserialize::error::DeserializeError>> { + match tokens.next().transpose()? { + Some(::aws_smithy_json::deserialize::Token::ValueNull { .. }) => Ok(None), + Some(::aws_smithy_json::deserialize::Token::StartObject { .. }) => { + #[allow(unused_mut)] + let mut builder = crate::types::builders::ImportErrorItemBuilder::default(); + loop { + match tokens.next().transpose()? { + Some(::aws_smithy_json::deserialize::Token::EndObject { .. }) => break, + Some(::aws_smithy_json::deserialize::Token::ObjectKey { key, .. }) => { + match key.to_unescaped()?.as_ref() { + "id" => { + builder = builder.set_id( + ::aws_smithy_json::deserialize::token::expect_string_or_null(tokens.next())?.map(|s| + s.to_unescaped().map(|u| + u.into_owned() + ) + ).transpose()? + ); + } + "error" => { + builder = builder.set_error( + ::aws_smithy_json::deserialize::token::expect_string_or_null(tokens.next())?.map(|s| + s.to_unescaped().map(|u| + u.into_owned() + ) + ).transpose()? + ); + } + _ => ::aws_smithy_json::deserialize::token::skip_value(tokens)? + } + } + other => return Err(::aws_smithy_json::deserialize::error::DeserializeError::custom(format!("expected object key or end object, found: {:?}", other))) + } + } + Ok(Some(crate::serde_util::import_error_item_correct_errors(builder).build().map_err(|err|::aws_smithy_json::deserialize::error::DeserializeError::custom_source("Response was invalid", err))?)) + } + _ => { + Err(::aws_smithy_json::deserialize::error::DeserializeError::custom("expected start object or null")) + } + } +} + diff --git a/crates/superposition_sdk/src/protocol_serde/shape_import_error_list.rs b/crates/superposition_sdk/src/protocol_serde/shape_import_error_list.rs new file mode 100644 index 000000000..29d185753 --- /dev/null +++ b/crates/superposition_sdk/src/protocol_serde/shape_import_error_list.rs @@ -0,0 +1,30 @@ +// Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT. +pub(crate) fn de_import_error_list<'a, I>(tokens: &mut ::std::iter::Peekable) -> ::std::result::Result>, ::aws_smithy_json::deserialize::error::DeserializeError> + where I: Iterator, ::aws_smithy_json::deserialize::error::DeserializeError>> { + match tokens.next().transpose()? { + Some(::aws_smithy_json::deserialize::Token::ValueNull { .. }) => Ok(None), + Some(::aws_smithy_json::deserialize::Token::StartArray { .. }) => { + let mut items = Vec::new(); + loop { + match tokens.peek() { + Some(Ok(::aws_smithy_json::deserialize::Token::EndArray { .. })) => { + tokens.next().transpose().unwrap(); break; + } + _ => { + let value = + crate::protocol_serde::shape_import_error_item::de_import_error_item(tokens)? + ; + if let Some(value) = value { + items.push(value); + } + } + } + } + Ok(Some(items)) + } + _ => { + Err(::aws_smithy_json::deserialize::error::DeserializeError::custom("expected start array or null")) + } + } +} + diff --git a/crates/superposition_sdk/src/serde_util.rs b/crates/superposition_sdk/src/serde_util.rs index 0344feacd..6f9d8818d 100644 --- a/crates/superposition_sdk/src/serde_util.rs +++ b/crates/superposition_sdk/src/serde_util.rs @@ -584,6 +584,24 @@ if builder.enable_change_reason_validation.is_none() { builder.enable_change_rea builder } +pub(crate) fn import_config_json_output_output_correct_errors(mut builder: crate::operation::import_config_json::builders::ImportConfigJsonOutputBuilder) -> crate::operation::import_config_json::builders::ImportConfigJsonOutputBuilder { + if builder.mode.is_none() { builder.mode = Some(Default::default()) } +if builder.dry_run.is_none() { builder.dry_run = Some(Default::default()) } +if builder.dimensions.is_none() { builder.dimensions = { let builder = crate::types::builders::ImportEntityReportBuilder::default(); crate::serde_util::import_entity_report_correct_errors(builder).build().ok() } } +if builder.default_configs.is_none() { builder.default_configs = { let builder = crate::types::builders::ImportEntityReportBuilder::default(); crate::serde_util::import_entity_report_correct_errors(builder).build().ok() } } +if builder.contexts.is_none() { builder.contexts = { let builder = crate::types::builders::ImportEntityReportBuilder::default(); crate::serde_util::import_entity_report_correct_errors(builder).build().ok() } } + builder + } + +pub(crate) fn import_config_toml_output_output_correct_errors(mut builder: crate::operation::import_config_toml::builders::ImportConfigTomlOutputBuilder) -> crate::operation::import_config_toml::builders::ImportConfigTomlOutputBuilder { + if builder.mode.is_none() { builder.mode = Some(Default::default()) } +if builder.dry_run.is_none() { builder.dry_run = Some(Default::default()) } +if builder.dimensions.is_none() { builder.dimensions = { let builder = crate::types::builders::ImportEntityReportBuilder::default(); crate::serde_util::import_entity_report_correct_errors(builder).build().ok() } } +if builder.default_configs.is_none() { builder.default_configs = { let builder = crate::types::builders::ImportEntityReportBuilder::default(); crate::serde_util::import_entity_report_correct_errors(builder).build().ok() } } +if builder.contexts.is_none() { builder.contexts = { let builder = crate::types::builders::ImportEntityReportBuilder::default(); crate::serde_util::import_entity_report_correct_errors(builder).build().ok() } } + builder + } + pub(crate) fn list_audit_logs_output_output_correct_errors(mut builder: crate::operation::list_audit_logs::builders::ListAuditLogsOutputBuilder) -> crate::operation::list_audit_logs::builders::ListAuditLogsOutputBuilder { if builder.total_pages.is_none() { builder.total_pages = Some(Default::default()) } if builder.total_items.is_none() { builder.total_items = Some(Default::default()) } @@ -1015,6 +1033,14 @@ if builder.dimensions.is_none() { builder.dimensions = Some(Default::default()) builder } +pub(crate) fn import_entity_report_correct_errors(mut builder: crate::types::builders::ImportEntityReportBuilder) -> crate::types::builders::ImportEntityReportBuilder { + if builder.created.is_none() { builder.created = Some(Default::default()) } +if builder.updated.is_none() { builder.updated = Some(Default::default()) } +if builder.skipped.is_none() { builder.skipped = Some(Default::default()) } +if builder.deleted.is_none() { builder.deleted = Some(Default::default()) } + builder + } + pub(crate) fn audit_log_full_correct_errors(mut builder: crate::types::builders::AuditLogFullBuilder) -> crate::types::builders::AuditLogFullBuilder { if builder.id.is_none() { builder.id = Some(Default::default()) } if builder.table_name.is_none() { builder.table_name = Some(Default::default()) } @@ -1257,3 +1283,8 @@ if builder.value_after.is_none() { builder.value_after = Some(Default::default() builder } +pub(crate) fn import_error_item_correct_errors(mut builder: crate::types::builders::ImportErrorItemBuilder) -> crate::types::builders::ImportErrorItemBuilder { + if builder.id.is_none() { builder.id = Some(Default::default()) } +if builder.error.is_none() { builder.error = Some(Default::default()) } + builder + } diff --git a/crates/superposition_sdk/src/types.rs b/crates/superposition_sdk/src/types.rs index b39d43f0e..cbdac46db 100644 --- a/crates/superposition_sdk/src/types.rs +++ b/crates/superposition_sdk/src/types.rs @@ -115,6 +115,14 @@ pub use crate::types::_dimension_response::DimensionResponse; pub use crate::types::_default_config_response::DefaultConfigResponse; +pub use crate::types::_import_entity_report::ImportEntityReport; + +pub use crate::types::_import_error_item::ImportErrorItem; + +pub use crate::types::_import_on_error::ImportOnError; + +pub use crate::types::_import_mode::ImportMode; + mod _audit_action; mod _audit_log_full; @@ -179,6 +187,14 @@ mod _group_type; mod _http_method; +mod _import_entity_report; + +mod _import_error_item; + +mod _import_mode; + +mod _import_on_error; + mod _list_versions_member; mod _merge_strategy; diff --git a/crates/superposition_sdk/src/types/_import_entity_report.rs b/crates/superposition_sdk/src/types/_import_entity_report.rs new file mode 100644 index 000000000..c97557bda --- /dev/null +++ b/crates/superposition_sdk/src/types/_import_entity_report.rs @@ -0,0 +1,170 @@ +// Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT. + +/// Per-entity outcome counts for an import. +#[non_exhaustive] +#[derive(::std::clone::Clone, ::std::cmp::PartialEq, ::std::fmt::Debug)] +pub struct ImportEntityReport { + #[allow(missing_docs)] // documentation missing in model + pub created: i32, + #[allow(missing_docs)] // documentation missing in model + pub updated: i32, + #[allow(missing_docs)] // documentation missing in model + pub skipped: i32, + #[allow(missing_docs)] // documentation missing in model + pub deleted: i32, + #[allow(missing_docs)] // documentation missing in model + pub errors: ::std::option::Option<::std::vec::Vec::>, +} +impl ImportEntityReport { + #[allow(missing_docs)] // documentation missing in model + pub fn created(&self) -> i32 { + self.created + } + #[allow(missing_docs)] // documentation missing in model + pub fn updated(&self) -> i32 { + self.updated + } + #[allow(missing_docs)] // documentation missing in model + pub fn skipped(&self) -> i32 { + self.skipped + } + #[allow(missing_docs)] // documentation missing in model + pub fn deleted(&self) -> i32 { + self.deleted + } + #[allow(missing_docs)] // documentation missing in model + /// + /// If no value was sent for this field, a default will be set. If you want to determine if no value was sent, use `.errors.is_none()`. + pub fn errors(&self) -> &[crate::types::ImportErrorItem] { + self.errors.as_deref() + .unwrap_or_default() + } +} +impl ImportEntityReport { + /// Creates a new builder-style object to manufacture [`ImportEntityReport`](crate::types::ImportEntityReport). + pub fn builder() -> crate::types::builders::ImportEntityReportBuilder { + crate::types::builders::ImportEntityReportBuilder::default() + } +} + +/// A builder for [`ImportEntityReport`](crate::types::ImportEntityReport). +#[derive(::std::clone::Clone, ::std::cmp::PartialEq, ::std::default::Default, ::std::fmt::Debug)] +#[non_exhaustive] +pub struct ImportEntityReportBuilder { + pub(crate) created: ::std::option::Option, + pub(crate) updated: ::std::option::Option, + pub(crate) skipped: ::std::option::Option, + pub(crate) deleted: ::std::option::Option, + pub(crate) errors: ::std::option::Option<::std::vec::Vec::>, +} +impl ImportEntityReportBuilder { + #[allow(missing_docs)] // documentation missing in model + /// This field is required. + pub fn created(mut self, input: i32) -> Self { + self.created = ::std::option::Option::Some(input); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_created(mut self, input: ::std::option::Option) -> Self { + self.created = input; self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_created(&self) -> &::std::option::Option { + &self.created + } + #[allow(missing_docs)] // documentation missing in model + /// This field is required. + pub fn updated(mut self, input: i32) -> Self { + self.updated = ::std::option::Option::Some(input); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_updated(mut self, input: ::std::option::Option) -> Self { + self.updated = input; self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_updated(&self) -> &::std::option::Option { + &self.updated + } + #[allow(missing_docs)] // documentation missing in model + /// This field is required. + pub fn skipped(mut self, input: i32) -> Self { + self.skipped = ::std::option::Option::Some(input); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_skipped(mut self, input: ::std::option::Option) -> Self { + self.skipped = input; self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_skipped(&self) -> &::std::option::Option { + &self.skipped + } + #[allow(missing_docs)] // documentation missing in model + /// This field is required. + pub fn deleted(mut self, input: i32) -> Self { + self.deleted = ::std::option::Option::Some(input); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_deleted(mut self, input: ::std::option::Option) -> Self { + self.deleted = input; self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_deleted(&self) -> &::std::option::Option { + &self.deleted + } + /// Appends an item to `errors`. + /// + /// To override the contents of this collection use [`set_errors`](Self::set_errors). + /// + pub fn errors(mut self, input: crate::types::ImportErrorItem) -> Self { + let mut v = self.errors.unwrap_or_default(); + v.push(input); + self.errors = ::std::option::Option::Some(v); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_errors(mut self, input: ::std::option::Option<::std::vec::Vec::>) -> Self { + self.errors = input; self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_errors(&self) -> &::std::option::Option<::std::vec::Vec::> { + &self.errors + } + /// Consumes the builder and constructs a [`ImportEntityReport`](crate::types::ImportEntityReport). + /// This method will fail if any of the following fields are not set: + /// - [`created`](crate::types::builders::ImportEntityReportBuilder::created) + /// - [`updated`](crate::types::builders::ImportEntityReportBuilder::updated) + /// - [`skipped`](crate::types::builders::ImportEntityReportBuilder::skipped) + /// - [`deleted`](crate::types::builders::ImportEntityReportBuilder::deleted) + pub fn build(self) -> ::std::result::Result { + ::std::result::Result::Ok( + crate::types::ImportEntityReport { + created: self.created + .ok_or_else(|| + ::aws_smithy_types::error::operation::BuildError::missing_field("created", "created was not specified but it is required when building ImportEntityReport") + )? + , + updated: self.updated + .ok_or_else(|| + ::aws_smithy_types::error::operation::BuildError::missing_field("updated", "updated was not specified but it is required when building ImportEntityReport") + )? + , + skipped: self.skipped + .ok_or_else(|| + ::aws_smithy_types::error::operation::BuildError::missing_field("skipped", "skipped was not specified but it is required when building ImportEntityReport") + )? + , + deleted: self.deleted + .ok_or_else(|| + ::aws_smithy_types::error::operation::BuildError::missing_field("deleted", "deleted was not specified but it is required when building ImportEntityReport") + )? + , + errors: self.errors + , + } + ) + } +} + diff --git a/crates/superposition_sdk/src/types/_import_error_item.rs b/crates/superposition_sdk/src/types/_import_error_item.rs new file mode 100644 index 000000000..aaf663a3b --- /dev/null +++ b/crates/superposition_sdk/src/types/_import_error_item.rs @@ -0,0 +1,85 @@ +// Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT. +#[allow(missing_docs)] // documentation missing in model +#[non_exhaustive] +#[derive(::std::clone::Clone, ::std::cmp::PartialEq, ::std::fmt::Debug)] +pub struct ImportErrorItem { + #[allow(missing_docs)] // documentation missing in model + pub id: ::std::string::String, + #[allow(missing_docs)] // documentation missing in model + pub error: ::std::string::String, +} +impl ImportErrorItem { + #[allow(missing_docs)] // documentation missing in model + pub fn id(&self) -> &str { + use std::ops::Deref; self.id.deref() + } + #[allow(missing_docs)] // documentation missing in model + pub fn error(&self) -> &str { + use std::ops::Deref; self.error.deref() + } +} +impl ImportErrorItem { + /// Creates a new builder-style object to manufacture [`ImportErrorItem`](crate::types::ImportErrorItem). + pub fn builder() -> crate::types::builders::ImportErrorItemBuilder { + crate::types::builders::ImportErrorItemBuilder::default() + } +} + +/// A builder for [`ImportErrorItem`](crate::types::ImportErrorItem). +#[derive(::std::clone::Clone, ::std::cmp::PartialEq, ::std::default::Default, ::std::fmt::Debug)] +#[non_exhaustive] +pub struct ImportErrorItemBuilder { + pub(crate) id: ::std::option::Option<::std::string::String>, + pub(crate) error: ::std::option::Option<::std::string::String>, +} +impl ImportErrorItemBuilder { + #[allow(missing_docs)] // documentation missing in model + /// This field is required. + pub fn id(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { + self.id = ::std::option::Option::Some(input.into()); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_id(mut self, input: ::std::option::Option<::std::string::String>) -> Self { + self.id = input; self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_id(&self) -> &::std::option::Option<::std::string::String> { + &self.id + } + #[allow(missing_docs)] // documentation missing in model + /// This field is required. + pub fn error(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { + self.error = ::std::option::Option::Some(input.into()); + self + } + #[allow(missing_docs)] // documentation missing in model + pub fn set_error(mut self, input: ::std::option::Option<::std::string::String>) -> Self { + self.error = input; self + } + #[allow(missing_docs)] // documentation missing in model + pub fn get_error(&self) -> &::std::option::Option<::std::string::String> { + &self.error + } + /// Consumes the builder and constructs a [`ImportErrorItem`](crate::types::ImportErrorItem). + /// This method will fail if any of the following fields are not set: + /// - [`id`](crate::types::builders::ImportErrorItemBuilder::id) + /// - [`error`](crate::types::builders::ImportErrorItemBuilder::error) + pub fn build(self) -> ::std::result::Result { + ::std::result::Result::Ok( + crate::types::ImportErrorItem { + id: self.id + .ok_or_else(|| + ::aws_smithy_types::error::operation::BuildError::missing_field("id", "id was not specified but it is required when building ImportErrorItem") + )? + , + error: self.error + .ok_or_else(|| + ::aws_smithy_types::error::operation::BuildError::missing_field("error", "error was not specified but it is required when building ImportErrorItem") + )? + , + } + ) + } +} + diff --git a/crates/superposition_sdk/src/types/_import_mode.rs b/crates/superposition_sdk/src/types/_import_mode.rs new file mode 100644 index 000000000..d916c5e61 --- /dev/null +++ b/crates/superposition_sdk/src/types/_import_mode.rs @@ -0,0 +1,107 @@ +// Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT. + +/// When writing a match expression against `ImportMode`, it is important to ensure +/// your code is forward-compatible. That is, if a match arm handles a case for a +/// feature that is supported by the service but has not been represented as an enum +/// variant in a current version of SDK, your code should continue to work when you +/// upgrade SDK to a future version in which the enum does include a variant for that +/// feature. +/// +/// Here is an example of how you can make a match expression forward-compatible: +/// +/// ```text +/// # let importmode = unimplemented!(); +/// match importmode { +/// ImportMode::Merge => { /* ... */ }, +/// ImportMode::Replace => { /* ... */ }, +/// other @ _ if other.as_str() == "NewFeature" => { /* handles a case for `NewFeature` */ }, +/// _ => { /* ... */ }, +/// } +/// ``` +/// The above code demonstrates that when `importmode` represents +/// `NewFeature`, the execution path will lead to the second last match arm, +/// even though the enum does not contain a variant `ImportMode::NewFeature` +/// in the current version of SDK. The reason is that the variable `other`, +/// created by the `@` operator, is bound to +/// `ImportMode::Unknown(UnknownVariantValue("NewFeature".to_owned()))` +/// and calling `as_str` on it yields `"NewFeature"`. +/// This match expression is forward-compatible when executed with a newer +/// version of SDK where the variant `ImportMode::NewFeature` is defined. +/// Specifically, when `importmode` represents `NewFeature`, +/// the execution path will hit the second last match arm as before by virtue of +/// calling `as_str` on `ImportMode::NewFeature` also yielding `"NewFeature"`. +/// +/// Explicitly matching on the `Unknown` variant should +/// be avoided for two reasons: +/// - The inner data `UnknownVariantValue` is opaque, and no further information can be extracted. +/// - It might inadvertently shadow other intended match arms. +/// +/// How an import treats workspace entities that are not present in the imported file. +#[non_exhaustive] +#[derive(::std::clone::Clone, ::std::cmp::Eq, ::std::cmp::Ord, ::std::cmp::PartialEq, ::std::cmp::PartialOrd, ::std::fmt::Debug, ::std::hash::Hash)] +pub enum ImportMode { + /// Upsert the entities in the file and leave everything else untouched. + Merge, + /// Mirror the file: additionally delete dimensions, default-configs and contexts that are absent from it. + Replace, + /// `Unknown` contains new variants that have been added since this code was generated. + #[deprecated(note = "Don't directly match on `Unknown`. See the docs on this enum for the correct way to handle unknown variants.")] + Unknown(crate::primitives::sealed_enum_unknown::UnknownVariantValue) +} +impl ::std::convert::From<&str> for ImportMode { + fn from(s: &str) -> Self { + match s { + "merge" => ImportMode::Merge, +"replace" => ImportMode::Replace, +other => ImportMode::Unknown(crate::primitives::sealed_enum_unknown::UnknownVariantValue(other.to_owned())) + } + } + } +impl ::std::str::FromStr for ImportMode { + type Err = ::std::convert::Infallible; + + fn from_str(s: &str) -> ::std::result::Result::Err> { + ::std::result::Result::Ok(ImportMode::from(s)) + } + } +impl ImportMode { + /// Returns the `&str` value of the enum member. + pub fn as_str(&self) -> &str { + match self { + ImportMode::Merge => "merge", + ImportMode::Replace => "replace", + ImportMode::Unknown(value) => value.as_str() +} + } + /// Returns all the `&str` representations of the enum members. + pub const fn values() -> &'static [&'static str] { + &["merge", "replace"] + } + } +impl ::std::convert::AsRef for ImportMode { + fn as_ref(&self) -> &str { + self.as_str() + } + } +impl ImportMode { + /// Parses the enum value while disallowing unknown variants. + /// + /// Unknown variants will result in an error. + pub fn try_parse(value: &str) -> ::std::result::Result { + match Self::from(value) { + #[allow(deprecated)] + Self::Unknown(_) => ::std::result::Result::Err(crate::error::UnknownVariantError::new(value)), + known => Ok(known), + } + } + } +impl ::std::fmt::Display for ImportMode { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + match self { + ImportMode::Merge => write!(f, "merge"), +ImportMode::Replace => write!(f, "replace"), +ImportMode::Unknown(value) => write!(f, "{}", value) + } + } + } + diff --git a/crates/superposition_sdk/src/types/_import_on_error.rs b/crates/superposition_sdk/src/types/_import_on_error.rs new file mode 100644 index 000000000..781bceba8 --- /dev/null +++ b/crates/superposition_sdk/src/types/_import_on_error.rs @@ -0,0 +1,107 @@ +// Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT. + +/// When writing a match expression against `ImportOnError`, it is important to ensure +/// your code is forward-compatible. That is, if a match arm handles a case for a +/// feature that is supported by the service but has not been represented as an enum +/// variant in a current version of SDK, your code should continue to work when you +/// upgrade SDK to a future version in which the enum does include a variant for that +/// feature. +/// +/// Here is an example of how you can make a match expression forward-compatible: +/// +/// ```text +/// # let importonerror = unimplemented!(); +/// match importonerror { +/// ImportOnError::Abort => { /* ... */ }, +/// ImportOnError::Continue => { /* ... */ }, +/// other @ _ if other.as_str() == "NewFeature" => { /* handles a case for `NewFeature` */ }, +/// _ => { /* ... */ }, +/// } +/// ``` +/// The above code demonstrates that when `importonerror` represents +/// `NewFeature`, the execution path will lead to the second last match arm, +/// even though the enum does not contain a variant `ImportOnError::NewFeature` +/// in the current version of SDK. The reason is that the variable `other`, +/// created by the `@` operator, is bound to +/// `ImportOnError::Unknown(UnknownVariantValue("NewFeature".to_owned()))` +/// and calling `as_str` on it yields `"NewFeature"`. +/// This match expression is forward-compatible when executed with a newer +/// version of SDK where the variant `ImportOnError::NewFeature` is defined. +/// Specifically, when `importonerror` represents `NewFeature`, +/// the execution path will hit the second last match arm as before by virtue of +/// calling `as_str` on `ImportOnError::NewFeature` also yielding `"NewFeature"`. +/// +/// Explicitly matching on the `Unknown` variant should +/// be avoided for two reasons: +/// - The inner data `UnknownVariantValue` is opaque, and no further information can be extracted. +/// - It might inadvertently shadow other intended match arms. +/// +/// How an import reacts when an individual entity fails to apply. +#[non_exhaustive] +#[derive(::std::clone::Clone, ::std::cmp::Eq, ::std::cmp::Ord, ::std::cmp::PartialEq, ::std::cmp::PartialOrd, ::std::fmt::Debug, ::std::hash::Hash)] +pub enum ImportOnError { + /// Roll the whole import back on the first error. + Abort, + /// Apply everything that is valid and report per-entity errors. + Continue, + /// `Unknown` contains new variants that have been added since this code was generated. + #[deprecated(note = "Don't directly match on `Unknown`. See the docs on this enum for the correct way to handle unknown variants.")] + Unknown(crate::primitives::sealed_enum_unknown::UnknownVariantValue) +} +impl ::std::convert::From<&str> for ImportOnError { + fn from(s: &str) -> Self { + match s { + "abort" => ImportOnError::Abort, +"continue" => ImportOnError::Continue, +other => ImportOnError::Unknown(crate::primitives::sealed_enum_unknown::UnknownVariantValue(other.to_owned())) + } + } + } +impl ::std::str::FromStr for ImportOnError { + type Err = ::std::convert::Infallible; + + fn from_str(s: &str) -> ::std::result::Result::Err> { + ::std::result::Result::Ok(ImportOnError::from(s)) + } + } +impl ImportOnError { + /// Returns the `&str` value of the enum member. + pub fn as_str(&self) -> &str { + match self { + ImportOnError::Abort => "abort", + ImportOnError::Continue => "continue", + ImportOnError::Unknown(value) => value.as_str() +} + } + /// Returns all the `&str` representations of the enum members. + pub const fn values() -> &'static [&'static str] { + &["abort", "continue"] + } + } +impl ::std::convert::AsRef for ImportOnError { + fn as_ref(&self) -> &str { + self.as_str() + } + } +impl ImportOnError { + /// Parses the enum value while disallowing unknown variants. + /// + /// Unknown variants will result in an error. + pub fn try_parse(value: &str) -> ::std::result::Result { + match Self::from(value) { + #[allow(deprecated)] + Self::Unknown(_) => ::std::result::Result::Err(crate::error::UnknownVariantError::new(value)), + known => Ok(known), + } + } + } +impl ::std::fmt::Display for ImportOnError { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + match self { + ImportOnError::Abort => write!(f, "abort"), +ImportOnError::Continue => write!(f, "continue"), +ImportOnError::Unknown(value) => write!(f, "{}", value) + } + } + } + diff --git a/crates/superposition_sdk/src/types/builders.rs b/crates/superposition_sdk/src/types/builders.rs index b7fc3a81b..2b5d54b92 100644 --- a/crates/superposition_sdk/src/types/builders.rs +++ b/crates/superposition_sdk/src/types/builders.rs @@ -65,3 +65,7 @@ pub use crate::types::_dimension_response::DimensionResponseBuilder; pub use crate::types::_default_config_response::DefaultConfigResponseBuilder; +pub use crate::types::_import_entity_report::ImportEntityReportBuilder; + +pub use crate::types::_import_error_item::ImportErrorItemBuilder; + diff --git a/docs/docs/api/Superposition.openapi.json b/docs/docs/api/Superposition.openapi.json index 68095e309..0ce9774ac 100644 --- a/docs/docs/api/Superposition.openapi.json +++ b/docs/docs/api/Superposition.openapi.json @@ -310,6 +310,115 @@ ] } }, + "/config/json/import": { + "post": { + "description": "Imports a full config from a JSON document, persisting dimensions, default-configs and contexts in a single transaction after validating the document.", + "operationId": "ImportConfigJson", + "requestBody": { + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ImportConfigJsonInputPayload" + } + } + }, + "required": true + }, + "parameters": [ + { + "name": "x-config-tags", + "in": "header", + "schema": { + "type": "string" + } + }, + { + "name": "x-import-dry-run", + "in": "header", + "description": "When true, validates and summarises the import without persisting anything. Defaults to false.", + "schema": { + "type": "boolean", + "description": "When true, validates and summarises the import without persisting anything. Defaults to false." + } + }, + { + "name": "x-import-mode", + "in": "header", + "description": "Whether to merge (default) or replace existing workspace config.", + "schema": { + "$ref": "#/components/schemas/ImportMode" + } + }, + { + "name": "x-import-on-error", + "in": "header", + "description": "Whether to abort (default) or continue on per-entity errors.", + "schema": { + "$ref": "#/components/schemas/ImportOnError" + } + }, + { + "name": "x-import-overwrite", + "in": "header", + "description": "When false, entities that already exist are skipped instead of updated. Defaults to true.", + "schema": { + "type": "boolean", + "description": "When false, entities that already exist are skipped instead of updated. Defaults to true." + } + }, + { + "name": "x-import-value-merge", + "in": "header", + "description": "When true, deep-merges object-valued default-configs with the existing value. Defaults to false.", + "schema": { + "type": "boolean", + "description": "When true, deep-merges object-valued default-configs with the existing value. Defaults to false." + } + }, + { + "name": "x-org-id", + "in": "header", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "x-workspace", + "in": "header", + "schema": { + "type": "string" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "ImportConfigJson 200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ImportConfigJsonResponseContent" + } + } + } + }, + "500": { + "description": "InternalServerError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InternalServerErrorResponseContent" + } + } + } + } + }, + "tags": [ + "Configuration Management" + ] + } + }, "/config/resolve": { "post": { "description": "Resolves and merges config values based on context conditions, applying overrides and merge strategies to produce the final configuration.", @@ -742,6 +851,115 @@ ] } }, + "/config/toml/import": { + "post": { + "description": "Imports a full config from a TOML document, persisting dimensions, default-configs and contexts in a single transaction after validating the document.", + "operationId": "ImportConfigToml", + "requestBody": { + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ImportConfigTomlInputPayload" + } + } + }, + "required": true + }, + "parameters": [ + { + "name": "x-config-tags", + "in": "header", + "schema": { + "type": "string" + } + }, + { + "name": "x-import-dry-run", + "in": "header", + "description": "When true, validates and summarises the import without persisting anything. Defaults to false.", + "schema": { + "type": "boolean", + "description": "When true, validates and summarises the import without persisting anything. Defaults to false." + } + }, + { + "name": "x-import-mode", + "in": "header", + "description": "Whether to merge (default) or replace existing workspace config.", + "schema": { + "$ref": "#/components/schemas/ImportMode" + } + }, + { + "name": "x-import-on-error", + "in": "header", + "description": "Whether to abort (default) or continue on per-entity errors.", + "schema": { + "$ref": "#/components/schemas/ImportOnError" + } + }, + { + "name": "x-import-overwrite", + "in": "header", + "description": "When false, entities that already exist are skipped instead of updated. Defaults to true.", + "schema": { + "type": "boolean", + "description": "When false, entities that already exist are skipped instead of updated. Defaults to true." + } + }, + { + "name": "x-import-value-merge", + "in": "header", + "description": "When true, deep-merges object-valued default-configs with the existing value. Defaults to false.", + "schema": { + "type": "boolean", + "description": "When true, deep-merges object-valued default-configs with the existing value. Defaults to false." + } + }, + { + "name": "x-org-id", + "in": "header", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "x-workspace", + "in": "header", + "schema": { + "type": "string" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "ImportConfigToml 200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ImportConfigTomlResponseContent" + } + } + } + }, + "500": { + "description": "InternalServerError 500 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InternalServerErrorResponseContent" + } + } + } + } + }, + "tags": [ + "Configuration Management" + ] + } + }, "/config/versions": { "get": { "description": "Retrieves a paginated list of config versions with their metadata, hash values, and creation timestamps for audit and rollback purposes.", @@ -10116,6 +10334,135 @@ "HEAD" ] }, + "ImportConfigJsonInputPayload": { + "type": "string" + }, + "ImportConfigJsonResponseContent": { + "type": "object", + "description": "Summary of what an import created, updated, skipped or deleted.", + "properties": { + "mode": { + "type": "string" + }, + "dry_run": { + "type": "boolean" + }, + "config_version": { + "type": "string" + }, + "dimensions": { + "$ref": "#/components/schemas/ImportEntityReport" + }, + "default_configs": { + "$ref": "#/components/schemas/ImportEntityReport" + }, + "contexts": { + "$ref": "#/components/schemas/ImportEntityReport" + } + }, + "required": [ + "contexts", + "default_configs", + "dimensions", + "dry_run", + "mode" + ] + }, + "ImportConfigTomlInputPayload": { + "type": "string" + }, + "ImportConfigTomlResponseContent": { + "type": "object", + "description": "Summary of what an import created, updated, skipped or deleted.", + "properties": { + "mode": { + "type": "string" + }, + "dry_run": { + "type": "boolean" + }, + "config_version": { + "type": "string" + }, + "dimensions": { + "$ref": "#/components/schemas/ImportEntityReport" + }, + "default_configs": { + "$ref": "#/components/schemas/ImportEntityReport" + }, + "contexts": { + "$ref": "#/components/schemas/ImportEntityReport" + } + }, + "required": [ + "contexts", + "default_configs", + "dimensions", + "dry_run", + "mode" + ] + }, + "ImportEntityReport": { + "type": "object", + "description": "Per-entity outcome counts for an import.", + "properties": { + "created": { + "type": "number" + }, + "updated": { + "type": "number" + }, + "skipped": { + "type": "number" + }, + "deleted": { + "type": "number" + }, + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ImportErrorItem" + } + } + }, + "required": [ + "created", + "deleted", + "skipped", + "updated" + ] + }, + "ImportErrorItem": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "error": { + "type": "string" + } + }, + "required": [ + "error", + "id" + ] + }, + "ImportMode": { + "type": "string", + "description": "How an import treats workspace entities that are not present in the imported file.", + "enum": [ + "merge", + "replace" + ] + }, + "ImportOnError": { + "type": "string", + "description": "How an import reacts when an individual entity fails to apply.", + "enum": [ + "abort", + "continue" + ] + }, "InternalServerErrorResponseContent": { "type": "object", "properties": { diff --git a/docs/docs/api/import-config-json.ParamsDetails.json b/docs/docs/api/import-config-json.ParamsDetails.json new file mode 100644 index 000000000..139c2e881 --- /dev/null +++ b/docs/docs/api/import-config-json.ParamsDetails.json @@ -0,0 +1 @@ +{"parameters":[{"name":"x-config-tags","in":"header","schema":{"type":"string"}},{"name":"x-import-dry-run","in":"header","description":"When true, validates and summarises the import without persisting anything. Defaults to false.","schema":{"type":"boolean","description":"When true, validates and summarises the import without persisting anything. Defaults to false."}},{"name":"x-import-mode","in":"header","description":"Whether to merge (default) or replace existing workspace config.","schema":{"type":"string","description":"How an import treats workspace entities that are not present in the imported file.","enum":["merge","replace"],"title":"ImportMode"}},{"name":"x-import-on-error","in":"header","description":"Whether to abort (default) or continue on per-entity errors.","schema":{"type":"string","description":"How an import reacts when an individual entity fails to apply.","enum":["abort","continue"],"title":"ImportOnError"}},{"name":"x-import-overwrite","in":"header","description":"When false, entities that already exist are skipped instead of updated. Defaults to true.","schema":{"type":"boolean","description":"When false, entities that already exist are skipped instead of updated. Defaults to true."}},{"name":"x-import-value-merge","in":"header","description":"When true, deep-merges object-valued default-configs with the existing value. Defaults to false.","schema":{"type":"boolean","description":"When true, deep-merges object-valued default-configs with the existing value. Defaults to false."}},{"name":"x-org-id","in":"header","schema":{"type":"string"},"required":true},{"name":"x-workspace","in":"header","schema":{"type":"string"},"required":true}]} \ No newline at end of file diff --git a/docs/docs/api/import-config-json.RequestSchema.json b/docs/docs/api/import-config-json.RequestSchema.json new file mode 100644 index 000000000..d6ff4e1a3 --- /dev/null +++ b/docs/docs/api/import-config-json.RequestSchema.json @@ -0,0 +1 @@ +{"title":"Body","body":{"content":{"text/plain":{"schema":{"type":"string","title":"ImportConfigJsonInputPayload"}}},"required":true}} \ No newline at end of file diff --git a/docs/docs/api/import-config-json.StatusCodes.json b/docs/docs/api/import-config-json.StatusCodes.json new file mode 100644 index 000000000..da87c505b --- /dev/null +++ b/docs/docs/api/import-config-json.StatusCodes.json @@ -0,0 +1 @@ +{"responses":{"200":{"description":"ImportConfigJson 200 response","content":{"application/json":{"schema":{"type":"object","description":"Summary of what an import created, updated, skipped or deleted.","properties":{"mode":{"type":"string"},"dry_run":{"type":"boolean"},"config_version":{"type":"string"},"dimensions":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"error":{"type":"string"}},"required":["error","id"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"},"default_configs":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"error":{"type":"string"}},"required":["error","id"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"},"contexts":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"error":{"type":"string"}},"required":["error","id"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"}},"required":["contexts","default_configs","dimensions","dry_run","mode"],"title":"ImportConfigJsonResponseContent"}}}},"500":{"description":"InternalServerError 500 response","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"title":"InternalServerErrorResponseContent"}}}}}} \ No newline at end of file diff --git a/docs/docs/api/import-config-json.api.mdx b/docs/docs/api/import-config-json.api.mdx new file mode 100644 index 000000000..aa4fb6402 --- /dev/null +++ b/docs/docs/api/import-config-json.api.mdx @@ -0,0 +1,69 @@ +--- +id: import-config-json +title: "ImportConfigJson" +description: "Imports a full config from a JSON document, persisting dimensions, default-configs and contexts in a single transaction after validating the document." +sidebar_label: "ImportConfigJson" +hide_title: true +hide_table_of_contents: true +api: eJztWEtv2zgQ/iuETi1gJUEXueTWxwJNgW6COIs9BEFAiyObiUSqJBVHCPzfd4akJdlSGm9SF4uil8TiYx7ffMMh5zERYDMjKye1Sk6S07LSxlnGWV4XBcu0yuWc5UaXOPRlevYXEzqrS1BuwiowVlon1ZwJiUMWRdgJE5DzunBp2IuilCA5Dh5QrlQox+KWApgzXFmekWbGcweG3fNCCu4lugW0qg6SSaJRG6elp6I186PX8MWi5ZPEwLcarPugRZOcPCZeo3L0kzQfVgWXir5stoCS+/GmApRlnUGFKMFJV8CI8FNV1e6cN4XmIlmtVkGXNICWOFMDDlTc8BLQBZucXD0mCj9Q0EPEIHV8blEBGZAsgAsw+PWUIaigJ0F6Y1JhmtTUaiBkM3r/LEAxMmmyhhIC/rYuS26kxU8CNghlS+kWunb9QHLVuAX+OGCfQhhxg2Y5LywcjNk807oArn62JaMYlVrA8wChVkOSSjBzYG8iW98ybZgBZEkGDB6iDUtt7mxFQyGQoxC0/NlU9Vkv0Ye1g84ARw86gchN6aRHgTvGDTCl0X8DFmcoTTp0QLBcFh5/UHWJDEu88Z703uLkepu9XwmLUZS0SsEYbXZHis/Igw2kKLukqoFh6mLMUu9Nw7xg+3KUEKSMUCLy0KgS8l6KmhcsKsi5LDwPeFUVTR8RbyR+ry0bQnKm/vR+j6NyD2ZppNuBQCqQcLIdwgKtF00gjw+ovZNVhcGTyjqcYjpndUWZIDY5TWny35NrL0aMYoMJXEO6ptyO548AqMIey/TsFrIoRwzqA+W+Z3ubdn7hDzyB9mPLJlbazFMpdj/kh0WkJ6w9Jl4h75pGbIUlGSytf3d0RP/Gyn1X6hiuYuttMZtiFaWMk5kvwYe3VHLHamlAdxCLqT/0GyLf0tO0zfiMzkUQkzUpJy1h8ZQRUAARFeVVhi4ARHTS5k/6MQywTN5QmRzSY+WdQTdv7qnGaDW+v73HPO/UeXfuYenKdElVolbIkRxtb10cWh997mnAM2yG4UUDIgyjcxGZ0bkI1ehcOJV7U9wY3hC1HJSjjm7aK8UoVqGKjF1felS8StpiI4ZHsj+QT9GK7WvVVQtS51oHQAfTUKIPyAXQ74CLz9ubmOC/o/pLRHX9nPgdzl8gnNsy17EdJu/G+dwd9pNQDwZqurJ2ESvax1jOVt6T49GKiCuM4sUUDBYK7zs7/kFFcauIgbV8PlbHVj1PhuaMOUM76I2J+AWv6/BYZl+5Qh30iCZ88Im60PR+rrT1BnG3wK/DALD34DAkBAUSshpvxI1/0doSb0bNAa/kwcK56gO3Mntf0+6ra7q6bM8D3j1Nu+C6kzYldAIAT8tsEaHx4Zvh8vKc+dWM43JK7xCC9f3I132a9wn5tGW7qPHLv6fHL/AxkyrXXmiM3bTGaCPSMu5pLx54F3t3nB79kR4dk4UUjJJ77sQbYOAvC6FkscGxYV2vwfH/7dlEdHv9Fzo8TUHmB+pdxUsZLr4NXkb6IWMWxFFc8fiIsYS/TbFa0fC3GgxxEn/ecyP5jLAmEq4vyURXIS1NINXDM+lp8J4y8w6akQ6Ofw3QLuL87kr23pf5rguDFtLLvXht82QXO2Mb5/VGvqRvsYuBvQ7Ka/iwpy7CTh70uh2vp/S+nvq7eLLZm3ihL28u4v3jLXtObfvM37+qfhNgQ1tYEMt/ekkyuhUbstZr32cZVK63anBvWfVvB+dn00tcPIut9PDaTgxfUscR/5Idq9W/J/R8gw== +sidebar_class_name: "post api-method" +info_path: docs/api/superposition +custom_edit_url: null +--- + +import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; +import ParamsDetails from "@theme/ParamsDetails"; +import RequestSchema from "@theme/RequestSchema"; +import StatusCodes from "@theme/StatusCodes"; +import OperationTabs from "@theme/OperationTabs"; +import TabItem from "@theme/TabItem"; +import Heading from "@theme/Heading"; +import Translate from "@docusaurus/Translate"; + + + + + + + + + + +Imports a full config from a JSON document, persisting dimensions, default-configs and contexts in a single transaction after validating the document. + + + Request + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/docs/api/import-config-toml.ParamsDetails.json b/docs/docs/api/import-config-toml.ParamsDetails.json new file mode 100644 index 000000000..139c2e881 --- /dev/null +++ b/docs/docs/api/import-config-toml.ParamsDetails.json @@ -0,0 +1 @@ +{"parameters":[{"name":"x-config-tags","in":"header","schema":{"type":"string"}},{"name":"x-import-dry-run","in":"header","description":"When true, validates and summarises the import without persisting anything. Defaults to false.","schema":{"type":"boolean","description":"When true, validates and summarises the import without persisting anything. Defaults to false."}},{"name":"x-import-mode","in":"header","description":"Whether to merge (default) or replace existing workspace config.","schema":{"type":"string","description":"How an import treats workspace entities that are not present in the imported file.","enum":["merge","replace"],"title":"ImportMode"}},{"name":"x-import-on-error","in":"header","description":"Whether to abort (default) or continue on per-entity errors.","schema":{"type":"string","description":"How an import reacts when an individual entity fails to apply.","enum":["abort","continue"],"title":"ImportOnError"}},{"name":"x-import-overwrite","in":"header","description":"When false, entities that already exist are skipped instead of updated. Defaults to true.","schema":{"type":"boolean","description":"When false, entities that already exist are skipped instead of updated. Defaults to true."}},{"name":"x-import-value-merge","in":"header","description":"When true, deep-merges object-valued default-configs with the existing value. Defaults to false.","schema":{"type":"boolean","description":"When true, deep-merges object-valued default-configs with the existing value. Defaults to false."}},{"name":"x-org-id","in":"header","schema":{"type":"string"},"required":true},{"name":"x-workspace","in":"header","schema":{"type":"string"},"required":true}]} \ No newline at end of file diff --git a/docs/docs/api/import-config-toml.RequestSchema.json b/docs/docs/api/import-config-toml.RequestSchema.json new file mode 100644 index 000000000..48efbdc46 --- /dev/null +++ b/docs/docs/api/import-config-toml.RequestSchema.json @@ -0,0 +1 @@ +{"title":"Body","body":{"content":{"text/plain":{"schema":{"type":"string","title":"ImportConfigTomlInputPayload"}}},"required":true}} \ No newline at end of file diff --git a/docs/docs/api/import-config-toml.StatusCodes.json b/docs/docs/api/import-config-toml.StatusCodes.json new file mode 100644 index 000000000..c9a6aa1e2 --- /dev/null +++ b/docs/docs/api/import-config-toml.StatusCodes.json @@ -0,0 +1 @@ +{"responses":{"200":{"description":"ImportConfigToml 200 response","content":{"application/json":{"schema":{"type":"object","description":"Summary of what an import created, updated, skipped or deleted.","properties":{"mode":{"type":"string"},"dry_run":{"type":"boolean"},"config_version":{"type":"string"},"dimensions":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"error":{"type":"string"}},"required":["error","id"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"},"default_configs":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"error":{"type":"string"}},"required":["error","id"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"},"contexts":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"error":{"type":"string"}},"required":["error","id"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"}},"required":["contexts","default_configs","dimensions","dry_run","mode"],"title":"ImportConfigTomlResponseContent"}}}},"500":{"description":"InternalServerError 500 response","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"title":"InternalServerErrorResponseContent"}}}}}} \ No newline at end of file diff --git a/docs/docs/api/import-config-toml.api.mdx b/docs/docs/api/import-config-toml.api.mdx new file mode 100644 index 000000000..96fb90d41 --- /dev/null +++ b/docs/docs/api/import-config-toml.api.mdx @@ -0,0 +1,69 @@ +--- +id: import-config-toml +title: "ImportConfigToml" +description: "Imports a full config from a TOML document, persisting dimensions, default-configs and contexts in a single transaction after validating the document." +sidebar_label: "ImportConfigToml" +hide_title: true +hide_table_of_contents: true +api: eJztWEtPIzkQ/itWn2akNEGz4sJtHist0iIQZLUHhJDTriQeuu0e201oofz3rbLdj6SbIQuT1Wo0F0jb5Xp89ZXL9lMiwGZGlk5qlZwmZ0WpjbOMs0WV5yzTaiGXbGF0gUOzi/M/mdBZVYByE1aCsdI6qZZMSByyqMJOmIAFr3KXhrWoSgnS4+AR9UqFeiwuyYE5w5XlGVlmfOHAsAeeS8G9RreC1tRRMkk0WuMkeiZaNz97CzNd5Chg4FsF1n3Sok5OnxJvUTn6SZanZc6loi+braDgfrwuAXVZZ9AganDS5TCi/EyVlbvkda65SDabTbAlDaAnzlSAAyU3vAAMwSanN0+Jwg9U9BgxSB1fWjRADiQr4AIMfj3nCBroaZDemVSYOjWVGijZzt7fK1CMXJo0UELA31ZFwY20+EnABqVsLd1KV66fSK5qt8IfR+xLSCMu0GzBcwtHYz7Ptc6Bq//ak1GMCi3gZYDQqiFNBZglsHeRre+ZNswAsiQDBo/Rh7U297akoZDIUQha/myb+kOvMYYmQGeAYwSdQuSmdNKjwB3jBpjSGL8BizNUJh06INhC5h5/UFWBDEu885703uPkdpe954TFKEpapWCMNvsjxecUwRZSVF1SVcCwdDFnqY+mZl6xfT1KCFJGKBF5aFQJ+SBFxXMWDSy4zD0PeFnmdR8R7yR+N54NIblQv/u4x1F5ALM20u1BIBVIONlNYY7eizqQxyfU3suyxORJZR1OMb1gVUmVILY5TWXy74vrIE6MYoMFXEHaUG7P/UcAlGGNZXr+FbKoRwz6A9W+Z3tbdl7wB+5Ah/FlGyttlqkU+2/ywybSU9ZuE2/Qd0sjtsSWDJbkPxwf07+xdt+1OoZSrFkWqyl2Uao4mfkWPP1q9XgvDegOcnHtN/2ayLf2NG0rPqN9EcSkIeWkJSzuMgJyIKKivtLQAYCITtb8Tj+GAbbJO2qTQ3psfDAY5t0D9Ritxte355iXg7rs9j1sXZkuqEtUCjmyQN/bEIfex5h7FnAPm2N60YEIw+hcRGZ0LkI1Ohd25d4UN4bXRC0HxWig2/5KMYpV6CJjx5ceFW+SttmI4ZbsN+Qz9GL3WHXTgtSF1gHQwTTU6BNyBfQ74OLr9i4W+K+s/hRZba4Tv9L5E6RzV2eT22Hxbu3P3WY/Cf1gYKZra1exo32O7WzjIzkZ7YgoYRTPr8Fgo/Cxs5Mf1BR3mhhYy5djfWzTi2TozlgwtILumIhfiLoKl2V2zhXaoEs04YNX1JWm+3OprXeIuxV+TQPAU4dQTUNBUCIhq/BEXPsbrS3wZFQf8VIerZwrP3Ers48Vrb65paPL7jzg2dO0AredtmtCJwDwvM4WERof3hlms0vmpRlHcSrvkILmfOT7Ps37gnzes33MePHv2fECPmdSLbRXGnN3XWG2EWkZ17QHDzyLfThJj39Lj0/IQ0pGwT134gkw8JeFVLL4wLHlXe+B4//7ZhPR7b2/0OZpcnI/UO8mHspIOEQZ6YeMWRFHUeLpCXMJf5l8s6HhbxUY4iT+fOBG8jlhTSRsDslEVyEtTSDVwzXpefCec/Me6pEXHH8boFXE+f2NHPxd5rshDJ6QXh/FWx9P9vEzPuO83cnXvFvs42DvBeUtfDjQK8JeEfReO95O6UNd9feJZPtt4pWxvLuK54/37CWz7TX/8Kb6jwBb1oJAbP/pjHR0Elu6GtmPWQal60kNzi2b/ung8uJ6hsLz+JQebtuJ4Wt6ccS/5Mdm8w8edXyV +sidebar_class_name: "post api-method" +info_path: docs/api/superposition +custom_edit_url: null +--- + +import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; +import ParamsDetails from "@theme/ParamsDetails"; +import RequestSchema from "@theme/RequestSchema"; +import StatusCodes from "@theme/StatusCodes"; +import OperationTabs from "@theme/OperationTabs"; +import TabItem from "@theme/TabItem"; +import Heading from "@theme/Heading"; +import Translate from "@docusaurus/Translate"; + + + + + + + + + + +Imports a full config from a TOML document, persisting dimensions, default-configs and contexts in a single transaction after validating the document. + + + Request + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/docs/api/sidebar.ts b/docs/docs/api/sidebar.ts index dc47c3a4d..35daa795b 100644 --- a/docs/docs/api/sidebar.ts +++ b/docs/docs/api/sidebar.ts @@ -39,6 +39,13 @@ const sidebar: SidebarsConfig = { customProps: {}, className: "api-method post", }, + { + type: "doc", + id: "api/import-config-json", + label: "ImportConfigJson", + customProps: {}, + className: "api-method post", + }, { type: "doc", id: "api/get-resolved-config", @@ -67,6 +74,13 @@ const sidebar: SidebarsConfig = { customProps: {}, className: "api-method post", }, + { + type: "doc", + id: "api/import-config-toml", + label: "ImportConfigToml", + customProps: {}, + className: "api-method post", + }, { type: "doc", id: "api/list-versions", From bb3db1d0e14f5751a57bc93c9860f25304c6ed5e Mon Sep 17 00:00:00 2001 From: Natarajan Kannan Date: Thu, 4 Jun 2026 22:03:21 +0530 Subject: [PATCH 07/15] fix(smithy): rename ImportErrorItem.error to message to fix Haskell SDK build The Haskell client-codegen plugin generates a record field that matches the smithy member name verbatim. For the ImportErrorItem.error field that meant a record field literally named error, which collides with Prelude.error and breaks the SDK build with an unresolvable "Ambiguous occurrence 'error'" at the auto-derived ToJSON site and the smart constructor: Io/Superposition/Model/ImportErrorItem.hs:34:31: error: Ambiguous occurrence 'error' It could refer to either 'Prelude.error', ... or the field 'error' of record 'ImportErrorItem', ... The plugin already escapes some Prelude clashes (it renames id -> id') but does not know about error, and we cannot hand-edit the generated module because make smithy-updates would overwrite it. Rename the smithy member to message and update the matching Rust handler struct so the JSON wire format stays in sync. The endpoint is brand-new and unreleased, so changing the JSON key from "error" to "message" has no external impact. Covers: - smithy/models/config.smithy: ImportErrorItem.error -> message. - crates/context_aware_config/.../config/import.rs: ImportError.error -> message, plus the matching struct-literal site. - Regenerated SDK output for Haskell, Java, JS, Python and Rust, plus the OpenAPI doc and the import-config-{json,toml} status-code/api.mdx files. The nix checks on x86_64-linux and aarch64-darwin failed on the previous push because of this; they should pass now. The "Check formatting" job also failed earlier but was an unrelated leptosfmt crates.io install timeout. --- .../Io/Superposition/Model/ImportErrorItem.hs | 24 ++++++------ .../superposition/model/ImportErrorItem.java | 38 +++++++++---------- .../src/commands/ImportConfigJsonCommand.ts | 6 +-- .../src/commands/ImportConfigTomlCommand.ts | 6 +-- clients/javascript/sdk/src/models/models_0.ts | 2 +- .../sdk/superposition_sdk/_private/schemas.py | 2 +- .../python/sdk/superposition_sdk/models.py | 6 +-- .../src/api/config/import.rs | 4 +- .../protocol_serde/shape_import_error_item.rs | 4 +- crates/superposition_sdk/src/serde_util.rs | 2 +- .../src/types/_import_error_item.rs | 26 ++++++------- docs/docs/api/Superposition.openapi.json | 6 +-- .../api/import-config-json.StatusCodes.json | 2 +- docs/docs/api/import-config-json.api.mdx | 2 +- .../api/import-config-toml.StatusCodes.json | 2 +- docs/docs/api/import-config-toml.api.mdx | 2 +- smithy/models/config.smithy | 2 +- 17 files changed, 68 insertions(+), 68 deletions(-) diff --git a/clients/haskell/sdk/Io/Superposition/Model/ImportErrorItem.hs b/clients/haskell/sdk/Io/Superposition/Model/ImportErrorItem.hs index 5e7891a76..f1ff5c841 100644 --- a/clients/haskell/sdk/Io/Superposition/Model/ImportErrorItem.hs +++ b/clients/haskell/sdk/Io/Superposition/Model/ImportErrorItem.hs @@ -1,11 +1,11 @@ module Io.Superposition.Model.ImportErrorItem ( setId', - setError, + setMessage, build, ImportErrorItemBuilder, ImportErrorItem, id', - error + message ) where import qualified Control.Applicative import qualified Control.Monad.State.Strict @@ -21,7 +21,7 @@ import qualified Io.Superposition.Utility data ImportErrorItem = ImportErrorItem { id' :: Data.Text.Text, - error :: Data.Text.Text + message :: Data.Text.Text } deriving ( GHC.Show.Show, Data.Eq.Eq, @@ -31,7 +31,7 @@ data ImportErrorItem = ImportErrorItem { instance Data.Aeson.ToJSON ImportErrorItem where toJSON a = Data.Aeson.object [ "id" Data.Aeson..= id' a, - "error" Data.Aeson..= error a + "message" Data.Aeson..= message a ] @@ -40,14 +40,14 @@ instance Io.Superposition.Utility.SerializeBody ImportErrorItem instance Data.Aeson.FromJSON ImportErrorItem where parseJSON = Data.Aeson.withObject "ImportErrorItem" $ \v -> ImportErrorItem Data.Functor.<$> (v Data.Aeson..: "id") - Control.Applicative.<*> (v Data.Aeson..: "error") + Control.Applicative.<*> (v Data.Aeson..: "message") data ImportErrorItemBuilderState = ImportErrorItemBuilderState { id'BuilderState :: Data.Maybe.Maybe Data.Text.Text, - errorBuilderState :: Data.Maybe.Maybe Data.Text.Text + messageBuilderState :: Data.Maybe.Maybe Data.Text.Text } deriving ( GHC.Generics.Generic ) @@ -55,7 +55,7 @@ data ImportErrorItemBuilderState = ImportErrorItemBuilderState { defaultBuilderState :: ImportErrorItemBuilderState defaultBuilderState = ImportErrorItemBuilderState { id'BuilderState = Data.Maybe.Nothing, - errorBuilderState = Data.Maybe.Nothing + messageBuilderState = Data.Maybe.Nothing } type ImportErrorItemBuilder = Control.Monad.State.Strict.State ImportErrorItemBuilderState @@ -64,18 +64,18 @@ setId' :: Data.Text.Text -> ImportErrorItemBuilder () setId' value = Control.Monad.State.Strict.modify (\s -> (s { id'BuilderState = Data.Maybe.Just value })) -setError :: Data.Text.Text -> ImportErrorItemBuilder () -setError value = - Control.Monad.State.Strict.modify (\s -> (s { errorBuilderState = Data.Maybe.Just value })) +setMessage :: Data.Text.Text -> ImportErrorItemBuilder () +setMessage value = + Control.Monad.State.Strict.modify (\s -> (s { messageBuilderState = Data.Maybe.Just value })) build :: ImportErrorItemBuilder () -> Data.Either.Either Data.Text.Text ImportErrorItem build builder = do let (_, st) = Control.Monad.State.Strict.runState builder defaultBuilderState id'' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportErrorItem.ImportErrorItem.id' is a required property.") Data.Either.Right (id'BuilderState st) - error' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportErrorItem.ImportErrorItem.error is a required property.") Data.Either.Right (errorBuilderState st) + message' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportErrorItem.ImportErrorItem.message is a required property.") Data.Either.Right (messageBuilderState st) Data.Either.Right (ImportErrorItem { id' = id'', - error = error' + message = message' }) diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportErrorItem.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportErrorItem.java index 5b09ec0d0..8b4407529 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportErrorItem.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportErrorItem.java @@ -22,27 +22,27 @@ public final class ImportErrorItem implements SerializableStruct { public static final Schema $SCHEMA = Schema.structureBuilder($ID) .putMember("id", PreludeSchemas.STRING, new RequiredTrait()) - .putMember("error", PreludeSchemas.STRING, + .putMember("message", PreludeSchemas.STRING, new RequiredTrait()) .build(); private static final Schema $SCHEMA_ID = $SCHEMA.member("id"); - private static final Schema $SCHEMA_ERROR = $SCHEMA.member("error"); + private static final Schema $SCHEMA_MESSAGE = $SCHEMA.member("message"); private final transient String id; - private final transient String error; + private final transient String message; private ImportErrorItem(Builder builder) { this.id = builder.id; - this.error = builder.error; + this.message = builder.message; } public String id() { return id; } - public String error() { - return error; + public String message() { + return message; } @Override @@ -60,12 +60,12 @@ public boolean equals(Object other) { } ImportErrorItem that = (ImportErrorItem) other; return Objects.equals(this.id, that.id) - && Objects.equals(this.error, that.error); + && Objects.equals(this.message, that.message); } @Override public int hashCode() { - return Objects.hash(id, error); + return Objects.hash(id, message); } @Override @@ -76,7 +76,7 @@ public Schema schema() { @Override public void serializeMembers(ShapeSerializer serializer) { serializer.writeString($SCHEMA_ID, id); - serializer.writeString($SCHEMA_ERROR, error); + serializer.writeString($SCHEMA_MESSAGE, message); } @Override @@ -84,7 +84,7 @@ public void serializeMembers(ShapeSerializer serializer) { public T getMemberValue(Schema member) { return switch (member.memberIndex()) { case 0 -> (T) SchemaUtils.validateSameMember($SCHEMA_ID, member, id); - case 1 -> (T) SchemaUtils.validateSameMember($SCHEMA_ERROR, member, error); + case 1 -> (T) SchemaUtils.validateSameMember($SCHEMA_MESSAGE, member, message); default -> throw new IllegalArgumentException("Attempted to get non-existent member: " + member.id()); }; } @@ -99,7 +99,7 @@ public T getMemberValue(Schema member) { public Builder toBuilder() { var builder = new Builder(); builder.id(this.id); - builder.error(this.error); + builder.message(this.message); return builder; } @@ -116,7 +116,7 @@ public static Builder builder() { public static final class Builder implements ShapeBuilder { private final PresenceTracker tracker = PresenceTracker.of($SCHEMA); private String id; - private String error; + private String message; private Builder() {} @@ -139,9 +139,9 @@ public Builder id(String id) { *

Required * @return this builder. */ - public Builder error(String error) { - this.error = Objects.requireNonNull(error, "error cannot be null"); - tracker.setMember($SCHEMA_ERROR); + public Builder message(String message) { + this.message = Objects.requireNonNull(message, "message cannot be null"); + tracker.setMember($SCHEMA_MESSAGE); return this; } @@ -156,7 +156,7 @@ public ImportErrorItem build() { public void setMemberValue(Schema member, Object value) { switch (member.memberIndex()) { case 0 -> id((String) SchemaUtils.validateSameMember($SCHEMA_ID, member, value)); - case 1 -> error((String) SchemaUtils.validateSameMember($SCHEMA_ERROR, member, value)); + case 1 -> message((String) SchemaUtils.validateSameMember($SCHEMA_MESSAGE, member, value)); default -> ShapeBuilder.super.setMemberValue(member, value); } } @@ -169,8 +169,8 @@ public ShapeBuilder errorCorrection() { if (!tracker.checkMember($SCHEMA_ID)) { id(""); } - if (!tracker.checkMember($SCHEMA_ERROR)) { - error(""); + if (!tracker.checkMember($SCHEMA_MESSAGE)) { + message(""); } return this; } @@ -194,7 +194,7 @@ private static final class $InnerDeserializer implements ShapeDeserializer.Struc public void accept(Builder builder, Schema member, ShapeDeserializer de) { switch (member.memberIndex()) { case 0 -> builder.id(de.readString(member)); - case 1 -> builder.error(de.readString(member)); + case 1 -> builder.message(de.readString(member)); default -> throw new IllegalArgumentException("Unexpected member: " + member.memberName()); } } diff --git a/clients/javascript/sdk/src/commands/ImportConfigJsonCommand.ts b/clients/javascript/sdk/src/commands/ImportConfigJsonCommand.ts index 1c2db7912..0e1183176 100644 --- a/clients/javascript/sdk/src/commands/ImportConfigJsonCommand.ts +++ b/clients/javascript/sdk/src/commands/ImportConfigJsonCommand.ts @@ -67,7 +67,7 @@ export interface ImportConfigJsonCommandOutput extends ImportConfigOutput, __Met * // errors: [ // ImportErrorList * // { // ImportErrorItem * // id: "STRING_VALUE", // required - * // error: "STRING_VALUE", // required + * // message: "STRING_VALUE", // required * // }, * // ], * // }, @@ -79,7 +79,7 @@ export interface ImportConfigJsonCommandOutput extends ImportConfigOutput, __Met * // errors: [ * // { * // id: "STRING_VALUE", // required - * // error: "STRING_VALUE", // required + * // message: "STRING_VALUE", // required * // }, * // ], * // }, @@ -91,7 +91,7 @@ export interface ImportConfigJsonCommandOutput extends ImportConfigOutput, __Met * // errors: [ * // { * // id: "STRING_VALUE", // required - * // error: "STRING_VALUE", // required + * // message: "STRING_VALUE", // required * // }, * // ], * // }, diff --git a/clients/javascript/sdk/src/commands/ImportConfigTomlCommand.ts b/clients/javascript/sdk/src/commands/ImportConfigTomlCommand.ts index ee7130db5..fe40e2dda 100644 --- a/clients/javascript/sdk/src/commands/ImportConfigTomlCommand.ts +++ b/clients/javascript/sdk/src/commands/ImportConfigTomlCommand.ts @@ -67,7 +67,7 @@ export interface ImportConfigTomlCommandOutput extends ImportConfigOutput, __Met * // errors: [ // ImportErrorList * // { // ImportErrorItem * // id: "STRING_VALUE", // required - * // error: "STRING_VALUE", // required + * // message: "STRING_VALUE", // required * // }, * // ], * // }, @@ -79,7 +79,7 @@ export interface ImportConfigTomlCommandOutput extends ImportConfigOutput, __Met * // errors: [ * // { * // id: "STRING_VALUE", // required - * // error: "STRING_VALUE", // required + * // message: "STRING_VALUE", // required * // }, * // ], * // }, @@ -91,7 +91,7 @@ export interface ImportConfigTomlCommandOutput extends ImportConfigOutput, __Met * // errors: [ * // { * // id: "STRING_VALUE", // required - * // error: "STRING_VALUE", // required + * // message: "STRING_VALUE", // required * // }, * // ], * // }, diff --git a/clients/javascript/sdk/src/models/models_0.ts b/clients/javascript/sdk/src/models/models_0.ts index ffdba0631..e55d07d98 100644 --- a/clients/javascript/sdk/src/models/models_0.ts +++ b/clients/javascript/sdk/src/models/models_0.ts @@ -2834,7 +2834,7 @@ export interface ImportConfigJsonInput { */ export interface ImportErrorItem { id: string | undefined; - error: string | undefined; + message: string | undefined; } /** diff --git a/clients/python/sdk/superposition_sdk/_private/schemas.py b/clients/python/sdk/superposition_sdk/_private/schemas.py index 38551be0d..aeed6680b 100644 --- a/clients/python/sdk/superposition_sdk/_private/schemas.py +++ b/clients/python/sdk/superposition_sdk/_private/schemas.py @@ -15473,7 +15473,7 @@ ], }, - "error": { + "message": { "target": STRING, "index": 1, "traits": [ diff --git a/clients/python/sdk/superposition_sdk/models.py b/clients/python/sdk/superposition_sdk/models.py index 54982aa34..8426292a7 100644 --- a/clients/python/sdk/superposition_sdk/models.py +++ b/clients/python/sdk/superposition_sdk/models.py @@ -15382,14 +15382,14 @@ class ImportErrorItem: id: str - error: str + message: str def serialize(self, serializer: ShapeSerializer): serializer.write_struct(_SCHEMA_IMPORT_ERROR_ITEM, self) def serialize_members(self, serializer: ShapeSerializer): serializer.write_string(_SCHEMA_IMPORT_ERROR_ITEM.members["id"], self.id) - serializer.write_string(_SCHEMA_IMPORT_ERROR_ITEM.members["error"], self.error) + serializer.write_string(_SCHEMA_IMPORT_ERROR_ITEM.members["message"], self.message) @classmethod def deserialize(cls, deserializer: ShapeDeserializer) -> Self: @@ -15405,7 +15405,7 @@ def _consumer(schema: Schema, de: ShapeDeserializer) -> None: kwargs["id"] = de.read_string(_SCHEMA_IMPORT_ERROR_ITEM.members["id"]) case 1: - kwargs["error"] = de.read_string(_SCHEMA_IMPORT_ERROR_ITEM.members["error"]) + kwargs["message"] = de.read_string(_SCHEMA_IMPORT_ERROR_ITEM.members["message"]) case _: logger.debug("Unexpected member schema: %s", schema) diff --git a/crates/context_aware_config/src/api/config/import.rs b/crates/context_aware_config/src/api/config/import.rs index a73844ec5..e13bf5124 100644 --- a/crates/context_aware_config/src/api/config/import.rs +++ b/crates/context_aware_config/src/api/config/import.rs @@ -135,7 +135,7 @@ impl ImportOptions { #[derive(Serialize)] pub struct ImportError { pub id: String, - pub error: String, + pub message: String, } #[derive(Default, Serialize)] @@ -260,7 +260,7 @@ fn apply_outcome( OnError::Continue => { report.errors.push(ImportError { id: id.to_string(), - error: e.message(), + message: e.message(), }); Ok(()) } diff --git a/crates/superposition_sdk/src/protocol_serde/shape_import_error_item.rs b/crates/superposition_sdk/src/protocol_serde/shape_import_error_item.rs index 7e83007d0..de2555f15 100644 --- a/crates/superposition_sdk/src/protocol_serde/shape_import_error_item.rs +++ b/crates/superposition_sdk/src/protocol_serde/shape_import_error_item.rs @@ -20,8 +20,8 @@ pub(crate) fn de_import_error_item<'a, I>(tokens: &mut ::std::iter::Peekable) ).transpose()? ); } - "error" => { - builder = builder.set_error( + "message" => { + builder = builder.set_message( ::aws_smithy_json::deserialize::token::expect_string_or_null(tokens.next())?.map(|s| s.to_unescaped().map(|u| u.into_owned() diff --git a/crates/superposition_sdk/src/serde_util.rs b/crates/superposition_sdk/src/serde_util.rs index 6f9d8818d..23c67fe75 100644 --- a/crates/superposition_sdk/src/serde_util.rs +++ b/crates/superposition_sdk/src/serde_util.rs @@ -1285,6 +1285,6 @@ if builder.value_after.is_none() { builder.value_after = Some(Default::default() pub(crate) fn import_error_item_correct_errors(mut builder: crate::types::builders::ImportErrorItemBuilder) -> crate::types::builders::ImportErrorItemBuilder { if builder.id.is_none() { builder.id = Some(Default::default()) } -if builder.error.is_none() { builder.error = Some(Default::default()) } +if builder.message.is_none() { builder.message = Some(Default::default()) } builder } diff --git a/crates/superposition_sdk/src/types/_import_error_item.rs b/crates/superposition_sdk/src/types/_import_error_item.rs index aaf663a3b..57b86c8c3 100644 --- a/crates/superposition_sdk/src/types/_import_error_item.rs +++ b/crates/superposition_sdk/src/types/_import_error_item.rs @@ -6,7 +6,7 @@ pub struct ImportErrorItem { #[allow(missing_docs)] // documentation missing in model pub id: ::std::string::String, #[allow(missing_docs)] // documentation missing in model - pub error: ::std::string::String, + pub message: ::std::string::String, } impl ImportErrorItem { #[allow(missing_docs)] // documentation missing in model @@ -14,8 +14,8 @@ impl ImportErrorItem { use std::ops::Deref; self.id.deref() } #[allow(missing_docs)] // documentation missing in model - pub fn error(&self) -> &str { - use std::ops::Deref; self.error.deref() + pub fn message(&self) -> &str { + use std::ops::Deref; self.message.deref() } } impl ImportErrorItem { @@ -30,7 +30,7 @@ impl ImportErrorItem { #[non_exhaustive] pub struct ImportErrorItemBuilder { pub(crate) id: ::std::option::Option<::std::string::String>, - pub(crate) error: ::std::option::Option<::std::string::String>, + pub(crate) message: ::std::option::Option<::std::string::String>, } impl ImportErrorItemBuilder { #[allow(missing_docs)] // documentation missing in model @@ -49,22 +49,22 @@ impl ImportErrorItemBuilder { } #[allow(missing_docs)] // documentation missing in model /// This field is required. - pub fn error(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { - self.error = ::std::option::Option::Some(input.into()); + pub fn message(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { + self.message = ::std::option::Option::Some(input.into()); self } #[allow(missing_docs)] // documentation missing in model - pub fn set_error(mut self, input: ::std::option::Option<::std::string::String>) -> Self { - self.error = input; self + pub fn set_message(mut self, input: ::std::option::Option<::std::string::String>) -> Self { + self.message = input; self } #[allow(missing_docs)] // documentation missing in model - pub fn get_error(&self) -> &::std::option::Option<::std::string::String> { - &self.error + pub fn get_message(&self) -> &::std::option::Option<::std::string::String> { + &self.message } /// Consumes the builder and constructs a [`ImportErrorItem`](crate::types::ImportErrorItem). /// This method will fail if any of the following fields are not set: /// - [`id`](crate::types::builders::ImportErrorItemBuilder::id) - /// - [`error`](crate::types::builders::ImportErrorItemBuilder::error) + /// - [`message`](crate::types::builders::ImportErrorItemBuilder::message) pub fn build(self) -> ::std::result::Result { ::std::result::Result::Ok( crate::types::ImportErrorItem { @@ -73,9 +73,9 @@ impl ImportErrorItemBuilder { ::aws_smithy_types::error::operation::BuildError::missing_field("id", "id was not specified but it is required when building ImportErrorItem") )? , - error: self.error + message: self.message .ok_or_else(|| - ::aws_smithy_types::error::operation::BuildError::missing_field("error", "error was not specified but it is required when building ImportErrorItem") + ::aws_smithy_types::error::operation::BuildError::missing_field("message", "message was not specified but it is required when building ImportErrorItem") )? , } diff --git a/docs/docs/api/Superposition.openapi.json b/docs/docs/api/Superposition.openapi.json index 0ce9774ac..5b8a62622 100644 --- a/docs/docs/api/Superposition.openapi.json +++ b/docs/docs/api/Superposition.openapi.json @@ -10438,13 +10438,13 @@ "id": { "type": "string" }, - "error": { + "message": { "type": "string" } }, "required": [ - "error", - "id" + "id", + "message" ] }, "ImportMode": { diff --git a/docs/docs/api/import-config-json.StatusCodes.json b/docs/docs/api/import-config-json.StatusCodes.json index da87c505b..de2664cca 100644 --- a/docs/docs/api/import-config-json.StatusCodes.json +++ b/docs/docs/api/import-config-json.StatusCodes.json @@ -1 +1 @@ -{"responses":{"200":{"description":"ImportConfigJson 200 response","content":{"application/json":{"schema":{"type":"object","description":"Summary of what an import created, updated, skipped or deleted.","properties":{"mode":{"type":"string"},"dry_run":{"type":"boolean"},"config_version":{"type":"string"},"dimensions":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"error":{"type":"string"}},"required":["error","id"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"},"default_configs":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"error":{"type":"string"}},"required":["error","id"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"},"contexts":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"error":{"type":"string"}},"required":["error","id"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"}},"required":["contexts","default_configs","dimensions","dry_run","mode"],"title":"ImportConfigJsonResponseContent"}}}},"500":{"description":"InternalServerError 500 response","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"title":"InternalServerErrorResponseContent"}}}}}} \ No newline at end of file +{"responses":{"200":{"description":"ImportConfigJson 200 response","content":{"application/json":{"schema":{"type":"object","description":"Summary of what an import created, updated, skipped or deleted.","properties":{"mode":{"type":"string"},"dry_run":{"type":"boolean"},"config_version":{"type":"string"},"dimensions":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"message":{"type":"string"}},"required":["id","message"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"},"default_configs":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"message":{"type":"string"}},"required":["id","message"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"},"contexts":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"message":{"type":"string"}},"required":["id","message"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"}},"required":["contexts","default_configs","dimensions","dry_run","mode"],"title":"ImportConfigJsonResponseContent"}}}},"500":{"description":"InternalServerError 500 response","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"title":"InternalServerErrorResponseContent"}}}}}} \ No newline at end of file diff --git a/docs/docs/api/import-config-json.api.mdx b/docs/docs/api/import-config-json.api.mdx index aa4fb6402..7fbd7cf01 100644 --- a/docs/docs/api/import-config-json.api.mdx +++ b/docs/docs/api/import-config-json.api.mdx @@ -5,7 +5,7 @@ description: "Imports a full config from a JSON document, persisting dimensions, sidebar_label: "ImportConfigJson" hide_title: true hide_table_of_contents: true -api: eJztWEtv2zgQ/iuETi1gJUEXueTWxwJNgW6COIs9BEFAiyObiUSqJBVHCPzfd4akJdlSGm9SF4uil8TiYx7ffMMh5zERYDMjKye1Sk6S07LSxlnGWV4XBcu0yuWc5UaXOPRlevYXEzqrS1BuwiowVlon1ZwJiUMWRdgJE5DzunBp2IuilCA5Dh5QrlQox+KWApgzXFmekWbGcweG3fNCCu4lugW0qg6SSaJRG6elp6I186PX8MWi5ZPEwLcarPugRZOcPCZeo3L0kzQfVgWXir5stoCS+/GmApRlnUGFKMFJV8CI8FNV1e6cN4XmIlmtVkGXNICWOFMDDlTc8BLQBZucXD0mCj9Q0EPEIHV8blEBGZAsgAsw+PWUIaigJ0F6Y1JhmtTUaiBkM3r/LEAxMmmyhhIC/rYuS26kxU8CNghlS+kWunb9QHLVuAX+OGCfQhhxg2Y5LywcjNk807oArn62JaMYlVrA8wChVkOSSjBzYG8iW98ybZgBZEkGDB6iDUtt7mxFQyGQoxC0/NlU9Vkv0Ye1g84ARw86gchN6aRHgTvGDTCl0X8DFmcoTTp0QLBcFh5/UHWJDEu88Z703uLkepu9XwmLUZS0SsEYbXZHis/Igw2kKLukqoFh6mLMUu9Nw7xg+3KUEKSMUCLy0KgS8l6KmhcsKsi5LDwPeFUVTR8RbyR+ry0bQnKm/vR+j6NyD2ZppNuBQCqQcLIdwgKtF00gjw+ovZNVhcGTyjqcYjpndUWZIDY5TWny35NrL0aMYoMJXEO6ptyO548AqMIey/TsFrIoRwzqA+W+Z3ubdn7hDzyB9mPLJlbazFMpdj/kh0WkJ6w9Jl4h75pGbIUlGSytf3d0RP/Gyn1X6hiuYuttMZtiFaWMk5kvwYe3VHLHamlAdxCLqT/0GyLf0tO0zfiMzkUQkzUpJy1h8ZQRUAARFeVVhi4ARHTS5k/6MQywTN5QmRzSY+WdQTdv7qnGaDW+v73HPO/UeXfuYenKdElVolbIkRxtb10cWh997mnAM2yG4UUDIgyjcxGZ0bkI1ehcOJV7U9wY3hC1HJSjjm7aK8UoVqGKjF1felS8StpiI4ZHsj+QT9GK7WvVVQtS51oHQAfTUKIPyAXQ74CLz9ubmOC/o/pLRHX9nPgdzl8gnNsy17EdJu/G+dwd9pNQDwZqurJ2ESvax1jOVt6T49GKiCuM4sUUDBYK7zs7/kFFcauIgbV8PlbHVj1PhuaMOUM76I2J+AWv6/BYZl+5Qh30iCZ88Im60PR+rrT1BnG3wK/DALD34DAkBAUSshpvxI1/0doSb0bNAa/kwcK56gO3Mntf0+6ra7q6bM8D3j1Nu+C6kzYldAIAT8tsEaHx4Zvh8vKc+dWM43JK7xCC9f3I132a9wn5tGW7qPHLv6fHL/AxkyrXXmiM3bTGaCPSMu5pLx54F3t3nB79kR4dk4UUjJJ77sQbYOAvC6FkscGxYV2vwfH/7dlEdHv9Fzo8TUHmB+pdxUsZLr4NXkb6IWMWxFFc8fiIsYS/TbFa0fC3GgxxEn/ecyP5jLAmEq4vyURXIS1NINXDM+lp8J4y8w6akQ6Ofw3QLuL87kr23pf5rguDFtLLvXht82QXO2Mb5/VGvqRvsYuBvQ7Ka/iwpy7CTh70uh2vp/S+nvq7eLLZm3ihL28u4v3jLXtObfvM37+qfhNgQ1tYEMt/ekkyuhUbstZr32cZVK63anBvWfVvB+dn00tcPIut9PDaTgxfUscR/5Idq9W/J/R8gw== +api: eJztWEtPIzkQ/itWn2akBNCsuHCbx0rLSLMgwmoPCCGnXUkM3XaP7Sa0UP77VtnuV7qBLExWq9FcIG2X6/HVVy7bj4kAmxpZOKlVcpKc5oU2zjLOFmWWsVSrhVyyhdE5Dn2dnf3JhE7LHJSbsAKMldZJtWRC4pBFFXbCBCx4mblpWIuqlCA9Dh5Qr1Sox+KSDJgzXFmekmXGFw4Mu+eZFNxrdCtoTB0kk0SjNU6ip6Jx87O38NWi55PEwPcSrPukRZWcPCbeonL0kywfFhmXir5suoKc+/GqANRlnUGDqMFJl8GI8lNVlO6cV5nmItlsNsGWNICeOFMCDhTc8BwwBJucXD0mCj9Q0UPEYOr40qIBciBZARdg8OspR9BAR4P0zkyFqaamVAMl/ez9vQLFyKVJDSUE/G2Z59xIi58EbFDK1tKtdOm6ieSqciv8ccC+hDTiAs0WPLNwMObzXOsMuPqvPRnFKNcCXgYIrRrSlINZAnsX2fqeacMMIEtSYPAQfVhrc2cLGgqJHIWg4U/f1B96jTHUAToDHCNoFSI3pZMeBe4YN8CUxvgNWJyhMmnRAcEWMvP4gypzZFjinfek9x4n19vs/UZYjKKk1RSM0WZ3pPicIughRdUlVQkMSxdzNvXRVMwrtq9HCUFKCSUiD40qIe+lKHnGooEFl5nnAS+KrOoi4p3E79qzISRn6ncf9zgq92DWRrodCKQCCSfbKczQe1EF8viE2jtZFJg8qazDKaYXrCyoEkSf01Qm/7649uLEKDZYwCVMa8rtuP8IgCKssUzPbyGNesSgP1Dte7Y3ZecFf+AOtB9f+lhps5xKsfsmP2wiHWXNNvEGfdc0YgtsyWBJ/sPREf0ba/dtq2MoxeplsZpiF6WKk6lvwYe31HLHemlAd5CLmd/0KyLf2tO0qfiU9kUQk5qUk4awuMsIyICIivoKQwcAIjpZ8zv9GAbYJm+oTQ7psfHBYJg399RjtBpf35xjXg7qvN33sHWlOqcuUSrkyAJ9b0Iceh9j7ljAPWyO6UUHIgyjcxGZ0bkI1ehc2JU7U9wYXhG1HOSjgfb9lWIUqxys5cuxPPTIeJX4sqilB9uy35RP0ZPto9VVA1QbXgtCC9VQo0/KBdDvgI2v3ZtY5L8y+9Nktr5W/ErpT5LSbZ11fodF3Nur241/EnrDwEzb4i5id/scW9vGR3I82h1RwiiezcBg0/Cxs+Mf1CC3Gtpz6WkiGbozFgytoPsm4heiLsPFmX3jCm3QhZrwwevqStNdutDWO8TdCr8OA8A+gsNQFJRISEs8HVf+dmtzPCVVB7yQByvnik/cyvRjSauvrukYsz0PeA41jcB1q21G6AQAntbZIELjw/vD5eU589KMoziVeEhBfVbyZwCa90X5tGe7mPHiz9nxAj5nUi20VxpzNysx24i0jGuaQwieyz4cT49+mx4dk4eUjJx77sTTYOAvC6lk8bGj513nseP/+34T0e28xdAGajJyP1DvKh7QUPg2RBnph4xZEUdR4vERcwl/mWyzoeHvJRjiJP6850byOWFNJKwPzERXIS1NINXDlelp8J5y8w6qkdccfzOgVcT53Y3s/Y3m2RAGz0mvj+KtDym7+BmfdN7u5GveMHZxsPOa8hY+7OlFYacIOi8fb6f0vq79u0TSf6d4ZSzvLuL54z17yWxz5d+/qe6DQM9aEIjtf3pJOlqJnq5a9mOaQuE6UoNzy6Z7Ojg/m12i8Dw+q4ebd2L4ml4f8S/5sdn8A3gcgOU= sidebar_class_name: "post api-method" info_path: docs/api/superposition custom_edit_url: null diff --git a/docs/docs/api/import-config-toml.StatusCodes.json b/docs/docs/api/import-config-toml.StatusCodes.json index c9a6aa1e2..d332a7ea8 100644 --- a/docs/docs/api/import-config-toml.StatusCodes.json +++ b/docs/docs/api/import-config-toml.StatusCodes.json @@ -1 +1 @@ -{"responses":{"200":{"description":"ImportConfigToml 200 response","content":{"application/json":{"schema":{"type":"object","description":"Summary of what an import created, updated, skipped or deleted.","properties":{"mode":{"type":"string"},"dry_run":{"type":"boolean"},"config_version":{"type":"string"},"dimensions":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"error":{"type":"string"}},"required":["error","id"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"},"default_configs":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"error":{"type":"string"}},"required":["error","id"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"},"contexts":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"error":{"type":"string"}},"required":["error","id"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"}},"required":["contexts","default_configs","dimensions","dry_run","mode"],"title":"ImportConfigTomlResponseContent"}}}},"500":{"description":"InternalServerError 500 response","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"title":"InternalServerErrorResponseContent"}}}}}} \ No newline at end of file +{"responses":{"200":{"description":"ImportConfigToml 200 response","content":{"application/json":{"schema":{"type":"object","description":"Summary of what an import created, updated, skipped or deleted.","properties":{"mode":{"type":"string"},"dry_run":{"type":"boolean"},"config_version":{"type":"string"},"dimensions":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"message":{"type":"string"}},"required":["id","message"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"},"default_configs":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"message":{"type":"string"}},"required":["id","message"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"},"contexts":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"message":{"type":"string"}},"required":["id","message"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"}},"required":["contexts","default_configs","dimensions","dry_run","mode"],"title":"ImportConfigTomlResponseContent"}}}},"500":{"description":"InternalServerError 500 response","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"title":"InternalServerErrorResponseContent"}}}}}} \ No newline at end of file diff --git a/docs/docs/api/import-config-toml.api.mdx b/docs/docs/api/import-config-toml.api.mdx index 96fb90d41..00f2c8cce 100644 --- a/docs/docs/api/import-config-toml.api.mdx +++ b/docs/docs/api/import-config-toml.api.mdx @@ -5,7 +5,7 @@ description: "Imports a full config from a TOML document, persisting dimensions, sidebar_label: "ImportConfigToml" hide_title: true hide_table_of_contents: true -api: eJztWEtPIzkQ/itWn2akNEGz4sJtHist0iIQZLUHhJDTriQeuu0e201oofz3rbLdj6SbIQuT1Wo0F0jb5Xp89ZXL9lMiwGZGlk5qlZwmZ0WpjbOMs0WV5yzTaiGXbGF0gUOzi/M/mdBZVYByE1aCsdI6qZZMSByyqMJOmIAFr3KXhrWoSgnS4+AR9UqFeiwuyYE5w5XlGVlmfOHAsAeeS8G9RreC1tRRMkk0WuMkeiZaNz97CzNd5Chg4FsF1n3Sok5OnxJvUTn6SZanZc6loi+braDgfrwuAXVZZ9AganDS5TCi/EyVlbvkda65SDabTbAlDaAnzlSAAyU3vAAMwSanN0+Jwg9U9BgxSB1fWjRADiQr4AIMfj3nCBroaZDemVSYOjWVGijZzt7fK1CMXJo0UELA31ZFwY20+EnABqVsLd1KV66fSK5qt8IfR+xLSCMu0GzBcwtHYz7Ptc6Bq//ak1GMCi3gZYDQqiFNBZglsHeRre+ZNswAsiQDBo/Rh7U297akoZDIUQha/myb+kOvMYYmQGeAYwSdQuSmdNKjwB3jBpjSGL8BizNUJh06INhC5h5/UFWBDEu885703uPkdpe954TFKEpapWCMNvsjxecUwRZSVF1SVcCwdDFnqY+mZl6xfT1KCFJGKBF5aFQJ+SBFxXMWDSy4zD0PeFnmdR8R7yR+N54NIblQv/u4x1F5ALM20u1BIBVIONlNYY7eizqQxyfU3suyxORJZR1OMb1gVUmVILY5TWXy74vrIE6MYoMFXEHaUG7P/UcAlGGNZXr+FbKoRwz6A9W+Z3tbdl7wB+5Ah/FlGyttlqkU+2/ywybSU9ZuE2/Qd0sjtsSWDJbkPxwf07+xdt+1OoZSrFkWqyl2Uao4mfkWPP1q9XgvDegOcnHtN/2ayLf2NG0rPqN9EcSkIeWkJSzuMgJyIKKivtLQAYCITtb8Tj+GAbbJO2qTQ3psfDAY5t0D9Ritxte355iXg7rs9j1sXZkuqEtUCjmyQN/bEIfex5h7FnAPm2N60YEIw+hcRGZ0LkI1Ohd25d4UN4bXRC0HxWig2/5KMYpV6CJjx5ceFW+SttmI4ZbsN+Qz9GL3WHXTgtSF1gHQwTTU6BNyBfQ74OLr9i4W+K+s/hRZba4Tv9L5E6RzV2eT22Hxbu3P3WY/Cf1gYKZra1exo32O7WzjIzkZ7YgoYRTPr8Fgo/Cxs5Mf1BR3mhhYy5djfWzTi2TozlgwtILumIhfiLoKl2V2zhXaoEs04YNX1JWm+3OprXeIuxV+TQPAU4dQTUNBUCIhq/BEXPsbrS3wZFQf8VIerZwrP3Ers48Vrb65paPL7jzg2dO0AredtmtCJwDwvM4WERof3hlms0vmpRlHcSrvkILmfOT7Ps37gnzes33MePHv2fECPmdSLbRXGnN3XWG2EWkZ17QHDzyLfThJj39Lj0/IQ0pGwT134gkw8JeFVLL4wLHlXe+B4//7ZhPR7b2/0OZpcnI/UO8mHspIOEQZ6YeMWRFHUeLpCXMJf5l8s6HhbxUY4iT+fOBG8jlhTSRsDslEVyEtTSDVwzXpefCec/Me6pEXHH8boFXE+f2NHPxd5rshDJ6QXh/FWx9P9vEzPuO83cnXvFvs42DvBeUtfDjQK8JeEfReO95O6UNd9feJZPtt4pWxvLuK54/37CWz7TX/8Kb6jwBb1oJAbP/pjHR0Elu6GtmPWQal60kNzi2b/ung8uJ6hsLz+JQebtuJ4Wt6ccS/5Mdm8w8edXyV +api: eJztWN1P4zgQ/1esPO1KLaA98cLbfpy0SIdA0NM9IITceNJ6Seys7VAi1P/9ZmwnTZoAPdiuTqt9gcYez8dvfuOx/ZgIsKmRpZNaJSfJaVFq4yzjLKvynKVaZXLBMqMLHJqdn/3FhE6rApSbsBKMldZJtWBC4pBFFXbCBGS8yt00rEVVSpAeBw+oVyrUY3FJDswZrixPyTLjmQPD7nkuBfca3RJaUwfJJNFojZPoqWjd/OwtzHSRo4CB7xVY90mLOjl5TLxF5egnWT4scy4Vfdl0CQX343UJqMs6gwZRg5MuhxHlp6qs3AWvc81Fsl6vgy1pAD1xpgIcKLnhBWAINjm5fkwUfqCih4jB1PGFRQPkQLIELsDg11OOoIGOBumdmQpTT02lBkr62ftnCYqRS5MGSgj426oouJEWPwnYoJStpFvqynUTyVXtlvjjgH0JacQFmmU8t3Aw5vNc6xy4+tmejGJUaAEvA4RWDWkqwCyAvYtsfc+0YQaQJSkweIg+rLS5syUNhUSOQtDyp2/qq15hDE2AzgDHCDYKkZvSSY8Cd4wbYEpj/AYszlCZbNABwTKZe/xBVQUyLPHOe9J7j5ObbfaeERajKGk1BWO02R0pPqcIekhRdUlVAcPSxZxNfTQ184rt61FCkFJCichDo0rIeykqnrNoIOMy9zzgZZnXXUS8k/jdeDaE5Fz96eMeR+UezMpItwOBVCDhZDuFOXov6kAen1B7J8sSkyeVdTjFdMaqkipB9DlNZfLfi2svToxigwVcwbSh3I77jwAowxrL9PwbpFGPGPQHqn3P9rbsvOAP3IH240sfK20WUyl23+SHTaSjrN0m3qDvhkZsiS0ZLMl/ODqif2PtftPqGEqxZlmspthFqeJk6lvw4Terx3tpQHeQiyu/6ddEvpWnaVvxKe2LICYNKSctYXGXEZADERX1lYYOAER0suZ3+jEMsE3eUpsc0mPtg8Ewb++px2g1vr49x7wc1MVm38PWleqCukSlkCMZ+t6GOPQ+xtyxgHvYHNOLDkQYRuciMqNzEarRubArd6a4MbwmajkoRgPt+yvFKFYFWMsXY3nokfE68WXRSA+2Zb8pn6In20er6xaoTXgbEDZQDTX6pFwC/Q7Y+Nq9jUX+O7O/TGaba8XvlP4iKd3W2eR3WMS9vXqz8U9CbxiY2bS4y9jdPsfWtvaRHI92R5QwiudXYLBp+NjZ8Q9qkFsN7bn0tJEM3RkLhlbQfRPxC1FX4eLMzrhCG3ShJnzwurrUdJcutfUOcbfEr8MA8KFDqA5DUVAiIa3wdFz7260t8JRUH/BSHiydKz9xK9OPFa2+vqFjzPY84DnUtAI3G21XhE4A4GmdLSI0Prw/zGYXzEszjuJU4iEFzVnJnwFo3hfl057tYsaLP2fHC/icSZVprzTm7qrCbCPSMq5pDyF4LvtwPD36Y3p0TB5SMgruuRNPg4G/LKSSxceOnnedx47/7/tNRLfzFkMbqMnJ/UC963hAI+EQZaQfMmZJHEWJx0fMJfxt8vWahr9XYIiT+POeG8nnhDWRsDkwE12FtDSBVA9XpqfBe8rNO6hHXnP8zYBWEed3N7L3N5pnQxg8J70+irc+pOziZ3zSebuTr3nD2MXBzmvKW/iwpxeFnSLovHy8ndL7uvbvEkn/neKVsby7jOeP9+wls+2Vf/+mug8CPWtBILb/6Yx0bCR6uhrZj2kKpetIDc4t6+7p4OL8aobC8/isHm7eieEren3Ev+THev0vbv2A9w== sidebar_class_name: "post api-method" info_path: docs/api/superposition custom_edit_url: null diff --git a/smithy/models/config.smithy b/smithy/models/config.smithy index 27099f0c8..35d41d2d9 100644 --- a/smithy/models/config.smithy +++ b/smithy/models/config.smithy @@ -227,7 +227,7 @@ structure ImportErrorItem { id: String @required - error: String + message: String } @documentation("Summary of what an import created, updated, skipped or deleted.") From 0649c283e46f4d6392cffbc3ab38dc4b593a5803 Mon Sep 17 00:00:00 2001 From: Natarajan Kannan Date: Thu, 4 Jun 2026 22:10:01 +0530 Subject: [PATCH 08/15] fix(lint): silence clippy::too_many_arguments on import_{toml,json}_handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both import handlers take 8 actix-web extractors (req, body, user, headers, db_conn, workspace_context, state plus the per-format type parameter via the handle_import generic), one over clippy's default limit of 7. The existing resolve_handler in the same file uses the same #[allow(clippy::too_many_arguments)] escape for the same reason — extractors are how actix wires in everything the handler needs, and bundling them into a struct just to silence the lint would work against the framework. Apply the same allow to both new import handlers so cargo clippy -- -Dwarnings (used by the Check formatting job) passes. --- crates/context_aware_config/src/api/config/handlers.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/context_aware_config/src/api/config/handlers.rs b/crates/context_aware_config/src/api/config/handlers.rs index ae4f951b4..0101b62aa 100644 --- a/crates/context_aware_config/src/api/config/handlers.rs +++ b/crates/context_aware_config/src/api/config/handlers.rs @@ -665,6 +665,7 @@ async fn get_json_handler( /// Imports a full config supplied as a TOML document in the request body. /// See [`crate::api::config::import`] for the supported `x-import-*` options. +#[allow(clippy::too_many_arguments)] #[authorized] #[post("/toml/import")] async fn import_toml_handler( @@ -691,6 +692,7 @@ async fn import_toml_handler( /// Imports a full config supplied as a JSON document in the request body. /// See [`crate::api::config::import`] for the supported `x-import-*` options. +#[allow(clippy::too_many_arguments)] #[authorized] #[post("/json/import")] async fn import_json_handler( From 7553883875e9db14ca477d8aedc9b55d6f9feb3a Mon Sep 17 00:00:00 2001 From: Natarajan Kannan Date: Thu, 4 Jun 2026 23:00:57 +0530 Subject: [PATCH 09/15] fix(smithy): regenerate python.patch so all auth-scheme blocks get the comma fix The Python client-codegen plugin emits an effective_auth_schemes list whose two ShapeID entries are not separated by a comma, e.g.: effective_auth_schemes = [ ShapeID("smithy.api#httpBasicAuth") ShapeID("smithy.api#httpBearerAuth") ] which is a SyntaxError ("invalid syntax. Perhaps you forgot a comma?") at import time. smithy/patches/python.patch has historically fixed this for every operation that supports both basic and bearer auth. Adding the new ImportConfigJson / ImportConfigToml operations pushed every operation below them down by ~600 lines, and crucially added two more broken blocks to the file. The patch's hunks for these new operations didn't exist, and because every hunk's context is identical (the only unique line is the APIOperation name in the @@ header, which git apply does not use for matching), git apply matched the existing 85 hunks against the first 85 broken blocks it found in file order. That happened to leave the last two operations in the file -- ROTATE_MASTER_ENCRYPTION_KEY and ROTATE_WORKSPACE_ENCRYPTION_KEY -- unpatched, so models.py shipped with two SyntaxErrors and broke the Python provider tests at import. Regenerate python.patch from the current fresh-codegen state of models.py so it has hunks for all 87 broken blocks (one per operation that supports two auth schemes) at current line numbers. Also commit the resulting two-line diff in models.py for the previously-unpatched Rotate*EncryptionKey blocks. pyproject.toml hunks (dynamic version via hatch) are preserved verbatim. Verified locally: - make smithy-clean smithy-clients now leaves clients/python/sdk/superposition_sdk/models.py free of broken auth blocks (0 matches for the missing-comma pattern). - python3 -c "import ast; ast.parse(open('.../models.py').read())" returns OK. - make smithy-updates leaves only the four CI-excluded SuperpositionClient*.java files dirty (known upstream Java-codegen nondeterminism). --- .../python/sdk/superposition_sdk/models.py | 4 +- smithy/patches/python.patch | 791 +++++++++++++++++- 2 files changed, 791 insertions(+), 4 deletions(-) diff --git a/clients/python/sdk/superposition_sdk/models.py b/clients/python/sdk/superposition_sdk/models.py index 8426292a7..1aa00a3e0 100644 --- a/clients/python/sdk/superposition_sdk/models.py +++ b/clients/python/sdk/superposition_sdk/models.py @@ -17068,7 +17068,7 @@ def _consumer(schema: Schema, de: ShapeDeserializer) -> None: ShapeID("io.superposition#InternalServerError"): InternalServerError, }), effective_auth_schemes = [ - ShapeID("smithy.api#httpBasicAuth") + ShapeID("smithy.api#httpBasicAuth"), ShapeID("smithy.api#httpBearerAuth") ] ) @@ -17532,7 +17532,7 @@ def _consumer(schema: Schema, de: ShapeDeserializer) -> None: ShapeID("io.superposition#InternalServerError"): InternalServerError, }), effective_auth_schemes = [ - ShapeID("smithy.api#httpBasicAuth") + ShapeID("smithy.api#httpBasicAuth"), ShapeID("smithy.api#httpBearerAuth") ] ) diff --git a/smithy/patches/python.patch b/smithy/patches/python.patch index d8bb36cf1..8c5fc17dd 100644 --- a/smithy/patches/python.patch +++ b/smithy/patches/python.patch @@ -3,7 +3,7 @@ index 7c295b28..b7a7bd7e 100644 --- a/clients/python/sdk/pyproject.toml +++ b/clients/python/sdk/pyproject.toml @@ -3,7 +3,7 @@ - + [project] name = "superposition_sdk" -version = "0.0.1" @@ -14,7 +14,7 @@ index 7c295b28..b7a7bd7e 100644 @@ -48,6 +48,10 @@ exclude = [ "tests", ] - + +[tool.hatch.version] +source = "env" +variable = "VERSION" @@ -22,3 +22,790 @@ index 7c295b28..b7a7bd7e 100644 [tool.pyright] typeCheckingMode = "strict" reportPrivateUsage = false + +diff --git a/clients/python/sdk/superposition_sdk/models.py b/clients/python/sdk/superposition_sdk/models.py +--- a/clients/python/sdk/superposition_sdk/models.py ++++ b/clients/python/sdk/superposition_sdk/models.py +@@ -691,7 +691,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -894,7 +894,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -1195,7 +1195,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -2004,7 +2004,7 @@ + ShapeID("io.superposition#ResourceNotFound"): ResourceNotFound, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -2294,7 +2294,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -2811,7 +2811,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -2908,7 +2908,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -3005,7 +3005,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -3142,7 +3142,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -3279,7 +3279,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -3449,7 +3449,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -3629,7 +3629,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -3794,7 +3794,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -3879,7 +3879,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -4039,7 +4039,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -4199,7 +4199,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -4402,7 +4402,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -4567,7 +4567,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -4732,7 +4732,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -4819,7 +4819,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -4984,7 +4984,7 @@ + ShapeID("io.superposition#WebhookFailed"): WebhookFailed, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -5218,7 +5218,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -5447,7 +5447,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -5715,7 +5715,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -5934,7 +5934,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -6145,7 +6145,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -6332,7 +6332,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -6484,7 +6484,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -6645,7 +6645,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -6792,7 +6792,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -7062,7 +7062,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -7312,7 +7312,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -7393,7 +7393,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -7547,7 +7547,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -7784,7 +7784,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -7992,7 +7992,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -8073,7 +8073,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -8243,7 +8243,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -8323,7 +8323,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -8448,7 +8448,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -8581,7 +8581,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -8707,7 +8707,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -8787,7 +8787,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -8959,7 +8959,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -9210,7 +9210,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -9436,7 +9436,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -9653,7 +9653,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -10088,7 +10088,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -10258,7 +10258,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -10497,7 +10497,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -10689,7 +10689,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -10891,7 +10891,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -11102,7 +11102,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -11326,7 +11326,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -11543,7 +11543,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -11767,7 +11767,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -11984,7 +11984,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -12296,7 +12296,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -12463,7 +12463,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -12731,7 +12731,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -12903,7 +12903,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -13225,7 +13225,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -13418,7 +13418,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -13565,7 +13565,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -13690,7 +13690,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -13823,7 +13823,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -14035,7 +14035,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -14161,7 +14161,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -14339,7 +14339,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -14517,7 +14517,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -14696,7 +14696,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -15015,7 +15015,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -15188,7 +15188,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -15414,7 +15414,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -15658,7 +15658,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -15904,7 +15904,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -16161,7 +16161,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -16419,7 +16419,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -16496,7 +16496,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -16675,7 +16675,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -16862,7 +16862,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -16947,7 +16947,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -17098,7 +17098,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -17257,7 +17257,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -17402,7 +17402,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -17641,7 +17641,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) +@@ -17887,7 +17887,7 @@ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ +- ShapeID("smithy.api#httpBasicAuth") ++ ShapeID("smithy.api#httpBasicAuth"), + ShapeID("smithy.api#httpBearerAuth") + ] + ) From 55403c34bcbc8e086dbbb9f2f67a83b632a8b994 Mon Sep 17 00:00:00 2001 From: Natarajan Kannan Date: Fri, 5 Jun 2026 07:50:57 +0530 Subject: [PATCH 10/15] test(supertoml): use ImportConfig{Json,Toml}Command instead of fetch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit c157366d's commit message flagged this as follow-up: now that make smithy-updates produces a usable SDK, the importConfig helper in config_import.test.ts can stop hand-rolling a fetch against /config/{toml,json}/import and go through the generated commands. The helper now wraps superpositionClient.send for ImportConfigJsonCommand or ImportConfigTomlCommand, takes typed import options (mode, overwrite, on_error, dry_run, value_merge) instead of x-import-* header strings, and surfaces ImportConfigOutput directly. Errors from the SDK are caught so the existing "expect 4xx" assertions keep working — we read the HTTP status off $metadata.httpStatusCode (or the wrapped $response on error). Call sites updated: - { "x-import-overwrite": "false" } -> { overwrite: false } - { "x-import-value-merge": "true" } -> { value_merge: true } - { "x-import-dry-run": "true" } -> { dry_run: true } - { "x-import-mode": "replace" } -> { mode: ImportMode.REPLACE } The local ImportSummary / EntityReport types are dropped in favour of the SDK's ImportConfigOutput, and the comment block explaining why the test went through fetch is removed (no longer applicable). --- tests/src/config_import.test.ts | 85 +++++++++++++++------------------ 1 file changed, 39 insertions(+), 46 deletions(-) diff --git a/tests/src/config_import.test.ts b/tests/src/config_import.test.ts index bc734ccd0..72901416c 100644 --- a/tests/src/config_import.test.ts +++ b/tests/src/config_import.test.ts @@ -6,18 +6,15 @@ import { ListDefaultConfigsCommand, ListContextsCommand, GetConfigJsonCommand, + ImportConfigJsonCommand, + ImportConfigTomlCommand, + type ImportConfigOutput, + ImportMode, + type ImportOnError, } from "@juspay/superposition-sdk"; import { superpositionClient, ENV } from "../env.ts"; import { describe, test, expect, beforeAll } from "bun:test"; -// Import lives at dedicated endpoints: POST /config/{toml,json}/import. These -// are modelled in Smithy as ImportConfigToml / ImportConfigJson, but until the -// SDK is regenerated (`make smithy-updates`) the commands aren't available, so -// the import calls here go through `fetch`. The SDK is used for setup and for -// verifying the resulting workspace state. Once the SDK is regenerated, the -// `importConfig` helper can be replaced with ImportConfigTomlCommand / -// ImportConfigJsonCommand. -// // Import in `replace` mode mirrors the *entire* workspace, so these tests run in // their own dedicated workspace to avoid clobbering data created by other suites. @@ -31,47 +28,43 @@ const FLAG = `imp_flag_${suffix}`; const DRYRUN_KEY = `imp_dryrun_${suffix}`; const TOML_KEY = `imp_toml_${suffix}`; -type ImportSummary = { - mode: string; - dry_run: boolean; - config_version?: string; - dimensions: EntityReport; - default_configs: EntityReport; - contexts: EntityReport; -}; -type EntityReport = { - created: number; - updated: number; - skipped: number; - deleted: number; - errors?: { id: string; error: string }[]; +type ImportOpts = { + mode?: ImportMode; + overwrite?: boolean; + on_error?: ImportOnError; + dry_run?: boolean; + value_merge?: boolean; }; async function importConfig( format: "toml" | "json", body: string, - extraHeaders: Record = {}, -): Promise<{ status: number; summary?: ImportSummary; raw: string }> { - const res = await fetch(`${ENV.baseUrl}/config/${format}/import`, { - method: "POST", - headers: { - Authorization: "Bearer some-token", - "x-org-id": ENV.org_id, - "x-workspace": IMPORT_WORKSPACE, - "Content-Type": - format === "toml" ? "application/toml" : "application/json", - ...extraHeaders, - }, - body, - }); - const raw = await res.text(); - let summary: ImportSummary | undefined; + opts: ImportOpts = {}, +): Promise<{ status: number; summary?: ImportConfigOutput; error?: unknown }> { + const base = { + workspace_id: IMPORT_WORKSPACE, + org_id: ENV.org_id, + ...opts, + }; + const cmd = + format === "json" + ? new ImportConfigJsonCommand({ ...base, json_config: body }) + : new ImportConfigTomlCommand({ ...base, toml_config: body }); try { - summary = JSON.parse(raw); - } catch { - summary = undefined; + const summary = await superpositionClient.send(cmd); + return { + status: summary.$metadata.httpStatusCode ?? 200, + summary, + }; + } catch (e: any) { + return { + status: + e?.$metadata?.httpStatusCode ?? + e?.$response?.statusCode ?? + 500, + error: e, + }; } - return { status: res.status, summary, raw }; } // A self-consistent JSON config: contexts only reference dimensions/keys defined @@ -222,7 +215,7 @@ describe("Config import - JSON", () => { const { status, summary } = await importConfig( "json", buildJsonConfig({ includeFlag: true }), - { "x-import-overwrite": "false" }, + { overwrite: false }, ); expect(status).toBe(200); @@ -245,7 +238,7 @@ describe("Config import - JSON", () => { }); const { status, summary } = await importConfig("json", body, { - "x-import-value-merge": "true", + value_merge: true, }); expect(status).toBe(200); @@ -270,7 +263,7 @@ describe("Config import - JSON", () => { }); const { status, summary } = await importConfig("json", body, { - "x-import-dry-run": "true", + dry_run: true, }); expect(status).toBe(200); @@ -288,7 +281,7 @@ describe("Config import - JSON", () => { const { status, summary } = await importConfig( "json", buildJsonConfig({ includeFlag: false }), - { "x-import-mode": "replace" }, + { mode: ImportMode.REPLACE }, ); expect(status).toBe(200); From 9f491dd88114fa7c46db3d12bfef981f2ae20764 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 7 Jun 2026 08:07:15 +0000 Subject: [PATCH 11/15] fix(ci): wait for superposition to boot before running integration tests The `test` target backgrounded `make run` (which rebuilds the frontend and server) and then probed /health with a single curl invocation. On a cold build cache the rebuild outlasts curl's retry window, so the health check failed with exit 7 before the server was up and the bun integration tests never ran, failing CI. Replace the one-shot curl with a bounded poll loop (up to 5 minutes, 2s apart) so the health check reliably waits for the build-and-boot to complete. --- makefile | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/makefile b/makefile index ac495cda4..9ade9f2e5 100644 --- a/makefile +++ b/makefile @@ -238,11 +238,22 @@ test: setup frontend superposition MASTER_ENCRYPTION_KEY=$${MASTER_ENCRYPTION_KEY:-"dGVzdC1tYXN0ZXIta2V5LTMyLWNoYXJhY3RlcnMtb2s="} \ $(MAKE) run & @echo "Awaiting superposition boot..." -## FIXME Curl doesn't retry. - @curl --silent --retry 10 \ - --connect-timeout 2 \ - --retry-all-errors \ - 'http://localhost:8080/health' 2>&1 > /dev/null +## `make run` (re)builds the frontend + server before the binary starts, so the +## health endpoint can take a while to come up. Poll it for up to 5 minutes +## instead of relying on curl's own retry handling, which gives up too early on +## connection-refused while the build is still running. + @for i in $$(seq 1 150); do \ + if curl --silent --fail --connect-timeout 2 \ + 'http://localhost:8080/health' > /dev/null 2>&1; then \ + echo "superposition is up"; \ + break; \ + fi; \ + if [ $$i -eq 150 ]; then \ + echo "superposition did not become healthy in time" >&2; \ + exit 1; \ + fi; \ + sleep 2; \ + done cd tests && bun test:clean $(MAKE) bindings-test $(MAKE) kill From 8573da08b890db5610e899e2a1dc159ee09fbfe0 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 22 Jun 2026 07:52:52 +0000 Subject: [PATCH 12/15] fix(supertoml): add description field to DimensionInfo literal in import test main added a required `description` field to `superposition_types::DimensionInfo` (#1037); the import test helper constructed the struct without it, breaking the build after the rebase. Default it to an empty string. --- crates/context_aware_config/src/api/config/import.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/context_aware_config/src/api/config/import.rs b/crates/context_aware_config/src/api/config/import.rs index e13bf5124..e65c56c0a 100644 --- a/crates/context_aware_config/src/api/config/import.rs +++ b/crates/context_aware_config/src/api/config/import.rs @@ -716,6 +716,7 @@ mod tests { dimension_type: DimensionType::Regular {}, dependency_graph: DependencyGraph::default(), value_compute_function_name: None, + description: String::new(), } } From e7989c661ba3f3358ef7d94731fa70ba0c717bf6 Mon Sep 17 00:00:00 2001 From: Saurav Suman Date: Tue, 23 Jun 2026 17:29:47 +0530 Subject: [PATCH 13/15] feat: add frontend changes for import and simplified backend behavior --- Cargo.lock | 1 + .../Model/ImportConfigJsonInput.hs | 56 +- .../Model/ImportConfigJsonOutput.hs | 36 +- .../Model/ImportConfigTomlInput.hs | 56 +- .../Model/ImportConfigTomlOutput.hs | 36 +- .../sdk/Io/Superposition/Model/ImportMode.hs | 44 - .../Io/Superposition/Model/ImportStrategy.hs | 43 + clients/haskell/sdk/SuperpositionSDK.cabal | 2 +- .../client/SuperpositionAsyncClient.java | 6 +- .../client/SuperpositionAsyncClientImpl.java | 2 +- .../client/SuperpositionClient.java | 6 +- .../client/SuperpositionClientImpl.java | 2 +- .../model/ImportConfigJsonInput.java | 120 +- .../model/ImportConfigJsonOutput.java | 38 +- .../model/ImportConfigTomlInput.java | 120 +- .../model/ImportConfigTomlOutput.java | 38 +- .../{ImportMode.java => ImportStrategy.java} | 61 +- .../src/commands/ImportConfigJsonCommand.ts | 6 +- .../src/commands/ImportConfigTomlCommand.ts | 6 +- clients/javascript/sdk/src/models/models_0.ts | 62 +- .../sdk/src/protocols/Aws_restJson1.ts | 24 +- .../sdk/superposition_sdk/_private/schemas.py | 105 +- .../python/sdk/superposition_sdk/config.py | 92 +- .../python/sdk/superposition_sdk/models.py | 106 +- .../python/sdk/superposition_sdk/serialize.py | 16 +- .../src/api/config/import.rs | 216 +-- crates/frontend/Cargo.toml | 5 + crates/frontend/src/api.rs | 183 ++- crates/frontend/src/app.rs | 7 + crates/frontend/src/components/side_nav.rs | 6 + crates/frontend/src/pages.rs | 1 + crates/frontend/src/pages/import_config.rs | 1263 +++++++++++++++++ crates/frontend/src/types.rs | 2 + crates/frontend/src/utils.rs | 53 + .../superposition_sdk/src/client/customize.rs | 2 + .../src/client/import_config_json.rs | 6 +- .../src/client/import_config_toml.rs | 6 +- .../_import_config_json_input.rs | 76 +- .../_import_config_json_output.rs | 26 +- .../operation/import_config_json/builders.rs | 46 +- .../_import_config_toml_input.rs | 76 +- .../_import_config_toml_output.rs | 26 +- .../operation/import_config_toml/builders.rs | 46 +- .../shape_import_config_json.rs | 54 +- .../shape_import_config_toml.rs | 54 +- crates/superposition_sdk/src/serde_util.rs | 16 +- crates/superposition_sdk/src/types.rs | 6 +- .../{_import_mode.rs => _import_strategy.rs} | 72 +- docs/docs/api/Superposition.openapi.json | 73 +- .../api/import-config-json.ParamsDetails.json | 2 +- .../api/import-config-json.StatusCodes.json | 2 +- docs/docs/api/import-config-json.api.mdx | 2 +- .../api/import-config-toml.ParamsDetails.json | 2 +- .../api/import-config-toml.StatusCodes.json | 2 +- docs/docs/api/import-config-toml.api.mdx | 2 +- .../import-export.md | 26 +- smithy/models/config.smithy | 47 +- tests/src/config_import.test.ts | 38 +- 58 files changed, 2185 insertions(+), 1343 deletions(-) delete mode 100644 clients/haskell/sdk/Io/Superposition/Model/ImportMode.hs create mode 100644 clients/haskell/sdk/Io/Superposition/Model/ImportStrategy.hs rename clients/java/sdk/src/main/java/io/juspay/superposition/model/{ImportMode.java => ImportStrategy.java} (62%) create mode 100644 crates/frontend/src/pages/import_config.rs rename crates/superposition_sdk/src/types/{_import_mode.rs => _import_strategy.rs} (59%) diff --git a/Cargo.lock b/Cargo.lock index e46b19359..24130209c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3694,6 +3694,7 @@ dependencies = [ "strum_macros 0.25.3", "superposition_derives", "superposition_types", + "toml 0.8.8", "url", "wasm-bindgen", "wasm-bindgen-futures", diff --git a/clients/haskell/sdk/Io/Superposition/Model/ImportConfigJsonInput.hs b/clients/haskell/sdk/Io/Superposition/Model/ImportConfigJsonInput.hs index 2d0002891..54bcd13d6 100644 --- a/clients/haskell/sdk/Io/Superposition/Model/ImportConfigJsonInput.hs +++ b/clients/haskell/sdk/Io/Superposition/Model/ImportConfigJsonInput.hs @@ -1,11 +1,9 @@ module Io.Superposition.Model.ImportConfigJsonInput ( setWorkspaceId, setOrgId, - setMode, - setOverwrite, + setStrategy, setOnError, setDryRun, - setValueMerge, setConfigTags, setJsonConfig, build, @@ -13,11 +11,9 @@ module Io.Superposition.Model.ImportConfigJsonInput ( ImportConfigJsonInput, workspace_id, org_id, - mode, - overwrite, + strategy, on_error, dry_run, - value_merge, config_tags, json_config ) where @@ -31,19 +27,17 @@ import qualified Data.Maybe import qualified Data.Text import qualified GHC.Generics import qualified GHC.Show -import qualified Io.Superposition.Model.ImportMode import qualified Io.Superposition.Model.ImportOnError +import qualified Io.Superposition.Model.ImportStrategy import qualified Io.Superposition.Utility import qualified Network.HTTP.Types.Method data ImportConfigJsonInput = ImportConfigJsonInput { workspace_id :: Data.Text.Text, org_id :: Data.Text.Text, - mode :: Data.Maybe.Maybe Io.Superposition.Model.ImportMode.ImportMode, - overwrite :: Data.Maybe.Maybe Bool, + strategy :: Data.Maybe.Maybe Io.Superposition.Model.ImportStrategy.ImportStrategy, on_error :: Data.Maybe.Maybe Io.Superposition.Model.ImportOnError.ImportOnError, dry_run :: Data.Maybe.Maybe Bool, - value_merge :: Data.Maybe.Maybe Bool, config_tags :: Data.Maybe.Maybe Data.Text.Text, json_config :: Data.Text.Text } deriving ( @@ -56,11 +50,9 @@ instance Data.Aeson.ToJSON ImportConfigJsonInput where toJSON a = Data.Aeson.object [ "workspace_id" Data.Aeson..= workspace_id a, "org_id" Data.Aeson..= org_id a, - "mode" Data.Aeson..= mode a, - "overwrite" Data.Aeson..= overwrite a, + "strategy" Data.Aeson..= strategy a, "on_error" Data.Aeson..= on_error a, "dry_run" Data.Aeson..= dry_run a, - "value_merge" Data.Aeson..= value_merge a, "config_tags" Data.Aeson..= config_tags a, "json_config" Data.Aeson..= json_config a ] @@ -72,11 +64,9 @@ instance Data.Aeson.FromJSON ImportConfigJsonInput where parseJSON = Data.Aeson.withObject "ImportConfigJsonInput" $ \v -> ImportConfigJsonInput Data.Functor.<$> (v Data.Aeson..: "workspace_id") Control.Applicative.<*> (v Data.Aeson..: "org_id") - Control.Applicative.<*> (v Data.Aeson..:? "mode") - Control.Applicative.<*> (v Data.Aeson..:? "overwrite") + Control.Applicative.<*> (v Data.Aeson..:? "strategy") Control.Applicative.<*> (v Data.Aeson..:? "on_error") Control.Applicative.<*> (v Data.Aeson..:? "dry_run") - Control.Applicative.<*> (v Data.Aeson..:? "value_merge") Control.Applicative.<*> (v Data.Aeson..:? "config_tags") Control.Applicative.<*> (v Data.Aeson..: "json_config") @@ -86,11 +76,9 @@ instance Data.Aeson.FromJSON ImportConfigJsonInput where data ImportConfigJsonInputBuilderState = ImportConfigJsonInputBuilderState { workspace_idBuilderState :: Data.Maybe.Maybe Data.Text.Text, org_idBuilderState :: Data.Maybe.Maybe Data.Text.Text, - modeBuilderState :: Data.Maybe.Maybe Io.Superposition.Model.ImportMode.ImportMode, - overwriteBuilderState :: Data.Maybe.Maybe Bool, + strategyBuilderState :: Data.Maybe.Maybe Io.Superposition.Model.ImportStrategy.ImportStrategy, on_errorBuilderState :: Data.Maybe.Maybe Io.Superposition.Model.ImportOnError.ImportOnError, dry_runBuilderState :: Data.Maybe.Maybe Bool, - value_mergeBuilderState :: Data.Maybe.Maybe Bool, config_tagsBuilderState :: Data.Maybe.Maybe Data.Text.Text, json_configBuilderState :: Data.Maybe.Maybe Data.Text.Text } deriving ( @@ -101,11 +89,9 @@ defaultBuilderState :: ImportConfigJsonInputBuilderState defaultBuilderState = ImportConfigJsonInputBuilderState { workspace_idBuilderState = Data.Maybe.Nothing, org_idBuilderState = Data.Maybe.Nothing, - modeBuilderState = Data.Maybe.Nothing, - overwriteBuilderState = Data.Maybe.Nothing, + strategyBuilderState = Data.Maybe.Nothing, on_errorBuilderState = Data.Maybe.Nothing, dry_runBuilderState = Data.Maybe.Nothing, - value_mergeBuilderState = Data.Maybe.Nothing, config_tagsBuilderState = Data.Maybe.Nothing, json_configBuilderState = Data.Maybe.Nothing } @@ -120,13 +106,9 @@ setOrgId :: Data.Text.Text -> ImportConfigJsonInputBuilder () setOrgId value = Control.Monad.State.Strict.modify (\s -> (s { org_idBuilderState = Data.Maybe.Just value })) -setMode :: Data.Maybe.Maybe Io.Superposition.Model.ImportMode.ImportMode -> ImportConfigJsonInputBuilder () -setMode value = - Control.Monad.State.Strict.modify (\s -> (s { modeBuilderState = value })) - -setOverwrite :: Data.Maybe.Maybe Bool -> ImportConfigJsonInputBuilder () -setOverwrite value = - Control.Monad.State.Strict.modify (\s -> (s { overwriteBuilderState = value })) +setStrategy :: Data.Maybe.Maybe Io.Superposition.Model.ImportStrategy.ImportStrategy -> ImportConfigJsonInputBuilder () +setStrategy value = + Control.Monad.State.Strict.modify (\s -> (s { strategyBuilderState = value })) setOnError :: Data.Maybe.Maybe Io.Superposition.Model.ImportOnError.ImportOnError -> ImportConfigJsonInputBuilder () setOnError value = @@ -136,10 +118,6 @@ setDryRun :: Data.Maybe.Maybe Bool -> ImportConfigJsonInputBuilder () setDryRun value = Control.Monad.State.Strict.modify (\s -> (s { dry_runBuilderState = value })) -setValueMerge :: Data.Maybe.Maybe Bool -> ImportConfigJsonInputBuilder () -setValueMerge value = - Control.Monad.State.Strict.modify (\s -> (s { value_mergeBuilderState = value })) - setConfigTags :: Data.Maybe.Maybe Data.Text.Text -> ImportConfigJsonInputBuilder () setConfigTags value = Control.Monad.State.Strict.modify (\s -> (s { config_tagsBuilderState = value })) @@ -153,21 +131,17 @@ build builder = do let (_, st) = Control.Monad.State.Strict.runState builder defaultBuilderState workspace_id' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigJsonInput.ImportConfigJsonInput.workspace_id is a required property.") Data.Either.Right (workspace_idBuilderState st) org_id' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigJsonInput.ImportConfigJsonInput.org_id is a required property.") Data.Either.Right (org_idBuilderState st) - mode' <- Data.Either.Right (modeBuilderState st) - overwrite' <- Data.Either.Right (overwriteBuilderState st) + strategy' <- Data.Either.Right (strategyBuilderState st) on_error' <- Data.Either.Right (on_errorBuilderState st) dry_run' <- Data.Either.Right (dry_runBuilderState st) - value_merge' <- Data.Either.Right (value_mergeBuilderState st) config_tags' <- Data.Either.Right (config_tagsBuilderState st) json_config' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigJsonInput.ImportConfigJsonInput.json_config is a required property.") Data.Either.Right (json_configBuilderState st) Data.Either.Right (ImportConfigJsonInput { workspace_id = workspace_id', org_id = org_id', - mode = mode', - overwrite = overwrite', + strategy = strategy', on_error = on_error', dry_run = dry_run', - value_merge = value_merge', config_tags = config_tags', json_config = json_config' }) @@ -183,12 +157,10 @@ instance Io.Superposition.Utility.IntoRequestBuilder ImportConfigJsonInput where ] Io.Superposition.Utility.serHeader "x-workspace" (workspace_id self) - Io.Superposition.Utility.serHeader "x-import-mode" (mode self) Io.Superposition.Utility.serHeader "x-org-id" (org_id self) Io.Superposition.Utility.serHeader "x-import-on-error" (on_error self) Io.Superposition.Utility.serHeader "x-import-dry-run" (dry_run self) - Io.Superposition.Utility.serHeader "x-import-overwrite" (overwrite self) - Io.Superposition.Utility.serHeader "x-import-value-merge" (value_merge self) + Io.Superposition.Utility.serHeader "x-import-strategy" (strategy self) Io.Superposition.Utility.serHeader "x-config-tags" (config_tags self) Io.Superposition.Utility.serBody "text/plain" (json_config self) diff --git a/clients/haskell/sdk/Io/Superposition/Model/ImportConfigJsonOutput.hs b/clients/haskell/sdk/Io/Superposition/Model/ImportConfigJsonOutput.hs index 1eeb3d457..819b66dad 100644 --- a/clients/haskell/sdk/Io/Superposition/Model/ImportConfigJsonOutput.hs +++ b/clients/haskell/sdk/Io/Superposition/Model/ImportConfigJsonOutput.hs @@ -1,5 +1,5 @@ module Io.Superposition.Model.ImportConfigJsonOutput ( - setMode, + setStrategy, setDryRun, setConfigVersion, setDimensions, @@ -8,7 +8,7 @@ module Io.Superposition.Model.ImportConfigJsonOutput ( build, ImportConfigJsonOutputBuilder, ImportConfigJsonOutput, - mode, + strategy, dry_run, config_version, dimensions, @@ -30,7 +30,7 @@ import qualified Io.Superposition.Utility import qualified Network.HTTP.Types data ImportConfigJsonOutput = ImportConfigJsonOutput { - mode :: Data.Text.Text, + strategy :: Data.Text.Text, dry_run :: Bool, config_version :: Data.Maybe.Maybe Data.Text.Text, dimensions :: Io.Superposition.Model.ImportEntityReport.ImportEntityReport, @@ -44,7 +44,7 @@ data ImportConfigJsonOutput = ImportConfigJsonOutput { instance Data.Aeson.ToJSON ImportConfigJsonOutput where toJSON a = Data.Aeson.object [ - "mode" Data.Aeson..= mode a, + "strategy" Data.Aeson..= strategy a, "dry_run" Data.Aeson..= dry_run a, "config_version" Data.Aeson..= config_version a, "dimensions" Data.Aeson..= dimensions a, @@ -57,7 +57,7 @@ instance Io.Superposition.Utility.SerializeBody ImportConfigJsonOutput instance Data.Aeson.FromJSON ImportConfigJsonOutput where parseJSON = Data.Aeson.withObject "ImportConfigJsonOutput" $ \v -> ImportConfigJsonOutput - Data.Functor.<$> (v Data.Aeson..: "mode") + Data.Functor.<$> (v Data.Aeson..: "strategy") Control.Applicative.<*> (v Data.Aeson..: "dry_run") Control.Applicative.<*> (v Data.Aeson..:? "config_version") Control.Applicative.<*> (v Data.Aeson..: "dimensions") @@ -68,7 +68,7 @@ instance Data.Aeson.FromJSON ImportConfigJsonOutput where data ImportConfigJsonOutputBuilderState = ImportConfigJsonOutputBuilderState { - modeBuilderState :: Data.Maybe.Maybe Data.Text.Text, + strategyBuilderState :: Data.Maybe.Maybe Data.Text.Text, dry_runBuilderState :: Data.Maybe.Maybe Bool, config_versionBuilderState :: Data.Maybe.Maybe Data.Text.Text, dimensionsBuilderState :: Data.Maybe.Maybe Io.Superposition.Model.ImportEntityReport.ImportEntityReport, @@ -80,7 +80,7 @@ data ImportConfigJsonOutputBuilderState = ImportConfigJsonOutputBuilderState { defaultBuilderState :: ImportConfigJsonOutputBuilderState defaultBuilderState = ImportConfigJsonOutputBuilderState { - modeBuilderState = Data.Maybe.Nothing, + strategyBuilderState = Data.Maybe.Nothing, dry_runBuilderState = Data.Maybe.Nothing, config_versionBuilderState = Data.Maybe.Nothing, dimensionsBuilderState = Data.Maybe.Nothing, @@ -90,9 +90,9 @@ defaultBuilderState = ImportConfigJsonOutputBuilderState { type ImportConfigJsonOutputBuilder = Control.Monad.State.Strict.State ImportConfigJsonOutputBuilderState -setMode :: Data.Text.Text -> ImportConfigJsonOutputBuilder () -setMode value = - Control.Monad.State.Strict.modify (\s -> (s { modeBuilderState = Data.Maybe.Just value })) +setStrategy :: Data.Text.Text -> ImportConfigJsonOutputBuilder () +setStrategy value = + Control.Monad.State.Strict.modify (\s -> (s { strategyBuilderState = Data.Maybe.Just value })) setDryRun :: Bool -> ImportConfigJsonOutputBuilder () setDryRun value = @@ -117,14 +117,14 @@ setContexts value = build :: ImportConfigJsonOutputBuilder () -> Data.Either.Either Data.Text.Text ImportConfigJsonOutput build builder = do let (_, st) = Control.Monad.State.Strict.runState builder defaultBuilderState - mode' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigJsonOutput.ImportConfigJsonOutput.mode is a required property.") Data.Either.Right (modeBuilderState st) + strategy' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigJsonOutput.ImportConfigJsonOutput.strategy is a required property.") Data.Either.Right (strategyBuilderState st) dry_run' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigJsonOutput.ImportConfigJsonOutput.dry_run is a required property.") Data.Either.Right (dry_runBuilderState st) config_version' <- Data.Either.Right (config_versionBuilderState st) dimensions' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigJsonOutput.ImportConfigJsonOutput.dimensions is a required property.") Data.Either.Right (dimensionsBuilderState st) default_configs' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigJsonOutput.ImportConfigJsonOutput.default_configs is a required property.") Data.Either.Right (default_configsBuilderState st) contexts' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigJsonOutput.ImportConfigJsonOutput.contexts is a required property.") Data.Either.Right (contextsBuilderState st) Data.Either.Right (ImportConfigJsonOutput { - mode = mode', + strategy = strategy', dry_run = dry_run', config_version = config_version', dimensions = dimensions', @@ -137,18 +137,18 @@ instance Io.Superposition.Utility.FromResponseParser ImportConfigJsonOutput wher expectedStatus = (Network.HTTP.Types.mkStatus 200 "") responseParser = do - var0 <- Io.Superposition.Utility.deSerField "mode" - var1 <- Io.Superposition.Utility.deSerField "dry_run" - var2 <- Io.Superposition.Utility.deSerField "contexts" + var0 <- Io.Superposition.Utility.deSerField "dry_run" + var1 <- Io.Superposition.Utility.deSerField "contexts" + var2 <- Io.Superposition.Utility.deSerField "strategy" var3 <- Io.Superposition.Utility.deSerField "default_configs" var4 <- Io.Superposition.Utility.deSerField "config_version" var5 <- Io.Superposition.Utility.deSerField "dimensions" pure $ ImportConfigJsonOutput { - mode = var0, - dry_run = var1, + strategy = var2, + dry_run = var0, config_version = var4, dimensions = var5, default_configs = var3, - contexts = var2 + contexts = var1 } diff --git a/clients/haskell/sdk/Io/Superposition/Model/ImportConfigTomlInput.hs b/clients/haskell/sdk/Io/Superposition/Model/ImportConfigTomlInput.hs index 00145e75b..b8fbee919 100644 --- a/clients/haskell/sdk/Io/Superposition/Model/ImportConfigTomlInput.hs +++ b/clients/haskell/sdk/Io/Superposition/Model/ImportConfigTomlInput.hs @@ -1,11 +1,9 @@ module Io.Superposition.Model.ImportConfigTomlInput ( setWorkspaceId, setOrgId, - setMode, - setOverwrite, + setStrategy, setOnError, setDryRun, - setValueMerge, setConfigTags, setTomlConfig, build, @@ -13,11 +11,9 @@ module Io.Superposition.Model.ImportConfigTomlInput ( ImportConfigTomlInput, workspace_id, org_id, - mode, - overwrite, + strategy, on_error, dry_run, - value_merge, config_tags, toml_config ) where @@ -31,19 +27,17 @@ import qualified Data.Maybe import qualified Data.Text import qualified GHC.Generics import qualified GHC.Show -import qualified Io.Superposition.Model.ImportMode import qualified Io.Superposition.Model.ImportOnError +import qualified Io.Superposition.Model.ImportStrategy import qualified Io.Superposition.Utility import qualified Network.HTTP.Types.Method data ImportConfigTomlInput = ImportConfigTomlInput { workspace_id :: Data.Text.Text, org_id :: Data.Text.Text, - mode :: Data.Maybe.Maybe Io.Superposition.Model.ImportMode.ImportMode, - overwrite :: Data.Maybe.Maybe Bool, + strategy :: Data.Maybe.Maybe Io.Superposition.Model.ImportStrategy.ImportStrategy, on_error :: Data.Maybe.Maybe Io.Superposition.Model.ImportOnError.ImportOnError, dry_run :: Data.Maybe.Maybe Bool, - value_merge :: Data.Maybe.Maybe Bool, config_tags :: Data.Maybe.Maybe Data.Text.Text, toml_config :: Data.Text.Text } deriving ( @@ -56,11 +50,9 @@ instance Data.Aeson.ToJSON ImportConfigTomlInput where toJSON a = Data.Aeson.object [ "workspace_id" Data.Aeson..= workspace_id a, "org_id" Data.Aeson..= org_id a, - "mode" Data.Aeson..= mode a, - "overwrite" Data.Aeson..= overwrite a, + "strategy" Data.Aeson..= strategy a, "on_error" Data.Aeson..= on_error a, "dry_run" Data.Aeson..= dry_run a, - "value_merge" Data.Aeson..= value_merge a, "config_tags" Data.Aeson..= config_tags a, "toml_config" Data.Aeson..= toml_config a ] @@ -72,11 +64,9 @@ instance Data.Aeson.FromJSON ImportConfigTomlInput where parseJSON = Data.Aeson.withObject "ImportConfigTomlInput" $ \v -> ImportConfigTomlInput Data.Functor.<$> (v Data.Aeson..: "workspace_id") Control.Applicative.<*> (v Data.Aeson..: "org_id") - Control.Applicative.<*> (v Data.Aeson..:? "mode") - Control.Applicative.<*> (v Data.Aeson..:? "overwrite") + Control.Applicative.<*> (v Data.Aeson..:? "strategy") Control.Applicative.<*> (v Data.Aeson..:? "on_error") Control.Applicative.<*> (v Data.Aeson..:? "dry_run") - Control.Applicative.<*> (v Data.Aeson..:? "value_merge") Control.Applicative.<*> (v Data.Aeson..:? "config_tags") Control.Applicative.<*> (v Data.Aeson..: "toml_config") @@ -86,11 +76,9 @@ instance Data.Aeson.FromJSON ImportConfigTomlInput where data ImportConfigTomlInputBuilderState = ImportConfigTomlInputBuilderState { workspace_idBuilderState :: Data.Maybe.Maybe Data.Text.Text, org_idBuilderState :: Data.Maybe.Maybe Data.Text.Text, - modeBuilderState :: Data.Maybe.Maybe Io.Superposition.Model.ImportMode.ImportMode, - overwriteBuilderState :: Data.Maybe.Maybe Bool, + strategyBuilderState :: Data.Maybe.Maybe Io.Superposition.Model.ImportStrategy.ImportStrategy, on_errorBuilderState :: Data.Maybe.Maybe Io.Superposition.Model.ImportOnError.ImportOnError, dry_runBuilderState :: Data.Maybe.Maybe Bool, - value_mergeBuilderState :: Data.Maybe.Maybe Bool, config_tagsBuilderState :: Data.Maybe.Maybe Data.Text.Text, toml_configBuilderState :: Data.Maybe.Maybe Data.Text.Text } deriving ( @@ -101,11 +89,9 @@ defaultBuilderState :: ImportConfigTomlInputBuilderState defaultBuilderState = ImportConfigTomlInputBuilderState { workspace_idBuilderState = Data.Maybe.Nothing, org_idBuilderState = Data.Maybe.Nothing, - modeBuilderState = Data.Maybe.Nothing, - overwriteBuilderState = Data.Maybe.Nothing, + strategyBuilderState = Data.Maybe.Nothing, on_errorBuilderState = Data.Maybe.Nothing, dry_runBuilderState = Data.Maybe.Nothing, - value_mergeBuilderState = Data.Maybe.Nothing, config_tagsBuilderState = Data.Maybe.Nothing, toml_configBuilderState = Data.Maybe.Nothing } @@ -120,13 +106,9 @@ setOrgId :: Data.Text.Text -> ImportConfigTomlInputBuilder () setOrgId value = Control.Monad.State.Strict.modify (\s -> (s { org_idBuilderState = Data.Maybe.Just value })) -setMode :: Data.Maybe.Maybe Io.Superposition.Model.ImportMode.ImportMode -> ImportConfigTomlInputBuilder () -setMode value = - Control.Monad.State.Strict.modify (\s -> (s { modeBuilderState = value })) - -setOverwrite :: Data.Maybe.Maybe Bool -> ImportConfigTomlInputBuilder () -setOverwrite value = - Control.Monad.State.Strict.modify (\s -> (s { overwriteBuilderState = value })) +setStrategy :: Data.Maybe.Maybe Io.Superposition.Model.ImportStrategy.ImportStrategy -> ImportConfigTomlInputBuilder () +setStrategy value = + Control.Monad.State.Strict.modify (\s -> (s { strategyBuilderState = value })) setOnError :: Data.Maybe.Maybe Io.Superposition.Model.ImportOnError.ImportOnError -> ImportConfigTomlInputBuilder () setOnError value = @@ -136,10 +118,6 @@ setDryRun :: Data.Maybe.Maybe Bool -> ImportConfigTomlInputBuilder () setDryRun value = Control.Monad.State.Strict.modify (\s -> (s { dry_runBuilderState = value })) -setValueMerge :: Data.Maybe.Maybe Bool -> ImportConfigTomlInputBuilder () -setValueMerge value = - Control.Monad.State.Strict.modify (\s -> (s { value_mergeBuilderState = value })) - setConfigTags :: Data.Maybe.Maybe Data.Text.Text -> ImportConfigTomlInputBuilder () setConfigTags value = Control.Monad.State.Strict.modify (\s -> (s { config_tagsBuilderState = value })) @@ -153,21 +131,17 @@ build builder = do let (_, st) = Control.Monad.State.Strict.runState builder defaultBuilderState workspace_id' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigTomlInput.ImportConfigTomlInput.workspace_id is a required property.") Data.Either.Right (workspace_idBuilderState st) org_id' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigTomlInput.ImportConfigTomlInput.org_id is a required property.") Data.Either.Right (org_idBuilderState st) - mode' <- Data.Either.Right (modeBuilderState st) - overwrite' <- Data.Either.Right (overwriteBuilderState st) + strategy' <- Data.Either.Right (strategyBuilderState st) on_error' <- Data.Either.Right (on_errorBuilderState st) dry_run' <- Data.Either.Right (dry_runBuilderState st) - value_merge' <- Data.Either.Right (value_mergeBuilderState st) config_tags' <- Data.Either.Right (config_tagsBuilderState st) toml_config' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigTomlInput.ImportConfigTomlInput.toml_config is a required property.") Data.Either.Right (toml_configBuilderState st) Data.Either.Right (ImportConfigTomlInput { workspace_id = workspace_id', org_id = org_id', - mode = mode', - overwrite = overwrite', + strategy = strategy', on_error = on_error', dry_run = dry_run', - value_merge = value_merge', config_tags = config_tags', toml_config = toml_config' }) @@ -183,12 +157,10 @@ instance Io.Superposition.Utility.IntoRequestBuilder ImportConfigTomlInput where ] Io.Superposition.Utility.serHeader "x-workspace" (workspace_id self) - Io.Superposition.Utility.serHeader "x-import-mode" (mode self) Io.Superposition.Utility.serHeader "x-org-id" (org_id self) Io.Superposition.Utility.serHeader "x-import-on-error" (on_error self) Io.Superposition.Utility.serHeader "x-import-dry-run" (dry_run self) - Io.Superposition.Utility.serHeader "x-import-overwrite" (overwrite self) - Io.Superposition.Utility.serHeader "x-import-value-merge" (value_merge self) + Io.Superposition.Utility.serHeader "x-import-strategy" (strategy self) Io.Superposition.Utility.serHeader "x-config-tags" (config_tags self) Io.Superposition.Utility.serBody "text/plain" (toml_config self) diff --git a/clients/haskell/sdk/Io/Superposition/Model/ImportConfigTomlOutput.hs b/clients/haskell/sdk/Io/Superposition/Model/ImportConfigTomlOutput.hs index d99c68953..c9926ec37 100644 --- a/clients/haskell/sdk/Io/Superposition/Model/ImportConfigTomlOutput.hs +++ b/clients/haskell/sdk/Io/Superposition/Model/ImportConfigTomlOutput.hs @@ -1,5 +1,5 @@ module Io.Superposition.Model.ImportConfigTomlOutput ( - setMode, + setStrategy, setDryRun, setConfigVersion, setDimensions, @@ -8,7 +8,7 @@ module Io.Superposition.Model.ImportConfigTomlOutput ( build, ImportConfigTomlOutputBuilder, ImportConfigTomlOutput, - mode, + strategy, dry_run, config_version, dimensions, @@ -30,7 +30,7 @@ import qualified Io.Superposition.Utility import qualified Network.HTTP.Types data ImportConfigTomlOutput = ImportConfigTomlOutput { - mode :: Data.Text.Text, + strategy :: Data.Text.Text, dry_run :: Bool, config_version :: Data.Maybe.Maybe Data.Text.Text, dimensions :: Io.Superposition.Model.ImportEntityReport.ImportEntityReport, @@ -44,7 +44,7 @@ data ImportConfigTomlOutput = ImportConfigTomlOutput { instance Data.Aeson.ToJSON ImportConfigTomlOutput where toJSON a = Data.Aeson.object [ - "mode" Data.Aeson..= mode a, + "strategy" Data.Aeson..= strategy a, "dry_run" Data.Aeson..= dry_run a, "config_version" Data.Aeson..= config_version a, "dimensions" Data.Aeson..= dimensions a, @@ -57,7 +57,7 @@ instance Io.Superposition.Utility.SerializeBody ImportConfigTomlOutput instance Data.Aeson.FromJSON ImportConfigTomlOutput where parseJSON = Data.Aeson.withObject "ImportConfigTomlOutput" $ \v -> ImportConfigTomlOutput - Data.Functor.<$> (v Data.Aeson..: "mode") + Data.Functor.<$> (v Data.Aeson..: "strategy") Control.Applicative.<*> (v Data.Aeson..: "dry_run") Control.Applicative.<*> (v Data.Aeson..:? "config_version") Control.Applicative.<*> (v Data.Aeson..: "dimensions") @@ -68,7 +68,7 @@ instance Data.Aeson.FromJSON ImportConfigTomlOutput where data ImportConfigTomlOutputBuilderState = ImportConfigTomlOutputBuilderState { - modeBuilderState :: Data.Maybe.Maybe Data.Text.Text, + strategyBuilderState :: Data.Maybe.Maybe Data.Text.Text, dry_runBuilderState :: Data.Maybe.Maybe Bool, config_versionBuilderState :: Data.Maybe.Maybe Data.Text.Text, dimensionsBuilderState :: Data.Maybe.Maybe Io.Superposition.Model.ImportEntityReport.ImportEntityReport, @@ -80,7 +80,7 @@ data ImportConfigTomlOutputBuilderState = ImportConfigTomlOutputBuilderState { defaultBuilderState :: ImportConfigTomlOutputBuilderState defaultBuilderState = ImportConfigTomlOutputBuilderState { - modeBuilderState = Data.Maybe.Nothing, + strategyBuilderState = Data.Maybe.Nothing, dry_runBuilderState = Data.Maybe.Nothing, config_versionBuilderState = Data.Maybe.Nothing, dimensionsBuilderState = Data.Maybe.Nothing, @@ -90,9 +90,9 @@ defaultBuilderState = ImportConfigTomlOutputBuilderState { type ImportConfigTomlOutputBuilder = Control.Monad.State.Strict.State ImportConfigTomlOutputBuilderState -setMode :: Data.Text.Text -> ImportConfigTomlOutputBuilder () -setMode value = - Control.Monad.State.Strict.modify (\s -> (s { modeBuilderState = Data.Maybe.Just value })) +setStrategy :: Data.Text.Text -> ImportConfigTomlOutputBuilder () +setStrategy value = + Control.Monad.State.Strict.modify (\s -> (s { strategyBuilderState = Data.Maybe.Just value })) setDryRun :: Bool -> ImportConfigTomlOutputBuilder () setDryRun value = @@ -117,14 +117,14 @@ setContexts value = build :: ImportConfigTomlOutputBuilder () -> Data.Either.Either Data.Text.Text ImportConfigTomlOutput build builder = do let (_, st) = Control.Monad.State.Strict.runState builder defaultBuilderState - mode' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigTomlOutput.ImportConfigTomlOutput.mode is a required property.") Data.Either.Right (modeBuilderState st) + strategy' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigTomlOutput.ImportConfigTomlOutput.strategy is a required property.") Data.Either.Right (strategyBuilderState st) dry_run' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigTomlOutput.ImportConfigTomlOutput.dry_run is a required property.") Data.Either.Right (dry_runBuilderState st) config_version' <- Data.Either.Right (config_versionBuilderState st) dimensions' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigTomlOutput.ImportConfigTomlOutput.dimensions is a required property.") Data.Either.Right (dimensionsBuilderState st) default_configs' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigTomlOutput.ImportConfigTomlOutput.default_configs is a required property.") Data.Either.Right (default_configsBuilderState st) contexts' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ImportConfigTomlOutput.ImportConfigTomlOutput.contexts is a required property.") Data.Either.Right (contextsBuilderState st) Data.Either.Right (ImportConfigTomlOutput { - mode = mode', + strategy = strategy', dry_run = dry_run', config_version = config_version', dimensions = dimensions', @@ -137,18 +137,18 @@ instance Io.Superposition.Utility.FromResponseParser ImportConfigTomlOutput wher expectedStatus = (Network.HTTP.Types.mkStatus 200 "") responseParser = do - var0 <- Io.Superposition.Utility.deSerField "mode" - var1 <- Io.Superposition.Utility.deSerField "dry_run" - var2 <- Io.Superposition.Utility.deSerField "contexts" + var0 <- Io.Superposition.Utility.deSerField "dry_run" + var1 <- Io.Superposition.Utility.deSerField "contexts" + var2 <- Io.Superposition.Utility.deSerField "strategy" var3 <- Io.Superposition.Utility.deSerField "default_configs" var4 <- Io.Superposition.Utility.deSerField "config_version" var5 <- Io.Superposition.Utility.deSerField "dimensions" pure $ ImportConfigTomlOutput { - mode = var0, - dry_run = var1, + strategy = var2, + dry_run = var0, config_version = var4, dimensions = var5, default_configs = var3, - contexts = var2 + contexts = var1 } diff --git a/clients/haskell/sdk/Io/Superposition/Model/ImportMode.hs b/clients/haskell/sdk/Io/Superposition/Model/ImportMode.hs deleted file mode 100644 index 052ba8d7f..000000000 --- a/clients/haskell/sdk/Io/Superposition/Model/ImportMode.hs +++ /dev/null @@ -1,44 +0,0 @@ -module Io.Superposition.Model.ImportMode ( - ImportMode(..) -) where -import qualified Data.Aeson -import qualified Data.Eq -import qualified Data.Text -import qualified Data.Text.Encoding -import qualified GHC.Generics -import qualified GHC.Show -import qualified Io.Superposition.Utility - --- Enum implementation for ImportMode -data ImportMode = - MERGE - | REPLACE - deriving ( - GHC.Generics.Generic, - Data.Eq.Eq, - GHC.Show.Show - ) - -instance Data.Aeson.ToJSON ImportMode where - toJSON MERGE = Data.Aeson.String $ Data.Text.pack "merge" - toJSON REPLACE = Data.Aeson.String $ Data.Text.pack "replace" - -instance Data.Aeson.FromJSON ImportMode where - parseJSON = Data.Aeson.withText "ImportMode" $ \v -> - case v of - "merge" -> pure MERGE - "replace" -> pure REPLACE - _ -> fail $ "Unknown value for ImportMode: " <> Data.Text.unpack v - - - -instance Io.Superposition.Utility.SerDe ImportMode where - serializeElement MERGE = Data.Text.Encoding.encodeUtf8 $ Data.Text.pack "merge" - serializeElement REPLACE = Data.Text.Encoding.encodeUtf8 $ Data.Text.pack "replace" - deSerializeElement bs = case Data.Text.Encoding.decodeUtf8 bs of - "merge" -> Right MERGE - "replace" -> Right REPLACE - e -> Left ("Failed to de-serialize ImportMode, encountered unknown variant: " ++ (show bs)) - - - diff --git a/clients/haskell/sdk/Io/Superposition/Model/ImportStrategy.hs b/clients/haskell/sdk/Io/Superposition/Model/ImportStrategy.hs new file mode 100644 index 000000000..819250ea3 --- /dev/null +++ b/clients/haskell/sdk/Io/Superposition/Model/ImportStrategy.hs @@ -0,0 +1,43 @@ +module Io.Superposition.Model.ImportStrategy ( + ImportStrategy(..) +) where +import qualified Data.Aeson +import qualified Data.Eq +import qualified Data.Text +import qualified Data.Text.Encoding +import qualified GHC.Generics +import qualified GHC.Show +import qualified Io.Superposition.Utility + +-- Enum implementation for ImportStrategy +data ImportStrategy = + CREATE_ONLY + | UPSERT + | REPLACE + deriving ( + GHC.Generics.Generic, + Data.Eq.Eq, + GHC.Show.Show + ) + +instance Data.Aeson.ToJSON ImportStrategy where + toJSON CREATE_ONLY = Data.Aeson.String $ Data.Text.pack "create_only" + toJSON UPSERT = Data.Aeson.String $ Data.Text.pack "upsert" + toJSON REPLACE = Data.Aeson.String $ Data.Text.pack "replace" + +instance Data.Aeson.FromJSON ImportStrategy where + parseJSON = Data.Aeson.withText "ImportStrategy" $ \v -> + case v of + "create_only" -> pure CREATE_ONLY + "upsert" -> pure UPSERT + "replace" -> pure REPLACE + _ -> fail $ "Unknown value for ImportStrategy: " <> Data.Text.unpack v +instance Io.Superposition.Utility.SerDe ImportStrategy where + serializeElement CREATE_ONLY = Data.Text.Encoding.encodeUtf8 $ Data.Text.pack "create_only" + serializeElement UPSERT = Data.Text.Encoding.encodeUtf8 $ Data.Text.pack "upsert" + serializeElement REPLACE = Data.Text.Encoding.encodeUtf8 $ Data.Text.pack "replace" + deSerializeElement bs = case Data.Text.Encoding.decodeUtf8 bs of + "create_only" -> Right CREATE_ONLY + "upsert" -> Right UPSERT + "replace" -> Right REPLACE + e -> Left ("Failed to de-serialize ImportStrategy, encountered unknown variant: " ++ (show bs)) diff --git a/clients/haskell/sdk/SuperpositionSDK.cabal b/clients/haskell/sdk/SuperpositionSDK.cabal index 76af397b5..bd77b1ef0 100644 --- a/clients/haskell/sdk/SuperpositionSDK.cabal +++ b/clients/haskell/sdk/SuperpositionSDK.cabal @@ -201,6 +201,7 @@ library Io.Superposition.Model.ListVersionsInput, Io.Superposition.Command.DeleteSecret, Io.Superposition.Command.RotateMasterEncryptionKey, + Io.Superposition.Model.ImportStrategy, Io.Superposition.Model.DeleteTypeTemplatesInput, Io.Superposition.Model.GetResolvedConfigInput, Io.Superposition.Model.Bucket, @@ -244,7 +245,6 @@ library Io.Superposition.Model.GetConfigTomlOutput, Io.Superposition.Model.GetExperimentGroupInput, Io.Superposition.Command.Test, - Io.Superposition.Model.ImportMode, Io.Superposition.Model.UpdateVariableOutput, Io.Superposition.Model.DeleteContextOutput, Io.Superposition.Command.CreateSecret, diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClient.java b/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClient.java index eeab27ee4..2411f90fc 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClient.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClient.java @@ -1941,12 +1941,12 @@ final class Builder extends Client.Builder { Node.objectNode() ); - private static final HttpBasicAuthTrait httpBasicAuthScheme = new HttpBasicAuthTrait(); - private static final AuthSchemeFactory httpBasicAuthSchemeFactory = new HttpBasicAuthAuthScheme.Factory(); - private static final HttpBearerAuthTrait httpBearerAuthScheme = new HttpBearerAuthTrait(); private static final AuthSchemeFactory httpBearerAuthSchemeFactory = new HttpBearerAuthScheme.Factory(); + private static final HttpBasicAuthTrait httpBasicAuthScheme = new HttpBasicAuthTrait(); + private static final AuthSchemeFactory httpBasicAuthSchemeFactory = new HttpBasicAuthAuthScheme.Factory(); + private Builder() { configBuilder().putSupportedAuthSchemes(httpBasicAuthSchemeFactory.createAuthScheme(httpBasicAuthScheme), httpBearerAuthSchemeFactory.createAuthScheme(httpBearerAuthScheme)); } diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClientImpl.java b/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClientImpl.java index 30c843425..5a591a2bb 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClientImpl.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClientImpl.java @@ -284,8 +284,8 @@ @SmithyGenerated final class SuperpositionAsyncClientImpl extends Client implements SuperpositionAsyncClient { private static final TypeRegistry TYPE_REGISTRY = TypeRegistry.builder() - .putType(ValidationException.$ID, ValidationException.class, ValidationException::builder) .putType(NotAuthorizedException.$ID, NotAuthorizedException.class, NotAuthorizedException::builder) + .putType(ValidationException.$ID, ValidationException.class, ValidationException::builder) .putType(AccessDeniedException.$ID, AccessDeniedException.class, AccessDeniedException::builder) .putType(InternalFailureException.$ID, InternalFailureException.class, InternalFailureException::builder) .putType(UnknownOperationException.$ID, UnknownOperationException.class, UnknownOperationException::builder) diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClient.java b/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClient.java index 5a9b1f8fc..cf8d20434 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClient.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClient.java @@ -1940,12 +1940,12 @@ final class Builder extends Client.Builder { Node.objectNode() ); - private static final HttpBasicAuthTrait httpBasicAuthScheme = new HttpBasicAuthTrait(); - private static final AuthSchemeFactory httpBasicAuthSchemeFactory = new HttpBasicAuthAuthScheme.Factory(); - private static final HttpBearerAuthTrait httpBearerAuthScheme = new HttpBearerAuthTrait(); private static final AuthSchemeFactory httpBearerAuthSchemeFactory = new HttpBearerAuthScheme.Factory(); + private static final HttpBasicAuthTrait httpBasicAuthScheme = new HttpBasicAuthTrait(); + private static final AuthSchemeFactory httpBasicAuthSchemeFactory = new HttpBasicAuthAuthScheme.Factory(); + private Builder() { configBuilder().putSupportedAuthSchemes(httpBasicAuthSchemeFactory.createAuthScheme(httpBasicAuthScheme), httpBearerAuthSchemeFactory.createAuthScheme(httpBearerAuthScheme)); } diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClientImpl.java b/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClientImpl.java index 137c5f2d3..2129ecfcc 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClientImpl.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClientImpl.java @@ -284,8 +284,8 @@ @SmithyGenerated final class SuperpositionClientImpl extends Client implements SuperpositionClient { private static final TypeRegistry TYPE_REGISTRY = TypeRegistry.builder() - .putType(ValidationException.$ID, ValidationException.class, ValidationException::builder) .putType(NotAuthorizedException.$ID, NotAuthorizedException.class, NotAuthorizedException::builder) + .putType(ValidationException.$ID, ValidationException.class, ValidationException::builder) .putType(AccessDeniedException.$ID, AccessDeniedException.class, AccessDeniedException::builder) .putType(InternalFailureException.$ID, InternalFailureException.class, InternalFailureException::builder) .putType(UnknownOperationException.$ID, UnknownOperationException.class, UnknownOperationException::builder) diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigJsonInput.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigJsonInput.java index a72a68daa..952e8a74b 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigJsonInput.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigJsonInput.java @@ -28,16 +28,12 @@ public final class ImportConfigJsonInput implements SerializableStruct { .putMember("org_id", PreludeSchemas.STRING, new HttpHeaderTrait("x-org-id"), new RequiredTrait()) - .putMember("mode", ImportMode.$SCHEMA, - new HttpHeaderTrait("x-import-mode")) - .putMember("overwrite", PreludeSchemas.BOOLEAN, - new HttpHeaderTrait("x-import-overwrite")) + .putMember("strategy", ImportStrategy.$SCHEMA, + new HttpHeaderTrait("x-import-strategy")) .putMember("on_error", ImportOnError.$SCHEMA, new HttpHeaderTrait("x-import-on-error")) .putMember("dry_run", PreludeSchemas.BOOLEAN, new HttpHeaderTrait("x-import-dry-run")) - .putMember("value_merge", PreludeSchemas.BOOLEAN, - new HttpHeaderTrait("x-import-value-merge")) .putMember("config_tags", PreludeSchemas.STRING, new HttpHeaderTrait("x-config-tags")) .putMember("json_config", PreludeSchemas.STRING, @@ -47,32 +43,26 @@ public final class ImportConfigJsonInput implements SerializableStruct { private static final Schema $SCHEMA_WORKSPACE_ID = $SCHEMA.member("workspace_id"); private static final Schema $SCHEMA_ORG_ID = $SCHEMA.member("org_id"); - private static final Schema $SCHEMA_MODE = $SCHEMA.member("mode"); - private static final Schema $SCHEMA_OVERWRITE = $SCHEMA.member("overwrite"); + private static final Schema $SCHEMA_STRATEGY = $SCHEMA.member("strategy"); private static final Schema $SCHEMA_ON_ERROR = $SCHEMA.member("on_error"); private static final Schema $SCHEMA_DRY_RUN = $SCHEMA.member("dry_run"); - private static final Schema $SCHEMA_VALUE_MERGE = $SCHEMA.member("value_merge"); private static final Schema $SCHEMA_CONFIG_TAGS = $SCHEMA.member("config_tags"); private static final Schema $SCHEMA_JSON_CONFIG = $SCHEMA.member("json_config"); private final transient String workspaceId; private final transient String orgId; - private final transient ImportMode mode; - private final transient Boolean overwrite; + private final transient ImportStrategy strategy; private final transient ImportOnError onError; private final transient Boolean dryRun; - private final transient Boolean valueMerge; private final transient String configTags; private final transient String jsonConfig; private ImportConfigJsonInput(Builder builder) { this.workspaceId = builder.workspaceId; this.orgId = builder.orgId; - this.mode = builder.mode; - this.overwrite = builder.overwrite; + this.strategy = builder.strategy; this.onError = builder.onError; this.dryRun = builder.dryRun; - this.valueMerge = builder.valueMerge; this.configTags = builder.configTags; this.jsonConfig = builder.jsonConfig; } @@ -86,17 +76,10 @@ public String orgId() { } /** - * Whether to merge (default) or replace existing workspace config. + * How the import applies file entities to the workspace. Defaults to upsert. */ - public ImportMode mode() { - return mode; - } - - /** - * When false, entities that already exist are skipped instead of updated. Defaults to true. - */ - public Boolean overwrite() { - return overwrite; + public ImportStrategy strategy() { + return strategy; } /** @@ -113,13 +96,6 @@ public Boolean dryRun() { return dryRun; } - /** - * When true, deep-merges object-valued default-configs with the existing value. Defaults to false. - */ - public Boolean valueMerge() { - return valueMerge; - } - public String configTags() { return configTags; } @@ -144,18 +120,16 @@ public boolean equals(Object other) { ImportConfigJsonInput that = (ImportConfigJsonInput) other; return Objects.equals(this.workspaceId, that.workspaceId) && Objects.equals(this.orgId, that.orgId) - && Objects.equals(this.mode, that.mode) - && Objects.equals(this.overwrite, that.overwrite) + && Objects.equals(this.strategy, that.strategy) && Objects.equals(this.onError, that.onError) && Objects.equals(this.dryRun, that.dryRun) - && Objects.equals(this.valueMerge, that.valueMerge) && Objects.equals(this.configTags, that.configTags) && Objects.equals(this.jsonConfig, that.jsonConfig); } @Override public int hashCode() { - return Objects.hash(workspaceId, orgId, mode, overwrite, onError, dryRun, valueMerge, configTags, jsonConfig); + return Objects.hash(workspaceId, orgId, strategy, onError, dryRun, configTags, jsonConfig); } @Override @@ -167,11 +141,8 @@ public Schema schema() { public void serializeMembers(ShapeSerializer serializer) { serializer.writeString($SCHEMA_WORKSPACE_ID, workspaceId); serializer.writeString($SCHEMA_ORG_ID, orgId); - if (mode != null) { - serializer.writeString($SCHEMA_MODE, mode.value()); - } - if (overwrite != null) { - serializer.writeBoolean($SCHEMA_OVERWRITE, overwrite); + if (strategy != null) { + serializer.writeString($SCHEMA_STRATEGY, strategy.value()); } if (onError != null) { serializer.writeString($SCHEMA_ON_ERROR, onError.value()); @@ -179,9 +150,6 @@ public void serializeMembers(ShapeSerializer serializer) { if (dryRun != null) { serializer.writeBoolean($SCHEMA_DRY_RUN, dryRun); } - if (valueMerge != null) { - serializer.writeBoolean($SCHEMA_VALUE_MERGE, valueMerge); - } if (configTags != null) { serializer.writeString($SCHEMA_CONFIG_TAGS, configTags); } @@ -195,12 +163,10 @@ public T getMemberValue(Schema member) { case 0 -> (T) SchemaUtils.validateSameMember($SCHEMA_WORKSPACE_ID, member, workspaceId); case 1 -> (T) SchemaUtils.validateSameMember($SCHEMA_ORG_ID, member, orgId); case 2 -> (T) SchemaUtils.validateSameMember($SCHEMA_JSON_CONFIG, member, jsonConfig); - case 3 -> (T) SchemaUtils.validateSameMember($SCHEMA_MODE, member, mode); - case 4 -> (T) SchemaUtils.validateSameMember($SCHEMA_OVERWRITE, member, overwrite); - case 5 -> (T) SchemaUtils.validateSameMember($SCHEMA_ON_ERROR, member, onError); - case 6 -> (T) SchemaUtils.validateSameMember($SCHEMA_DRY_RUN, member, dryRun); - case 7 -> (T) SchemaUtils.validateSameMember($SCHEMA_VALUE_MERGE, member, valueMerge); - case 8 -> (T) SchemaUtils.validateSameMember($SCHEMA_CONFIG_TAGS, member, configTags); + case 3 -> (T) SchemaUtils.validateSameMember($SCHEMA_STRATEGY, member, strategy); + case 4 -> (T) SchemaUtils.validateSameMember($SCHEMA_ON_ERROR, member, onError); + case 5 -> (T) SchemaUtils.validateSameMember($SCHEMA_DRY_RUN, member, dryRun); + case 6 -> (T) SchemaUtils.validateSameMember($SCHEMA_CONFIG_TAGS, member, configTags); default -> throw new IllegalArgumentException("Attempted to get non-existent member: " + member.id()); }; } @@ -216,11 +182,9 @@ public Builder toBuilder() { var builder = new Builder(); builder.workspaceId(this.workspaceId); builder.orgId(this.orgId); - builder.mode(this.mode); - builder.overwrite(this.overwrite); + builder.strategy(this.strategy); builder.onError(this.onError); builder.dryRun(this.dryRun); - builder.valueMerge(this.valueMerge); builder.configTags(this.configTags); builder.jsonConfig(this.jsonConfig); return builder; @@ -240,11 +204,9 @@ public static final class Builder implements ShapeBuilder private final PresenceTracker tracker = PresenceTracker.of($SCHEMA); private String workspaceId; private String orgId; - private ImportMode mode; - private Boolean overwrite; + private ImportStrategy strategy; private ImportOnError onError; private Boolean dryRun; - private Boolean valueMerge; private String configTags; private String jsonConfig; @@ -276,22 +238,12 @@ public Builder orgId(String orgId) { } /** - * Whether to merge (default) or replace existing workspace config. - * - * @return this builder. - */ - public Builder mode(ImportMode mode) { - this.mode = mode; - return this; - } - - /** - * When false, entities that already exist are skipped instead of updated. Defaults to true. + * How the import applies file entities to the workspace. Defaults to upsert. * * @return this builder. */ - public Builder overwrite(boolean overwrite) { - this.overwrite = overwrite; + public Builder strategy(ImportStrategy strategy) { + this.strategy = strategy; return this; } @@ -315,16 +267,6 @@ public Builder dryRun(boolean dryRun) { return this; } - /** - * When true, deep-merges object-valued default-configs with the existing value. Defaults to false. - * - * @return this builder. - */ - public Builder valueMerge(boolean valueMerge) { - this.valueMerge = valueMerge; - return this; - } - /** * @return this builder. */ @@ -356,12 +298,10 @@ public void setMemberValue(Schema member, Object value) { case 0 -> workspaceId((String) SchemaUtils.validateSameMember($SCHEMA_WORKSPACE_ID, member, value)); case 1 -> orgId((String) SchemaUtils.validateSameMember($SCHEMA_ORG_ID, member, value)); case 2 -> jsonConfig((String) SchemaUtils.validateSameMember($SCHEMA_JSON_CONFIG, member, value)); - case 3 -> mode((ImportMode) SchemaUtils.validateSameMember($SCHEMA_MODE, member, value)); - case 4 -> overwrite((boolean) SchemaUtils.validateSameMember($SCHEMA_OVERWRITE, member, value)); - case 5 -> onError((ImportOnError) SchemaUtils.validateSameMember($SCHEMA_ON_ERROR, member, value)); - case 6 -> dryRun((boolean) SchemaUtils.validateSameMember($SCHEMA_DRY_RUN, member, value)); - case 7 -> valueMerge((boolean) SchemaUtils.validateSameMember($SCHEMA_VALUE_MERGE, member, value)); - case 8 -> configTags((String) SchemaUtils.validateSameMember($SCHEMA_CONFIG_TAGS, member, value)); + case 3 -> strategy((ImportStrategy) SchemaUtils.validateSameMember($SCHEMA_STRATEGY, member, value)); + case 4 -> onError((ImportOnError) SchemaUtils.validateSameMember($SCHEMA_ON_ERROR, member, value)); + case 5 -> dryRun((boolean) SchemaUtils.validateSameMember($SCHEMA_DRY_RUN, member, value)); + case 6 -> configTags((String) SchemaUtils.validateSameMember($SCHEMA_CONFIG_TAGS, member, value)); default -> ShapeBuilder.super.setMemberValue(member, value); } } @@ -404,12 +344,10 @@ public void accept(Builder builder, Schema member, ShapeDeserializer de) { case 0 -> builder.workspaceId(de.readString(member)); case 1 -> builder.orgId(de.readString(member)); case 2 -> builder.jsonConfig(de.readString(member)); - case 3 -> builder.mode(ImportMode.builder().deserializeMember(de, member).build()); - case 4 -> builder.overwrite(de.readBoolean(member)); - case 5 -> builder.onError(ImportOnError.builder().deserializeMember(de, member).build()); - case 6 -> builder.dryRun(de.readBoolean(member)); - case 7 -> builder.valueMerge(de.readBoolean(member)); - case 8 -> builder.configTags(de.readString(member)); + case 3 -> builder.strategy(ImportStrategy.builder().deserializeMember(de, member).build()); + case 4 -> builder.onError(ImportOnError.builder().deserializeMember(de, member).build()); + case 5 -> builder.dryRun(de.readBoolean(member)); + case 6 -> builder.configTags(de.readString(member)); default -> throw new IllegalArgumentException("Unexpected member: " + member.memberName()); } } diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigJsonOutput.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigJsonOutput.java index c2bf372b4..5bcdab6ea 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigJsonOutput.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigJsonOutput.java @@ -23,7 +23,7 @@ public final class ImportConfigJsonOutput implements SerializableStruct { public static final ShapeId $ID = ShapeId.from("io.superposition#ImportConfigOutput"); public static final Schema $SCHEMA = Schema.structureBuilder($ID) - .putMember("mode", PreludeSchemas.STRING, + .putMember("strategy", PreludeSchemas.STRING, new RequiredTrait()) .putMember("dry_run", PreludeSchemas.BOOLEAN, new RequiredTrait()) @@ -36,14 +36,14 @@ public final class ImportConfigJsonOutput implements SerializableStruct { new RequiredTrait()) .build(); - private static final Schema $SCHEMA_MODE = $SCHEMA.member("mode"); + private static final Schema $SCHEMA_STRATEGY = $SCHEMA.member("strategy"); private static final Schema $SCHEMA_DRY_RUN = $SCHEMA.member("dry_run"); private static final Schema $SCHEMA_CONFIG_VERSION = $SCHEMA.member("config_version"); private static final Schema $SCHEMA_DIMENSIONS = $SCHEMA.member("dimensions"); private static final Schema $SCHEMA_DEFAULT_CONFIGS = $SCHEMA.member("default_configs"); private static final Schema $SCHEMA_CONTEXTS = $SCHEMA.member("contexts"); - private final transient String mode; + private final transient String strategy; private final transient boolean dryRun; private final transient String configVersion; private final transient ImportEntityReport dimensions; @@ -51,7 +51,7 @@ public final class ImportConfigJsonOutput implements SerializableStruct { private final transient ImportEntityReport contexts; private ImportConfigJsonOutput(Builder builder) { - this.mode = builder.mode; + this.strategy = builder.strategy; this.dryRun = builder.dryRun; this.configVersion = builder.configVersion; this.dimensions = builder.dimensions; @@ -59,8 +59,8 @@ private ImportConfigJsonOutput(Builder builder) { this.contexts = builder.contexts; } - public String mode() { - return mode; + public String strategy() { + return strategy; } public boolean dryRun() { @@ -97,7 +97,7 @@ public boolean equals(Object other) { return false; } ImportConfigJsonOutput that = (ImportConfigJsonOutput) other; - return Objects.equals(this.mode, that.mode) + return Objects.equals(this.strategy, that.strategy) && this.dryRun == that.dryRun && Objects.equals(this.configVersion, that.configVersion) && Objects.equals(this.dimensions, that.dimensions) @@ -107,7 +107,7 @@ public boolean equals(Object other) { @Override public int hashCode() { - return Objects.hash(mode, dryRun, configVersion, dimensions, defaultConfigs, contexts); + return Objects.hash(strategy, dryRun, configVersion, dimensions, defaultConfigs, contexts); } @Override @@ -117,7 +117,7 @@ public Schema schema() { @Override public void serializeMembers(ShapeSerializer serializer) { - serializer.writeString($SCHEMA_MODE, mode); + serializer.writeString($SCHEMA_STRATEGY, strategy); serializer.writeBoolean($SCHEMA_DRY_RUN, dryRun); if (configVersion != null) { serializer.writeString($SCHEMA_CONFIG_VERSION, configVersion); @@ -137,7 +137,7 @@ public void serializeMembers(ShapeSerializer serializer) { @SuppressWarnings("unchecked") public T getMemberValue(Schema member) { return switch (member.memberIndex()) { - case 0 -> (T) SchemaUtils.validateSameMember($SCHEMA_MODE, member, mode); + case 0 -> (T) SchemaUtils.validateSameMember($SCHEMA_STRATEGY, member, strategy); case 1 -> (T) SchemaUtils.validateSameMember($SCHEMA_DRY_RUN, member, dryRun); case 2 -> (T) SchemaUtils.validateSameMember($SCHEMA_DIMENSIONS, member, dimensions); case 3 -> (T) SchemaUtils.validateSameMember($SCHEMA_DEFAULT_CONFIGS, member, defaultConfigs); @@ -156,7 +156,7 @@ public T getMemberValue(Schema member) { */ public Builder toBuilder() { var builder = new Builder(); - builder.mode(this.mode); + builder.strategy(this.strategy); builder.dryRun(this.dryRun); builder.configVersion(this.configVersion); builder.dimensions(this.dimensions); @@ -177,7 +177,7 @@ public static Builder builder() { */ public static final class Builder implements ShapeBuilder { private final PresenceTracker tracker = PresenceTracker.of($SCHEMA); - private String mode; + private String strategy; private boolean dryRun; private String configVersion; private ImportEntityReport dimensions; @@ -195,9 +195,9 @@ public Schema schema() { *

Required * @return this builder. */ - public Builder mode(String mode) { - this.mode = Objects.requireNonNull(mode, "mode cannot be null"); - tracker.setMember($SCHEMA_MODE); + public Builder strategy(String strategy) { + this.strategy = Objects.requireNonNull(strategy, "strategy cannot be null"); + tracker.setMember($SCHEMA_STRATEGY); return this; } @@ -259,7 +259,7 @@ public ImportConfigJsonOutput build() { @SuppressWarnings("unchecked") public void setMemberValue(Schema member, Object value) { switch (member.memberIndex()) { - case 0 -> mode((String) SchemaUtils.validateSameMember($SCHEMA_MODE, member, value)); + case 0 -> strategy((String) SchemaUtils.validateSameMember($SCHEMA_STRATEGY, member, value)); case 1 -> dryRun((boolean) SchemaUtils.validateSameMember($SCHEMA_DRY_RUN, member, value)); case 2 -> dimensions((ImportEntityReport) SchemaUtils.validateSameMember($SCHEMA_DIMENSIONS, member, value)); case 3 -> defaultConfigs((ImportEntityReport) SchemaUtils.validateSameMember($SCHEMA_DEFAULT_CONFIGS, member, value)); @@ -274,8 +274,8 @@ public ShapeBuilder errorCorrection() { if (tracker.allSet()) { return this; } - if (!tracker.checkMember($SCHEMA_MODE)) { - mode(""); + if (!tracker.checkMember($SCHEMA_STRATEGY)) { + strategy(""); } if (!tracker.checkMember($SCHEMA_DRY_RUN)) { tracker.setMember($SCHEMA_DRY_RUN); @@ -310,7 +310,7 @@ private static final class $InnerDeserializer implements ShapeDeserializer.Struc @Override public void accept(Builder builder, Schema member, ShapeDeserializer de) { switch (member.memberIndex()) { - case 0 -> builder.mode(de.readString(member)); + case 0 -> builder.strategy(de.readString(member)); case 1 -> builder.dryRun(de.readBoolean(member)); case 2 -> builder.dimensions(ImportEntityReport.builder().deserializeMember(de, member).build()); case 3 -> builder.defaultConfigs(ImportEntityReport.builder().deserializeMember(de, member).build()); diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigTomlInput.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigTomlInput.java index a7bbd561d..8c132d17f 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigTomlInput.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigTomlInput.java @@ -28,16 +28,12 @@ public final class ImportConfigTomlInput implements SerializableStruct { .putMember("org_id", PreludeSchemas.STRING, new HttpHeaderTrait("x-org-id"), new RequiredTrait()) - .putMember("mode", ImportMode.$SCHEMA, - new HttpHeaderTrait("x-import-mode")) - .putMember("overwrite", PreludeSchemas.BOOLEAN, - new HttpHeaderTrait("x-import-overwrite")) + .putMember("strategy", ImportStrategy.$SCHEMA, + new HttpHeaderTrait("x-import-strategy")) .putMember("on_error", ImportOnError.$SCHEMA, new HttpHeaderTrait("x-import-on-error")) .putMember("dry_run", PreludeSchemas.BOOLEAN, new HttpHeaderTrait("x-import-dry-run")) - .putMember("value_merge", PreludeSchemas.BOOLEAN, - new HttpHeaderTrait("x-import-value-merge")) .putMember("config_tags", PreludeSchemas.STRING, new HttpHeaderTrait("x-config-tags")) .putMember("toml_config", PreludeSchemas.STRING, @@ -47,32 +43,26 @@ public final class ImportConfigTomlInput implements SerializableStruct { private static final Schema $SCHEMA_WORKSPACE_ID = $SCHEMA.member("workspace_id"); private static final Schema $SCHEMA_ORG_ID = $SCHEMA.member("org_id"); - private static final Schema $SCHEMA_MODE = $SCHEMA.member("mode"); - private static final Schema $SCHEMA_OVERWRITE = $SCHEMA.member("overwrite"); + private static final Schema $SCHEMA_STRATEGY = $SCHEMA.member("strategy"); private static final Schema $SCHEMA_ON_ERROR = $SCHEMA.member("on_error"); private static final Schema $SCHEMA_DRY_RUN = $SCHEMA.member("dry_run"); - private static final Schema $SCHEMA_VALUE_MERGE = $SCHEMA.member("value_merge"); private static final Schema $SCHEMA_CONFIG_TAGS = $SCHEMA.member("config_tags"); private static final Schema $SCHEMA_TOML_CONFIG = $SCHEMA.member("toml_config"); private final transient String workspaceId; private final transient String orgId; - private final transient ImportMode mode; - private final transient Boolean overwrite; + private final transient ImportStrategy strategy; private final transient ImportOnError onError; private final transient Boolean dryRun; - private final transient Boolean valueMerge; private final transient String configTags; private final transient String tomlConfig; private ImportConfigTomlInput(Builder builder) { this.workspaceId = builder.workspaceId; this.orgId = builder.orgId; - this.mode = builder.mode; - this.overwrite = builder.overwrite; + this.strategy = builder.strategy; this.onError = builder.onError; this.dryRun = builder.dryRun; - this.valueMerge = builder.valueMerge; this.configTags = builder.configTags; this.tomlConfig = builder.tomlConfig; } @@ -86,17 +76,10 @@ public String orgId() { } /** - * Whether to merge (default) or replace existing workspace config. + * How the import applies file entities to the workspace. Defaults to upsert. */ - public ImportMode mode() { - return mode; - } - - /** - * When false, entities that already exist are skipped instead of updated. Defaults to true. - */ - public Boolean overwrite() { - return overwrite; + public ImportStrategy strategy() { + return strategy; } /** @@ -113,13 +96,6 @@ public Boolean dryRun() { return dryRun; } - /** - * When true, deep-merges object-valued default-configs with the existing value. Defaults to false. - */ - public Boolean valueMerge() { - return valueMerge; - } - public String configTags() { return configTags; } @@ -144,18 +120,16 @@ public boolean equals(Object other) { ImportConfigTomlInput that = (ImportConfigTomlInput) other; return Objects.equals(this.workspaceId, that.workspaceId) && Objects.equals(this.orgId, that.orgId) - && Objects.equals(this.mode, that.mode) - && Objects.equals(this.overwrite, that.overwrite) + && Objects.equals(this.strategy, that.strategy) && Objects.equals(this.onError, that.onError) && Objects.equals(this.dryRun, that.dryRun) - && Objects.equals(this.valueMerge, that.valueMerge) && Objects.equals(this.configTags, that.configTags) && Objects.equals(this.tomlConfig, that.tomlConfig); } @Override public int hashCode() { - return Objects.hash(workspaceId, orgId, mode, overwrite, onError, dryRun, valueMerge, configTags, tomlConfig); + return Objects.hash(workspaceId, orgId, strategy, onError, dryRun, configTags, tomlConfig); } @Override @@ -167,11 +141,8 @@ public Schema schema() { public void serializeMembers(ShapeSerializer serializer) { serializer.writeString($SCHEMA_WORKSPACE_ID, workspaceId); serializer.writeString($SCHEMA_ORG_ID, orgId); - if (mode != null) { - serializer.writeString($SCHEMA_MODE, mode.value()); - } - if (overwrite != null) { - serializer.writeBoolean($SCHEMA_OVERWRITE, overwrite); + if (strategy != null) { + serializer.writeString($SCHEMA_STRATEGY, strategy.value()); } if (onError != null) { serializer.writeString($SCHEMA_ON_ERROR, onError.value()); @@ -179,9 +150,6 @@ public void serializeMembers(ShapeSerializer serializer) { if (dryRun != null) { serializer.writeBoolean($SCHEMA_DRY_RUN, dryRun); } - if (valueMerge != null) { - serializer.writeBoolean($SCHEMA_VALUE_MERGE, valueMerge); - } if (configTags != null) { serializer.writeString($SCHEMA_CONFIG_TAGS, configTags); } @@ -195,12 +163,10 @@ public T getMemberValue(Schema member) { case 0 -> (T) SchemaUtils.validateSameMember($SCHEMA_WORKSPACE_ID, member, workspaceId); case 1 -> (T) SchemaUtils.validateSameMember($SCHEMA_ORG_ID, member, orgId); case 2 -> (T) SchemaUtils.validateSameMember($SCHEMA_TOML_CONFIG, member, tomlConfig); - case 3 -> (T) SchemaUtils.validateSameMember($SCHEMA_MODE, member, mode); - case 4 -> (T) SchemaUtils.validateSameMember($SCHEMA_OVERWRITE, member, overwrite); - case 5 -> (T) SchemaUtils.validateSameMember($SCHEMA_ON_ERROR, member, onError); - case 6 -> (T) SchemaUtils.validateSameMember($SCHEMA_DRY_RUN, member, dryRun); - case 7 -> (T) SchemaUtils.validateSameMember($SCHEMA_VALUE_MERGE, member, valueMerge); - case 8 -> (T) SchemaUtils.validateSameMember($SCHEMA_CONFIG_TAGS, member, configTags); + case 3 -> (T) SchemaUtils.validateSameMember($SCHEMA_STRATEGY, member, strategy); + case 4 -> (T) SchemaUtils.validateSameMember($SCHEMA_ON_ERROR, member, onError); + case 5 -> (T) SchemaUtils.validateSameMember($SCHEMA_DRY_RUN, member, dryRun); + case 6 -> (T) SchemaUtils.validateSameMember($SCHEMA_CONFIG_TAGS, member, configTags); default -> throw new IllegalArgumentException("Attempted to get non-existent member: " + member.id()); }; } @@ -216,11 +182,9 @@ public Builder toBuilder() { var builder = new Builder(); builder.workspaceId(this.workspaceId); builder.orgId(this.orgId); - builder.mode(this.mode); - builder.overwrite(this.overwrite); + builder.strategy(this.strategy); builder.onError(this.onError); builder.dryRun(this.dryRun); - builder.valueMerge(this.valueMerge); builder.configTags(this.configTags); builder.tomlConfig(this.tomlConfig); return builder; @@ -240,11 +204,9 @@ public static final class Builder implements ShapeBuilder private final PresenceTracker tracker = PresenceTracker.of($SCHEMA); private String workspaceId; private String orgId; - private ImportMode mode; - private Boolean overwrite; + private ImportStrategy strategy; private ImportOnError onError; private Boolean dryRun; - private Boolean valueMerge; private String configTags; private String tomlConfig; @@ -276,22 +238,12 @@ public Builder orgId(String orgId) { } /** - * Whether to merge (default) or replace existing workspace config. - * - * @return this builder. - */ - public Builder mode(ImportMode mode) { - this.mode = mode; - return this; - } - - /** - * When false, entities that already exist are skipped instead of updated. Defaults to true. + * How the import applies file entities to the workspace. Defaults to upsert. * * @return this builder. */ - public Builder overwrite(boolean overwrite) { - this.overwrite = overwrite; + public Builder strategy(ImportStrategy strategy) { + this.strategy = strategy; return this; } @@ -315,16 +267,6 @@ public Builder dryRun(boolean dryRun) { return this; } - /** - * When true, deep-merges object-valued default-configs with the existing value. Defaults to false. - * - * @return this builder. - */ - public Builder valueMerge(boolean valueMerge) { - this.valueMerge = valueMerge; - return this; - } - /** * @return this builder. */ @@ -356,12 +298,10 @@ public void setMemberValue(Schema member, Object value) { case 0 -> workspaceId((String) SchemaUtils.validateSameMember($SCHEMA_WORKSPACE_ID, member, value)); case 1 -> orgId((String) SchemaUtils.validateSameMember($SCHEMA_ORG_ID, member, value)); case 2 -> tomlConfig((String) SchemaUtils.validateSameMember($SCHEMA_TOML_CONFIG, member, value)); - case 3 -> mode((ImportMode) SchemaUtils.validateSameMember($SCHEMA_MODE, member, value)); - case 4 -> overwrite((boolean) SchemaUtils.validateSameMember($SCHEMA_OVERWRITE, member, value)); - case 5 -> onError((ImportOnError) SchemaUtils.validateSameMember($SCHEMA_ON_ERROR, member, value)); - case 6 -> dryRun((boolean) SchemaUtils.validateSameMember($SCHEMA_DRY_RUN, member, value)); - case 7 -> valueMerge((boolean) SchemaUtils.validateSameMember($SCHEMA_VALUE_MERGE, member, value)); - case 8 -> configTags((String) SchemaUtils.validateSameMember($SCHEMA_CONFIG_TAGS, member, value)); + case 3 -> strategy((ImportStrategy) SchemaUtils.validateSameMember($SCHEMA_STRATEGY, member, value)); + case 4 -> onError((ImportOnError) SchemaUtils.validateSameMember($SCHEMA_ON_ERROR, member, value)); + case 5 -> dryRun((boolean) SchemaUtils.validateSameMember($SCHEMA_DRY_RUN, member, value)); + case 6 -> configTags((String) SchemaUtils.validateSameMember($SCHEMA_CONFIG_TAGS, member, value)); default -> ShapeBuilder.super.setMemberValue(member, value); } } @@ -404,12 +344,10 @@ public void accept(Builder builder, Schema member, ShapeDeserializer de) { case 0 -> builder.workspaceId(de.readString(member)); case 1 -> builder.orgId(de.readString(member)); case 2 -> builder.tomlConfig(de.readString(member)); - case 3 -> builder.mode(ImportMode.builder().deserializeMember(de, member).build()); - case 4 -> builder.overwrite(de.readBoolean(member)); - case 5 -> builder.onError(ImportOnError.builder().deserializeMember(de, member).build()); - case 6 -> builder.dryRun(de.readBoolean(member)); - case 7 -> builder.valueMerge(de.readBoolean(member)); - case 8 -> builder.configTags(de.readString(member)); + case 3 -> builder.strategy(ImportStrategy.builder().deserializeMember(de, member).build()); + case 4 -> builder.onError(ImportOnError.builder().deserializeMember(de, member).build()); + case 5 -> builder.dryRun(de.readBoolean(member)); + case 6 -> builder.configTags(de.readString(member)); default -> throw new IllegalArgumentException("Unexpected member: " + member.memberName()); } } diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigTomlOutput.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigTomlOutput.java index 7eb29101b..ced56c036 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigTomlOutput.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportConfigTomlOutput.java @@ -23,7 +23,7 @@ public final class ImportConfigTomlOutput implements SerializableStruct { public static final ShapeId $ID = ShapeId.from("io.superposition#ImportConfigOutput"); public static final Schema $SCHEMA = Schema.structureBuilder($ID) - .putMember("mode", PreludeSchemas.STRING, + .putMember("strategy", PreludeSchemas.STRING, new RequiredTrait()) .putMember("dry_run", PreludeSchemas.BOOLEAN, new RequiredTrait()) @@ -36,14 +36,14 @@ public final class ImportConfigTomlOutput implements SerializableStruct { new RequiredTrait()) .build(); - private static final Schema $SCHEMA_MODE = $SCHEMA.member("mode"); + private static final Schema $SCHEMA_STRATEGY = $SCHEMA.member("strategy"); private static final Schema $SCHEMA_DRY_RUN = $SCHEMA.member("dry_run"); private static final Schema $SCHEMA_CONFIG_VERSION = $SCHEMA.member("config_version"); private static final Schema $SCHEMA_DIMENSIONS = $SCHEMA.member("dimensions"); private static final Schema $SCHEMA_DEFAULT_CONFIGS = $SCHEMA.member("default_configs"); private static final Schema $SCHEMA_CONTEXTS = $SCHEMA.member("contexts"); - private final transient String mode; + private final transient String strategy; private final transient boolean dryRun; private final transient String configVersion; private final transient ImportEntityReport dimensions; @@ -51,7 +51,7 @@ public final class ImportConfigTomlOutput implements SerializableStruct { private final transient ImportEntityReport contexts; private ImportConfigTomlOutput(Builder builder) { - this.mode = builder.mode; + this.strategy = builder.strategy; this.dryRun = builder.dryRun; this.configVersion = builder.configVersion; this.dimensions = builder.dimensions; @@ -59,8 +59,8 @@ private ImportConfigTomlOutput(Builder builder) { this.contexts = builder.contexts; } - public String mode() { - return mode; + public String strategy() { + return strategy; } public boolean dryRun() { @@ -97,7 +97,7 @@ public boolean equals(Object other) { return false; } ImportConfigTomlOutput that = (ImportConfigTomlOutput) other; - return Objects.equals(this.mode, that.mode) + return Objects.equals(this.strategy, that.strategy) && this.dryRun == that.dryRun && Objects.equals(this.configVersion, that.configVersion) && Objects.equals(this.dimensions, that.dimensions) @@ -107,7 +107,7 @@ public boolean equals(Object other) { @Override public int hashCode() { - return Objects.hash(mode, dryRun, configVersion, dimensions, defaultConfigs, contexts); + return Objects.hash(strategy, dryRun, configVersion, dimensions, defaultConfigs, contexts); } @Override @@ -117,7 +117,7 @@ public Schema schema() { @Override public void serializeMembers(ShapeSerializer serializer) { - serializer.writeString($SCHEMA_MODE, mode); + serializer.writeString($SCHEMA_STRATEGY, strategy); serializer.writeBoolean($SCHEMA_DRY_RUN, dryRun); if (configVersion != null) { serializer.writeString($SCHEMA_CONFIG_VERSION, configVersion); @@ -137,7 +137,7 @@ public void serializeMembers(ShapeSerializer serializer) { @SuppressWarnings("unchecked") public T getMemberValue(Schema member) { return switch (member.memberIndex()) { - case 0 -> (T) SchemaUtils.validateSameMember($SCHEMA_MODE, member, mode); + case 0 -> (T) SchemaUtils.validateSameMember($SCHEMA_STRATEGY, member, strategy); case 1 -> (T) SchemaUtils.validateSameMember($SCHEMA_DRY_RUN, member, dryRun); case 2 -> (T) SchemaUtils.validateSameMember($SCHEMA_DIMENSIONS, member, dimensions); case 3 -> (T) SchemaUtils.validateSameMember($SCHEMA_DEFAULT_CONFIGS, member, defaultConfigs); @@ -156,7 +156,7 @@ public T getMemberValue(Schema member) { */ public Builder toBuilder() { var builder = new Builder(); - builder.mode(this.mode); + builder.strategy(this.strategy); builder.dryRun(this.dryRun); builder.configVersion(this.configVersion); builder.dimensions(this.dimensions); @@ -177,7 +177,7 @@ public static Builder builder() { */ public static final class Builder implements ShapeBuilder { private final PresenceTracker tracker = PresenceTracker.of($SCHEMA); - private String mode; + private String strategy; private boolean dryRun; private String configVersion; private ImportEntityReport dimensions; @@ -195,9 +195,9 @@ public Schema schema() { *

Required * @return this builder. */ - public Builder mode(String mode) { - this.mode = Objects.requireNonNull(mode, "mode cannot be null"); - tracker.setMember($SCHEMA_MODE); + public Builder strategy(String strategy) { + this.strategy = Objects.requireNonNull(strategy, "strategy cannot be null"); + tracker.setMember($SCHEMA_STRATEGY); return this; } @@ -259,7 +259,7 @@ public ImportConfigTomlOutput build() { @SuppressWarnings("unchecked") public void setMemberValue(Schema member, Object value) { switch (member.memberIndex()) { - case 0 -> mode((String) SchemaUtils.validateSameMember($SCHEMA_MODE, member, value)); + case 0 -> strategy((String) SchemaUtils.validateSameMember($SCHEMA_STRATEGY, member, value)); case 1 -> dryRun((boolean) SchemaUtils.validateSameMember($SCHEMA_DRY_RUN, member, value)); case 2 -> dimensions((ImportEntityReport) SchemaUtils.validateSameMember($SCHEMA_DIMENSIONS, member, value)); case 3 -> defaultConfigs((ImportEntityReport) SchemaUtils.validateSameMember($SCHEMA_DEFAULT_CONFIGS, member, value)); @@ -274,8 +274,8 @@ public ShapeBuilder errorCorrection() { if (tracker.allSet()) { return this; } - if (!tracker.checkMember($SCHEMA_MODE)) { - mode(""); + if (!tracker.checkMember($SCHEMA_STRATEGY)) { + strategy(""); } if (!tracker.checkMember($SCHEMA_DRY_RUN)) { tracker.setMember($SCHEMA_DRY_RUN); @@ -310,7 +310,7 @@ private static final class $InnerDeserializer implements ShapeDeserializer.Struc @Override public void accept(Builder builder, Schema member, ShapeDeserializer de) { switch (member.memberIndex()) { - case 0 -> builder.mode(de.readString(member)); + case 0 -> builder.strategy(de.readString(member)); case 1 -> builder.dryRun(de.readBoolean(member)); case 2 -> builder.dimensions(ImportEntityReport.builder().deserializeMember(de, member).build()); case 3 -> builder.defaultConfigs(ImportEntityReport.builder().deserializeMember(de, member).build()); diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportMode.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportStrategy.java similarity index 62% rename from clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportMode.java rename to clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportStrategy.java index 6f541575f..c3fea9211 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportMode.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ImportStrategy.java @@ -14,40 +14,47 @@ import software.amazon.smithy.utils.SmithyGenerated; /** - * How an import treats workspace entities that are not present in the imported file. + * How an import applies file entities to the workspace. */ @SmithyGenerated -public final class ImportMode implements SerializableShape { - public static final ShapeId $ID = ShapeId.from("io.superposition#ImportMode"); +public final class ImportStrategy implements SerializableShape { + public static final ShapeId $ID = ShapeId.from("io.superposition#ImportStrategy"); /** - * Upsert the entities in the file and leave everything else untouched. + * Create entities that are present in the file but missing from the workspace. Existing entities are + * skipped. Nothing is deleted. */ - public static final ImportMode MERGE = new ImportMode(Type.MERGE, "merge"); + public static final ImportStrategy CREATE_ONLY = new ImportStrategy(Type.CREATE_ONLY, "create_only"); /** - * Mirror the file: additionally delete dimensions, default-configs and contexts that are absent from - * it. + * Create missing entities and update existing entities from the file. Entities absent from the file + * are left untouched. */ - public static final ImportMode REPLACE = new ImportMode(Type.REPLACE, "replace"); - private static final List $TYPES = List.of(MERGE, REPLACE); + public static final ImportStrategy UPSERT = new ImportStrategy(Type.UPSERT, "upsert"); + /** + * Mirror the file: create missing entities, update existing entities, and delete dimensions, + * default-configs and contexts that are absent from it. + */ + public static final ImportStrategy REPLACE = new ImportStrategy(Type.REPLACE, "replace"); + private static final List $TYPES = List.of(CREATE_ONLY, UPSERT, REPLACE); public static final Schema $SCHEMA = Schema.createEnum($ID, - Set.of(MERGE.value, REPLACE.value) + Set.of(CREATE_ONLY.value, UPSERT.value, REPLACE.value) ); private final String value; private final Type type; - private ImportMode(Type type, String value) { + private ImportStrategy(Type type, String value) { this.type = Objects.requireNonNull(type, "type cannot be null"); this.value = Objects.requireNonNull(value, "value cannot be null"); } /** - * Enum representing the possible variants of {@link ImportMode}. + * Enum representing the possible variants of {@link ImportStrategy}. */ public enum Type { $UNKNOWN, - MERGE, + CREATE_ONLY, + UPSERT, REPLACE } @@ -70,14 +77,14 @@ public Type type() { * * @param value value contained by unknown Enum. */ - public static ImportMode unknown(String value) { - return new ImportMode(Type.$UNKNOWN, value); + public static ImportStrategy unknown(String value) { + return new ImportStrategy(Type.$UNKNOWN, value); } /** * Returns an unmodifiable list containing the constants of this enum type, in the order declared. */ - public static List values() { + public static List values() { return $TYPES; } @@ -92,14 +99,15 @@ public String toString() { } /** - * Returns a {@link ImportMode} constant with the specified value. + * Returns a {@link ImportStrategy} constant with the specified value. * - * @param value value to create {@code ImportMode} from. + * @param value value to create {@code ImportStrategy} from. * @throws IllegalArgumentException if value does not match a known value. */ - public static ImportMode from(String value) { + public static ImportStrategy from(String value) { return switch (value) { - case "merge" -> MERGE; + case "create_only" -> CREATE_ONLY; + case "upsert" -> UPSERT; case "replace" -> REPLACE; default -> throw new IllegalArgumentException("Unknown value: " + value); }; @@ -113,7 +121,7 @@ public boolean equals(Object other) { if (other == null || getClass() != other.getClass()) { return false; } - ImportMode that = (ImportMode) other; + ImportStrategy that = (ImportStrategy) other; return this.value.equals(that.value); } @@ -130,9 +138,9 @@ public static Builder builder() { } /** - * Builder for {@link ImportMode}. + * Builder for {@link ImportStrategy}. */ - public static final class Builder implements ShapeBuilder { + public static final class Builder implements ShapeBuilder { private String value; private Builder() {} @@ -148,11 +156,12 @@ private Builder value(String value) { } @Override - public ImportMode build() { + public ImportStrategy build() { return switch (value) { - case "merge" -> MERGE; + case "create_only" -> CREATE_ONLY; + case "upsert" -> UPSERT; case "replace" -> REPLACE; - default -> new ImportMode(Type.$UNKNOWN, value); + default -> new ImportStrategy(Type.$UNKNOWN, value); }; } diff --git a/clients/javascript/sdk/src/commands/ImportConfigJsonCommand.ts b/clients/javascript/sdk/src/commands/ImportConfigJsonCommand.ts index 0e1183176..a9db222e5 100644 --- a/clients/javascript/sdk/src/commands/ImportConfigJsonCommand.ts +++ b/clients/javascript/sdk/src/commands/ImportConfigJsonCommand.ts @@ -45,18 +45,16 @@ export interface ImportConfigJsonCommandOutput extends ImportConfigOutput, __Met * const input = { // ImportConfigJsonInput * workspace_id: "STRING_VALUE", // required * org_id: "STRING_VALUE", // required - * mode: "merge" || "replace", - * overwrite: true || false, + * strategy: "create_only" || "upsert" || "replace", * on_error: "abort" || "continue", * dry_run: true || false, - * value_merge: true || false, * config_tags: "STRING_VALUE", * json_config: "STRING_VALUE", // required * }; * const command = new ImportConfigJsonCommand(input); * const response = await client.send(command); * // { // ImportConfigOutput - * // mode: "STRING_VALUE", // required + * // strategy: "STRING_VALUE", // required * // dry_run: true || false, // required * // config_version: "STRING_VALUE", * // dimensions: { // ImportEntityReport diff --git a/clients/javascript/sdk/src/commands/ImportConfigTomlCommand.ts b/clients/javascript/sdk/src/commands/ImportConfigTomlCommand.ts index fe40e2dda..903a928c8 100644 --- a/clients/javascript/sdk/src/commands/ImportConfigTomlCommand.ts +++ b/clients/javascript/sdk/src/commands/ImportConfigTomlCommand.ts @@ -45,18 +45,16 @@ export interface ImportConfigTomlCommandOutput extends ImportConfigOutput, __Met * const input = { // ImportConfigTomlInput * workspace_id: "STRING_VALUE", // required * org_id: "STRING_VALUE", // required - * mode: "merge" || "replace", - * overwrite: true || false, + * strategy: "create_only" || "upsert" || "replace", * on_error: "abort" || "continue", * dry_run: true || false, - * value_merge: true || false, * config_tags: "STRING_VALUE", * toml_config: "STRING_VALUE", // required * }; * const command = new ImportConfigTomlCommand(input); * const response = await client.send(command); * // { // ImportConfigOutput - * // mode: "STRING_VALUE", // required + * // strategy: "STRING_VALUE", // required * // dry_run: true || false, // required * // config_version: "STRING_VALUE", * // dimensions: { // ImportEntityReport diff --git a/clients/javascript/sdk/src/models/models_0.ts b/clients/javascript/sdk/src/models/models_0.ts index e55d07d98..0e7bb0d87 100644 --- a/clients/javascript/sdk/src/models/models_0.ts +++ b/clients/javascript/sdk/src/models/models_0.ts @@ -2755,39 +2755,43 @@ export interface GetWorkspaceInput { * @public * @enum */ -export const ImportMode = { +export const ImportOnError = { /** - * Upsert the entities in the file and leave everything else untouched. + * Roll the whole import back on the first error. */ - MERGE: "merge", + ABORT: "abort", /** - * Mirror the file: additionally delete dimensions, default-configs and contexts that are absent from it. + * Apply everything that is valid and report per-entity errors. */ - REPLACE: "replace", + CONTINUE: "continue", } as const /** * @public */ -export type ImportMode = typeof ImportMode[keyof typeof ImportMode] +export type ImportOnError = typeof ImportOnError[keyof typeof ImportOnError] /** * @public * @enum */ -export const ImportOnError = { +export const ImportStrategy = { /** - * Roll the whole import back on the first error. + * Create entities that are present in the file but missing from the workspace. Existing entities are skipped. Nothing is deleted. */ - ABORT: "abort", + CREATE_ONLY: "create_only", /** - * Apply everything that is valid and report per-entity errors. + * Mirror the file: create missing entities, update existing entities, and delete dimensions, default-configs and contexts that are absent from it. */ - CONTINUE: "continue", + REPLACE: "replace", + /** + * Create missing entities and update existing entities from the file. Entities absent from the file are left untouched. + */ + UPSERT: "upsert", } as const /** * @public */ -export type ImportOnError = typeof ImportOnError[keyof typeof ImportOnError] +export type ImportStrategy = typeof ImportStrategy[keyof typeof ImportStrategy] /** * @public @@ -2796,16 +2800,10 @@ export interface ImportConfigJsonInput { workspace_id: string | undefined; org_id: string | undefined; /** - * Whether to merge (default) or replace existing workspace config. + * How the import applies file entities to the workspace. Defaults to upsert. * @public */ - mode?: ImportMode | undefined; - - /** - * When false, entities that already exist are skipped instead of updated. Defaults to true. - * @public - */ - overwrite?: boolean | undefined; + strategy?: ImportStrategy | undefined; /** * Whether to abort (default) or continue on per-entity errors. @@ -2819,12 +2817,6 @@ export interface ImportConfigJsonInput { */ dry_run?: boolean | undefined; - /** - * When true, deep-merges object-valued default-configs with the existing value. Defaults to false. - * @public - */ - value_merge?: boolean | undefined; - config_tags?: string | undefined; json_config: string | undefined; } @@ -2854,7 +2846,7 @@ export interface ImportEntityReport { * @public */ export interface ImportConfigOutput { - mode: string | undefined; + strategy: string | undefined; dry_run: boolean | undefined; config_version?: string | undefined; /** @@ -2883,16 +2875,10 @@ export interface ImportConfigTomlInput { workspace_id: string | undefined; org_id: string | undefined; /** - * Whether to merge (default) or replace existing workspace config. + * How the import applies file entities to the workspace. Defaults to upsert. * @public */ - mode?: ImportMode | undefined; - - /** - * When false, entities that already exist are skipped instead of updated. Defaults to true. - * @public - */ - overwrite?: boolean | undefined; + strategy?: ImportStrategy | undefined; /** * Whether to abort (default) or continue on per-entity errors. @@ -2906,12 +2892,6 @@ export interface ImportConfigTomlInput { */ dry_run?: boolean | undefined; - /** - * When true, deep-merges object-valued default-configs with the existing value. Defaults to false. - * @public - */ - value_merge?: boolean | undefined; - config_tags?: string | undefined; toml_config: string | undefined; } diff --git a/clients/javascript/sdk/src/protocols/Aws_restJson1.ts b/clients/javascript/sdk/src/protocols/Aws_restJson1.ts index 2d4a84595..a16962e32 100644 --- a/clients/javascript/sdk/src/protocols/Aws_restJson1.ts +++ b/clients/javascript/sdk/src/protocols/Aws_restJson1.ts @@ -1705,11 +1705,9 @@ export const se_ImportConfigJsonCommand = async( 'content-type': 'text/plain', [_xw]: input[_wi]!, [_xoi]: input[_oi]!, - [_xim]: input[_m]!, - [_xio]: [() => isSerializableHeaderValue(input[_o]), () => input[_o]!.toString()], + [_xis]: input[_s]!, [_xioe]: input[_oe]!, [_xidr]: [() => isSerializableHeaderValue(input[_dr]), () => input[_dr]!.toString()], - [_xivm]: [() => isSerializableHeaderValue(input[_vm]), () => input[_vm]!.toString()], [_xct]: input[_ct]!, }); b.bp("/config/json/import"); @@ -1735,11 +1733,9 @@ export const se_ImportConfigTomlCommand = async( 'content-type': 'text/plain', [_xw]: input[_wi]!, [_xoi]: input[_oi]!, - [_xim]: input[_m]!, - [_xio]: [() => isSerializableHeaderValue(input[_o]), () => input[_o]!.toString()], + [_xis]: input[_s]!, [_xioe]: input[_oe]!, [_xidr]: [() => isSerializableHeaderValue(input[_dr]), () => input[_dr]!.toString()], - [_xivm]: [() => isSerializableHeaderValue(input[_vm]), () => input[_vm]!.toString()], [_xct]: input[_ct]!, }); b.bp("/config/toml/import"); @@ -1893,7 +1889,7 @@ export const se_ListExperimentCommand = async( [_c]: [() => input.count !== void 0, () => (input[_c]!.toString())], [_pa]: [() => input.page !== void 0, () => (input[_pa]!.toString())], [_a]: [() => input.all !== void 0, () => (input[_a]!.toString())], - [_s]: [() => input.status !== void 0, () => ((input[_s]! || []))], + [_st]: [() => input.status !== void 0, () => ((input[_st]! || []))], [_fd]: [() => input.from_date !== void 0, () => (__serializeDateTime(input[_fd]!).toString())], [_td]: [() => input.to_date !== void 0, () => (__serializeDateTime(input[_td]!).toString())], [_en]: [,input[_en]!], @@ -4232,7 +4228,7 @@ export const de_ImportConfigJsonCommand = async( 'default_configs': _json, 'dimensions': _json, 'dry_run': __expectBoolean, - 'mode': __expectString, + 'strategy': __expectString, }); Object.assign(contents, doc); return contents; @@ -4258,7 +4254,7 @@ export const de_ImportConfigTomlCommand = async( 'default_configs': _json, 'dimensions': _json, 'dry_run': __expectBoolean, - 'mode': __expectString, + 'strategy': __expectString, }); Object.assign(contents, doc); return contents; @@ -6615,35 +6611,31 @@ const de_CommandError = async( const _lm = "last-modified"; const _lm_ = "last_modified"; const _lmb = "last_modified_by"; - const _m = "mode"; const _ms = "merge_strategy"; const _n = "name"; - const _o = "overwrite"; const _oe = "on_error"; const _oi = "org_id"; const _p = "prefix"; const _pa = "page"; const _pl = "plaintext"; const _rr = "resolve_remote"; - const _s = "status"; + const _s = "strategy"; const _sb = "sort_by"; const _so = "sort_on"; const _sr = "show_reasoning"; + const _st = "status"; const _t = "tables"; const _ta = "table"; const _td = "to_date"; const _u = "username"; const _v = "version"; - const _vm = "value_merge"; const _wi = "workspace_id"; const _xai = "x-audit-id"; const _xct = "x-config-tags"; const _xcv = "x-config-version"; const _xidr = "x-import-dry-run"; - const _xim = "x-import-mode"; - const _xio = "x-import-overwrite"; const _xioe = "x-import-on-error"; - const _xivm = "x-import-value-merge"; + const _xis = "x-import-strategy"; const _xms = "x-merge-strategy"; const _xoi = "x-org-id"; const _xw = "x-workspace"; diff --git a/clients/python/sdk/superposition_sdk/_private/schemas.py b/clients/python/sdk/superposition_sdk/_private/schemas.py index aeed6680b..3a0759d9a 100644 --- a/clients/python/sdk/superposition_sdk/_private/schemas.py +++ b/clients/python/sdk/superposition_sdk/_private/schemas.py @@ -15308,24 +15308,24 @@ ) -IMPORT_MODE = Schema.collection( - id=ShapeID("io.superposition#ImportMode"), +IMPORT_ON_ERROR = Schema.collection( + id=ShapeID("io.superposition#ImportOnError"), shape_type=ShapeType.ENUM, members={ - "MERGE": { + "ABORT": { "target": UNIT, "index": 0, "traits": [ - Trait.new(id=ShapeID("smithy.api#enumValue"), value="merge"), + Trait.new(id=ShapeID("smithy.api#enumValue"), value="abort"), ], }, - "REPLACE": { + "CONTINUE": { "target": UNIT, "index": 1, "traits": [ - Trait.new(id=ShapeID("smithy.api#enumValue"), value="replace"), + Trait.new(id=ShapeID("smithy.api#enumValue"), value="continue"), ], }, @@ -15333,24 +15333,33 @@ } ) -IMPORT_ON_ERROR = Schema.collection( - id=ShapeID("io.superposition#ImportOnError"), +IMPORT_STRATEGY = Schema.collection( + id=ShapeID("io.superposition#ImportStrategy"), shape_type=ShapeType.ENUM, members={ - "ABORT": { + "CREATE_ONLY": { "target": UNIT, "index": 0, "traits": [ - Trait.new(id=ShapeID("smithy.api#enumValue"), value="abort"), + Trait.new(id=ShapeID("smithy.api#enumValue"), value="create_only"), ], }, - "CONTINUE": { + "UPSERT": { "target": UNIT, "index": 1, "traits": [ - Trait.new(id=ShapeID("smithy.api#enumValue"), value="continue"), + Trait.new(id=ShapeID("smithy.api#enumValue"), value="upsert"), + + ], + }, + + "REPLACE": { + "target": UNIT, + "index": 2, + "traits": [ + Trait.new(id=ShapeID("smithy.api#enumValue"), value="replace"), ], }, @@ -15386,21 +15395,11 @@ ], }, - "mode": { - "target": IMPORT_MODE, + "strategy": { + "target": IMPORT_STRATEGY, "index": 2, "traits": [ - Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-import-mode"), - Trait.new(id=ShapeID("smithy.api#notProperty")), - - ], - }, - - "overwrite": { - "target": BOOLEAN, - "index": 3, - "traits": [ - Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-import-overwrite"), + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-import-strategy"), Trait.new(id=ShapeID("smithy.api#notProperty")), ], @@ -15408,7 +15407,7 @@ "on_error": { "target": IMPORT_ON_ERROR, - "index": 4, + "index": 3, "traits": [ Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-import-on-error"), Trait.new(id=ShapeID("smithy.api#notProperty")), @@ -15418,7 +15417,7 @@ "dry_run": { "target": BOOLEAN, - "index": 5, + "index": 4, "traits": [ Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-import-dry-run"), Trait.new(id=ShapeID("smithy.api#notProperty")), @@ -15426,19 +15425,9 @@ ], }, - "value_merge": { - "target": BOOLEAN, - "index": 6, - "traits": [ - Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-import-value-merge"), - Trait.new(id=ShapeID("smithy.api#notProperty")), - - ], - }, - "config_tags": { "target": STRING, - "index": 7, + "index": 5, "traits": [ Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-config-tags"), Trait.new(id=ShapeID("smithy.api#notProperty")), @@ -15448,7 +15437,7 @@ "json_config": { "target": STRING, - "index": 8, + "index": 6, "traits": [ Trait.new(id=ShapeID("smithy.api#notProperty")), Trait.new(id=ShapeID("smithy.api#required")), @@ -15554,7 +15543,7 @@ ], members={ - "mode": { + "strategy": { "target": STRING, "index": 0, "traits": [ @@ -15660,21 +15649,11 @@ ], }, - "mode": { - "target": IMPORT_MODE, + "strategy": { + "target": IMPORT_STRATEGY, "index": 2, "traits": [ - Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-import-mode"), - Trait.new(id=ShapeID("smithy.api#notProperty")), - - ], - }, - - "overwrite": { - "target": BOOLEAN, - "index": 3, - "traits": [ - Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-import-overwrite"), + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-import-strategy"), Trait.new(id=ShapeID("smithy.api#notProperty")), ], @@ -15682,7 +15661,7 @@ "on_error": { "target": IMPORT_ON_ERROR, - "index": 4, + "index": 3, "traits": [ Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-import-on-error"), Trait.new(id=ShapeID("smithy.api#notProperty")), @@ -15692,7 +15671,7 @@ "dry_run": { "target": BOOLEAN, - "index": 5, + "index": 4, "traits": [ Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-import-dry-run"), Trait.new(id=ShapeID("smithy.api#notProperty")), @@ -15700,19 +15679,9 @@ ], }, - "value_merge": { - "target": BOOLEAN, - "index": 6, - "traits": [ - Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-import-value-merge"), - Trait.new(id=ShapeID("smithy.api#notProperty")), - - ], - }, - "config_tags": { "target": STRING, - "index": 7, + "index": 5, "traits": [ Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-config-tags"), Trait.new(id=ShapeID("smithy.api#notProperty")), @@ -15722,7 +15691,7 @@ "toml_config": { "target": STRING, - "index": 8, + "index": 6, "traits": [ Trait.new(id=ShapeID("smithy.api#notProperty")), Trait.new(id=ShapeID("smithy.api#required")), @@ -15743,7 +15712,7 @@ ], members={ - "mode": { + "strategy": { "target": STRING, "index": 0, "traits": [ diff --git a/clients/python/sdk/superposition_sdk/config.py b/clients/python/sdk/superposition_sdk/config.py index c99032bd7..f2f052ca6 100644 --- a/clients/python/sdk/superposition_sdk/config.py +++ b/clients/python/sdk/superposition_sdk/config.py @@ -197,97 +197,7 @@ ) -_ServiceInterceptor = Union[ - Interceptor[AddMembersToGroupInput, AddMembersToGroupOutput, Any, Any], - Interceptor[ApplicableVariantsInput, ApplicableVariantsOutput, Any, Any], - Interceptor[BulkOperationInput, BulkOperationOutput, Any, Any], - Interceptor[ConcludeExperimentInput, ConcludeExperimentOutput, Any, Any], - Interceptor[CreateContextInput, CreateContextOutput, Any, Any], - Interceptor[CreateDefaultConfigInput, CreateDefaultConfigOutput, Any, Any], - Interceptor[CreateDimensionInput, CreateDimensionOutput, Any, Any], - Interceptor[CreateExperimentInput, CreateExperimentOutput, Any, Any], - Interceptor[CreateExperimentGroupInput, CreateExperimentGroupOutput, Any, Any], - Interceptor[CreateFunctionInput, CreateFunctionOutput, Any, Any], - Interceptor[CreateOrganisationInput, CreateOrganisationOutput, Any, Any], - Interceptor[CreateSecretInput, CreateSecretOutput, Any, Any], - Interceptor[CreateTypeTemplatesInput, CreateTypeTemplatesOutput, Any, Any], - Interceptor[CreateVariableInput, CreateVariableOutput, Any, Any], - Interceptor[CreateWebhookInput, CreateWebhookOutput, Any, Any], - Interceptor[CreateWorkspaceInput, CreateWorkspaceOutput, Any, Any], - Interceptor[DeleteContextInput, DeleteContextOutput, Any, Any], - Interceptor[DeleteDefaultConfigInput, DeleteDefaultConfigOutput, Any, Any], - Interceptor[DeleteDimensionInput, DeleteDimensionOutput, Any, Any], - Interceptor[DeleteExperimentGroupInput, DeleteExperimentGroupOutput, Any, Any], - Interceptor[DeleteFunctionInput, DeleteFunctionOutput, Any, Any], - Interceptor[DeleteSecretInput, DeleteSecretOutput, Any, Any], - Interceptor[DeleteTypeTemplatesInput, DeleteTypeTemplatesOutput, Any, Any], - Interceptor[DeleteVariableInput, DeleteVariableOutput, Any, Any], - Interceptor[DeleteWebhookInput, DeleteWebhookOutput, Any, Any], - Interceptor[DiscardExperimentInput, DiscardExperimentOutput, Any, Any], - Interceptor[GetConfigInput, GetConfigOutput, Any, Any], - Interceptor[GetConfigJsonInput, GetConfigJsonOutput, Any, Any], - Interceptor[GetConfigTomlInput, GetConfigTomlOutput, Any, Any], - Interceptor[GetContextInput, GetContextOutput, Any, Any], - Interceptor[GetContextFromConditionInput, GetContextFromConditionOutput, Any, Any], - Interceptor[GetDefaultConfigInput, GetDefaultConfigOutput, Any, Any], - Interceptor[GetDetailedResolvedConfigInput, GetDetailedResolvedConfigOutput, Any, Any], - Interceptor[GetDimensionInput, GetDimensionOutput, Any, Any], - Interceptor[GetExperimentInput, GetExperimentOutput, Any, Any], - Interceptor[GetExperimentConfigInput, GetExperimentConfigOutput, Any, Any], - Interceptor[GetExperimentGroupInput, GetExperimentGroupOutput, Any, Any], - Interceptor[GetFunctionInput, GetFunctionOutput, Any, Any], - Interceptor[GetOrganisationInput, GetOrganisationOutput, Any, Any], - Interceptor[GetResolvedConfigInput, GetResolvedConfigOutput, Any, Any], - Interceptor[GetResolvedConfigExplanationInput, GetResolvedConfigExplanationOutput, Any, Any], - Interceptor[GetResolvedConfigWithIdentifierInput, GetResolvedConfigWithIdentifierOutput, Any, Any], - Interceptor[GetSecretInput, GetSecretOutput, Any, Any], - Interceptor[GetTypeTemplateInput, GetTypeTemplateOutput, Any, Any], - Interceptor[GetTypeTemplatesListInput, GetTypeTemplatesListOutput, Any, Any], - Interceptor[GetVariableInput, GetVariableOutput, Any, Any], - Interceptor[GetVersionInput, GetVersionOutput, Any, Any], - Interceptor[GetWebhookInput, GetWebhookOutput, Any, Any], - Interceptor[GetWebhookByEventInput, GetWebhookByEventOutput, Any, Any], - Interceptor[GetWorkspaceInput, GetWorkspaceOutput, Any, Any], - Interceptor[ImportConfigJsonInput, ImportConfigJsonOutput, Any, Any], - Interceptor[ImportConfigTomlInput, ImportConfigTomlOutput, Any, Any], - Interceptor[ListAuditLogsInput, ListAuditLogsOutput, Any, Any], - Interceptor[ListContextsInput, ListContextsOutput, Any, Any], - Interceptor[ListDefaultConfigsInput, ListDefaultConfigsOutput, Any, Any], - Interceptor[ListDimensionsInput, ListDimensionsOutput, Any, Any], - Interceptor[ListExperimentInput, ListExperimentOutput, Any, Any], - Interceptor[ListExperimentGroupsInput, ListExperimentGroupsOutput, Any, Any], - Interceptor[ListFunctionInput, ListFunctionOutput, Any, Any], - Interceptor[ListOrganisationInput, ListOrganisationOutput, Any, Any], - Interceptor[ListSecretsInput, ListSecretsOutput, Any, Any], - Interceptor[ListVariablesInput, ListVariablesOutput, Any, Any], - Interceptor[ListVersionsInput, ListVersionsOutput, Any, Any], - Interceptor[ListWebhookInput, ListWebhookOutput, Any, Any], - Interceptor[ListWorkspaceInput, ListWorkspaceOutput, Any, Any], - Interceptor[MigrateWorkspaceSchemaInput, MigrateWorkspaceSchemaOutput, Any, Any], - Interceptor[MoveContextInput, MoveContextOutput, Any, Any], - Interceptor[PauseExperimentInput, PauseExperimentOutput, Any, Any], - Interceptor[PublishInput, PublishOutput, Any, Any], - Interceptor[RampExperimentInput, RampExperimentOutput, Any, Any], - Interceptor[RemoveMembersFromGroupInput, RemoveMembersFromGroupOutput, Any, Any], - Interceptor[ResumeExperimentInput, ResumeExperimentOutput, Any, Any], - Interceptor[RotateMasterEncryptionKeyInput, RotateMasterEncryptionKeyOutput, Any, Any], - Interceptor[RotateWorkspaceEncryptionKeyInput, RotateWorkspaceEncryptionKeyOutput, Any, Any], - Interceptor[TestInput, TestOutput, Any, Any], - Interceptor[UpdateDefaultConfigInput, UpdateDefaultConfigOutput, Any, Any], - Interceptor[UpdateDimensionInput, UpdateDimensionOutput, Any, Any], - Interceptor[UpdateExperimentGroupInput, UpdateExperimentGroupOutput, Any, Any], - Interceptor[UpdateFunctionInput, UpdateFunctionOutput, Any, Any], - Interceptor[UpdateOrganisationInput, UpdateOrganisationOutput, Any, Any], - Interceptor[UpdateOverrideInput, UpdateOverrideOutput, Any, Any], - Interceptor[UpdateOverridesExperimentInput, UpdateOverridesExperimentOutput, Any, Any], - Interceptor[UpdateSecretInput, UpdateSecretOutput, Any, Any], - Interceptor[UpdateTypeTemplatesInput, UpdateTypeTemplatesOutput, Any, Any], - Interceptor[UpdateVariableInput, UpdateVariableOutput, Any, Any], - Interceptor[UpdateWebhookInput, UpdateWebhookOutput, Any, Any], - Interceptor[UpdateWorkspaceInput, UpdateWorkspaceOutput, Any, Any], - Interceptor[ValidateContextInput, ValidateContextOutput, Any, Any], - Interceptor[WeightRecomputeInput, WeightRecomputeOutput, Any, Any], -] +_ServiceInterceptor = Union[Interceptor[AddMembersToGroupInput, AddMembersToGroupOutput, Any, Any], Interceptor[ApplicableVariantsInput, ApplicableVariantsOutput, Any, Any], Interceptor[BulkOperationInput, BulkOperationOutput, Any, Any], Interceptor[ConcludeExperimentInput, ConcludeExperimentOutput, Any, Any], Interceptor[CreateContextInput, CreateContextOutput, Any, Any], Interceptor[CreateDefaultConfigInput, CreateDefaultConfigOutput, Any, Any], Interceptor[CreateDimensionInput, CreateDimensionOutput, Any, Any], Interceptor[CreateExperimentInput, CreateExperimentOutput, Any, Any], Interceptor[CreateExperimentGroupInput, CreateExperimentGroupOutput, Any, Any], Interceptor[CreateFunctionInput, CreateFunctionOutput, Any, Any], Interceptor[CreateOrganisationInput, CreateOrganisationOutput, Any, Any], Interceptor[CreateSecretInput, CreateSecretOutput, Any, Any], Interceptor[CreateTypeTemplatesInput, CreateTypeTemplatesOutput, Any, Any], Interceptor[CreateVariableInput, CreateVariableOutput, Any, Any], Interceptor[CreateWebhookInput, CreateWebhookOutput, Any, Any], Interceptor[CreateWorkspaceInput, CreateWorkspaceOutput, Any, Any], Interceptor[DeleteContextInput, DeleteContextOutput, Any, Any], Interceptor[DeleteDefaultConfigInput, DeleteDefaultConfigOutput, Any, Any], Interceptor[DeleteDimensionInput, DeleteDimensionOutput, Any, Any], Interceptor[DeleteExperimentGroupInput, DeleteExperimentGroupOutput, Any, Any], Interceptor[DeleteFunctionInput, DeleteFunctionOutput, Any, Any], Interceptor[DeleteSecretInput, DeleteSecretOutput, Any, Any], Interceptor[DeleteTypeTemplatesInput, DeleteTypeTemplatesOutput, Any, Any], Interceptor[DeleteVariableInput, DeleteVariableOutput, Any, Any], Interceptor[DeleteWebhookInput, DeleteWebhookOutput, Any, Any], Interceptor[DiscardExperimentInput, DiscardExperimentOutput, Any, Any], Interceptor[GetConfigInput, GetConfigOutput, Any, Any], Interceptor[GetConfigJsonInput, GetConfigJsonOutput, Any, Any], Interceptor[GetConfigTomlInput, GetConfigTomlOutput, Any, Any], Interceptor[GetContextInput, GetContextOutput, Any, Any], Interceptor[GetContextFromConditionInput, GetContextFromConditionOutput, Any, Any], Interceptor[GetDefaultConfigInput, GetDefaultConfigOutput, Any, Any], Interceptor[GetDetailedResolvedConfigInput, GetDetailedResolvedConfigOutput, Any, Any], Interceptor[GetDimensionInput, GetDimensionOutput, Any, Any], Interceptor[GetExperimentInput, GetExperimentOutput, Any, Any], Interceptor[GetExperimentConfigInput, GetExperimentConfigOutput, Any, Any], Interceptor[GetExperimentGroupInput, GetExperimentGroupOutput, Any, Any], Interceptor[GetFunctionInput, GetFunctionOutput, Any, Any], Interceptor[GetOrganisationInput, GetOrganisationOutput, Any, Any], Interceptor[GetResolvedConfigInput, GetResolvedConfigOutput, Any, Any], Interceptor[GetResolvedConfigExplanationInput, GetResolvedConfigExplanationOutput, Any, Any], Interceptor[GetResolvedConfigWithIdentifierInput, GetResolvedConfigWithIdentifierOutput, Any, Any], Interceptor[GetSecretInput, GetSecretOutput, Any, Any], Interceptor[GetTypeTemplateInput, GetTypeTemplateOutput, Any, Any], Interceptor[GetTypeTemplatesListInput, GetTypeTemplatesListOutput, Any, Any], Interceptor[GetVariableInput, GetVariableOutput, Any, Any], Interceptor[GetVersionInput, GetVersionOutput, Any, Any], Interceptor[GetWebhookInput, GetWebhookOutput, Any, Any], Interceptor[GetWebhookByEventInput, GetWebhookByEventOutput, Any, Any], Interceptor[GetWorkspaceInput, GetWorkspaceOutput, Any, Any], Interceptor[ImportConfigJsonInput, ImportConfigJsonOutput, Any, Any], Interceptor[ImportConfigTomlInput, ImportConfigTomlOutput, Any, Any], Interceptor[ListAuditLogsInput, ListAuditLogsOutput, Any, Any], Interceptor[ListContextsInput, ListContextsOutput, Any, Any], Interceptor[ListDefaultConfigsInput, ListDefaultConfigsOutput, Any, Any], Interceptor[ListDimensionsInput, ListDimensionsOutput, Any, Any], Interceptor[ListExperimentInput, ListExperimentOutput, Any, Any], Interceptor[ListExperimentGroupsInput, ListExperimentGroupsOutput, Any, Any], Interceptor[ListFunctionInput, ListFunctionOutput, Any, Any], Interceptor[ListOrganisationInput, ListOrganisationOutput, Any, Any], Interceptor[ListSecretsInput, ListSecretsOutput, Any, Any], Interceptor[ListVariablesInput, ListVariablesOutput, Any, Any], Interceptor[ListVersionsInput, ListVersionsOutput, Any, Any], Interceptor[ListWebhookInput, ListWebhookOutput, Any, Any], Interceptor[ListWorkspaceInput, ListWorkspaceOutput, Any, Any], Interceptor[MigrateWorkspaceSchemaInput, MigrateWorkspaceSchemaOutput, Any, Any], Interceptor[MoveContextInput, MoveContextOutput, Any, Any], Interceptor[PauseExperimentInput, PauseExperimentOutput, Any, Any], Interceptor[PublishInput, PublishOutput, Any, Any], Interceptor[RampExperimentInput, RampExperimentOutput, Any, Any], Interceptor[RemoveMembersFromGroupInput, RemoveMembersFromGroupOutput, Any, Any], Interceptor[ResumeExperimentInput, ResumeExperimentOutput, Any, Any], Interceptor[RotateMasterEncryptionKeyInput, RotateMasterEncryptionKeyOutput, Any, Any], Interceptor[RotateWorkspaceEncryptionKeyInput, RotateWorkspaceEncryptionKeyOutput, Any, Any], Interceptor[TestInput, TestOutput, Any, Any], Interceptor[UpdateDefaultConfigInput, UpdateDefaultConfigOutput, Any, Any], Interceptor[UpdateDimensionInput, UpdateDimensionOutput, Any, Any], Interceptor[UpdateExperimentGroupInput, UpdateExperimentGroupOutput, Any, Any], Interceptor[UpdateFunctionInput, UpdateFunctionOutput, Any, Any], Interceptor[UpdateOrganisationInput, UpdateOrganisationOutput, Any, Any], Interceptor[UpdateOverrideInput, UpdateOverrideOutput, Any, Any], Interceptor[UpdateOverridesExperimentInput, UpdateOverridesExperimentOutput, Any, Any], Interceptor[UpdateSecretInput, UpdateSecretOutput, Any, Any], Interceptor[UpdateTypeTemplatesInput, UpdateTypeTemplatesOutput, Any, Any], Interceptor[UpdateVariableInput, UpdateVariableOutput, Any, Any], Interceptor[UpdateWebhookInput, UpdateWebhookOutput, Any, Any], Interceptor[UpdateWorkspaceInput, UpdateWorkspaceOutput, Any, Any], Interceptor[ValidateContextInput, ValidateContextOutput, Any, Any], Interceptor[WeightRecomputeInput, WeightRecomputeOutput, Any, Any]] @dataclass(init=False) class Config: """Configuration for Superposition.""" diff --git a/clients/python/sdk/superposition_sdk/models.py b/clients/python/sdk/superposition_sdk/models.py index 1aa00a3e0..dd9197e7c 100644 --- a/clients/python/sdk/superposition_sdk/models.py +++ b/clients/python/sdk/superposition_sdk/models.py @@ -15260,37 +15260,43 @@ def _consumer(schema: Schema, de: ShapeDeserializer) -> None: ] ) -class ImportMode(StrEnum): +class ImportOnError(StrEnum): """ - How an import treats workspace entities that are not present in the imported - file. + How an import reacts when an individual entity fails to apply. """ - MERGE = "merge" + ABORT = "abort" """ - Upsert the entities in the file and leave everything else untouched. + Roll the whole import back on the first error. """ - REPLACE = "replace" + CONTINUE_ = "continue" """ - Mirror the file: additionally delete dimensions, default-configs and contexts - that are absent from it. + Apply everything that is valid and report per-entity errors. """ -class ImportOnError(StrEnum): +class ImportStrategy(StrEnum): """ - How an import reacts when an individual entity fails to apply. + How an import applies file entities to the workspace. """ - ABORT = "abort" + CREATE_ONLY = "create_only" """ - Roll the whole import back on the first error. + Create entities that are present in the file but missing from the workspace. + Existing entities are skipped. Nothing is deleted. """ - CONTINUE_ = "continue" + UPSERT = "upsert" """ - Apply everything that is valid and report per-entity errors. + Create missing entities and update existing entities from the file. Entities + absent from the file are left untouched. + + """ + REPLACE = "replace" + """ + Mirror the file: create missing entities, update existing entities, and delete + dimensions, default-configs and contexts that are absent from it. """ @@ -15298,12 +15304,8 @@ class ImportOnError(StrEnum): class ImportConfigJsonInput: """ - :param mode: - Whether to merge (default) or replace existing workspace config. - - :param overwrite: - When false, entities that already exist are skipped instead of updated. Defaults - to true. + :param strategy: + How the import applies file entities to the workspace. Defaults to upsert. :param on_error: Whether to abort (default) or continue on per-entity errors. @@ -15312,19 +15314,13 @@ class ImportConfigJsonInput: When true, validates and summarises the import without persisting anything. Defaults to false. - :param value_merge: - When true, deep-merges object-valued default-configs with the existing value. - Defaults to false. - """ workspace_id: str | None = None org_id: str | None = None - mode: str | None = None - overwrite: bool | None = None + strategy: str | None = None on_error: str | None = None dry_run: bool | None = None - value_merge: bool | None = None config_tags: str | None = None json_config: str | None = None @@ -15351,24 +15347,18 @@ def _consumer(schema: Schema, de: ShapeDeserializer) -> None: kwargs["org_id"] = de.read_string(_SCHEMA_IMPORT_CONFIG_JSON_INPUT.members["org_id"]) case 2: - kwargs["mode"] = de.read_string(_SCHEMA_IMPORT_CONFIG_JSON_INPUT.members["mode"]) + kwargs["strategy"] = de.read_string(_SCHEMA_IMPORT_CONFIG_JSON_INPUT.members["strategy"]) case 3: - kwargs["overwrite"] = de.read_boolean(_SCHEMA_IMPORT_CONFIG_JSON_INPUT.members["overwrite"]) - - case 4: kwargs["on_error"] = de.read_string(_SCHEMA_IMPORT_CONFIG_JSON_INPUT.members["on_error"]) - case 5: + case 4: kwargs["dry_run"] = de.read_boolean(_SCHEMA_IMPORT_CONFIG_JSON_INPUT.members["dry_run"]) - case 6: - kwargs["value_merge"] = de.read_boolean(_SCHEMA_IMPORT_CONFIG_JSON_INPUT.members["value_merge"]) - - case 7: + case 5: kwargs["config_tags"] = de.read_string(_SCHEMA_IMPORT_CONFIG_JSON_INPUT.members["config_tags"]) - case 8: + case 6: kwargs["json_config"] = de.read_string(_SCHEMA_IMPORT_CONFIG_JSON_INPUT.members["json_config"]) case _: @@ -15505,7 +15495,7 @@ class ImportConfigJsonOutput: """ - mode: str + strategy: str dry_run: bool @@ -15521,7 +15511,7 @@ def serialize(self, serializer: ShapeSerializer): serializer.write_struct(_SCHEMA_IMPORT_CONFIG_JSON_OUTPUT, self) def serialize_members(self, serializer: ShapeSerializer): - serializer.write_string(_SCHEMA_IMPORT_CONFIG_JSON_OUTPUT.members["mode"], self.mode) + serializer.write_string(_SCHEMA_IMPORT_CONFIG_JSON_OUTPUT.members["strategy"], self.strategy) serializer.write_boolean(_SCHEMA_IMPORT_CONFIG_JSON_OUTPUT.members["dry_run"], self.dry_run) if self.config_version is not None: serializer.write_string(_SCHEMA_IMPORT_CONFIG_JSON_OUTPUT.members["config_version"], self.config_version) @@ -15541,7 +15531,7 @@ def deserialize_kwargs(cls, deserializer: ShapeDeserializer) -> dict[str, Any]: def _consumer(schema: Schema, de: ShapeDeserializer) -> None: match schema.expect_member_index(): case 0: - kwargs["mode"] = de.read_string(_SCHEMA_IMPORT_CONFIG_JSON_OUTPUT.members["mode"]) + kwargs["strategy"] = de.read_string(_SCHEMA_IMPORT_CONFIG_JSON_OUTPUT.members["strategy"]) case 1: kwargs["dry_run"] = de.read_boolean(_SCHEMA_IMPORT_CONFIG_JSON_OUTPUT.members["dry_run"]) @@ -15583,12 +15573,8 @@ def _consumer(schema: Schema, de: ShapeDeserializer) -> None: class ImportConfigTomlInput: """ - :param mode: - Whether to merge (default) or replace existing workspace config. - - :param overwrite: - When false, entities that already exist are skipped instead of updated. Defaults - to true. + :param strategy: + How the import applies file entities to the workspace. Defaults to upsert. :param on_error: Whether to abort (default) or continue on per-entity errors. @@ -15597,19 +15583,13 @@ class ImportConfigTomlInput: When true, validates and summarises the import without persisting anything. Defaults to false. - :param value_merge: - When true, deep-merges object-valued default-configs with the existing value. - Defaults to false. - """ workspace_id: str | None = None org_id: str | None = None - mode: str | None = None - overwrite: bool | None = None + strategy: str | None = None on_error: str | None = None dry_run: bool | None = None - value_merge: bool | None = None config_tags: str | None = None toml_config: str | None = None @@ -15636,24 +15616,18 @@ def _consumer(schema: Schema, de: ShapeDeserializer) -> None: kwargs["org_id"] = de.read_string(_SCHEMA_IMPORT_CONFIG_TOML_INPUT.members["org_id"]) case 2: - kwargs["mode"] = de.read_string(_SCHEMA_IMPORT_CONFIG_TOML_INPUT.members["mode"]) + kwargs["strategy"] = de.read_string(_SCHEMA_IMPORT_CONFIG_TOML_INPUT.members["strategy"]) case 3: - kwargs["overwrite"] = de.read_boolean(_SCHEMA_IMPORT_CONFIG_TOML_INPUT.members["overwrite"]) - - case 4: kwargs["on_error"] = de.read_string(_SCHEMA_IMPORT_CONFIG_TOML_INPUT.members["on_error"]) - case 5: + case 4: kwargs["dry_run"] = de.read_boolean(_SCHEMA_IMPORT_CONFIG_TOML_INPUT.members["dry_run"]) - case 6: - kwargs["value_merge"] = de.read_boolean(_SCHEMA_IMPORT_CONFIG_TOML_INPUT.members["value_merge"]) - - case 7: + case 5: kwargs["config_tags"] = de.read_string(_SCHEMA_IMPORT_CONFIG_TOML_INPUT.members["config_tags"]) - case 8: + case 6: kwargs["toml_config"] = de.read_string(_SCHEMA_IMPORT_CONFIG_TOML_INPUT.members["toml_config"]) case _: @@ -15678,7 +15652,7 @@ class ImportConfigTomlOutput: """ - mode: str + strategy: str dry_run: bool @@ -15694,7 +15668,7 @@ def serialize(self, serializer: ShapeSerializer): serializer.write_struct(_SCHEMA_IMPORT_CONFIG_TOML_OUTPUT, self) def serialize_members(self, serializer: ShapeSerializer): - serializer.write_string(_SCHEMA_IMPORT_CONFIG_TOML_OUTPUT.members["mode"], self.mode) + serializer.write_string(_SCHEMA_IMPORT_CONFIG_TOML_OUTPUT.members["strategy"], self.strategy) serializer.write_boolean(_SCHEMA_IMPORT_CONFIG_TOML_OUTPUT.members["dry_run"], self.dry_run) if self.config_version is not None: serializer.write_string(_SCHEMA_IMPORT_CONFIG_TOML_OUTPUT.members["config_version"], self.config_version) @@ -15714,7 +15688,7 @@ def deserialize_kwargs(cls, deserializer: ShapeDeserializer) -> dict[str, Any]: def _consumer(schema: Schema, de: ShapeDeserializer) -> None: match schema.expect_member_index(): case 0: - kwargs["mode"] = de.read_string(_SCHEMA_IMPORT_CONFIG_TOML_OUTPUT.members["mode"]) + kwargs["strategy"] = de.read_string(_SCHEMA_IMPORT_CONFIG_TOML_OUTPUT.members["strategy"]) case 1: kwargs["dry_run"] = de.read_boolean(_SCHEMA_IMPORT_CONFIG_TOML_OUTPUT.members["dry_run"]) diff --git a/clients/python/sdk/superposition_sdk/serialize.py b/clients/python/sdk/superposition_sdk/serialize.py index 4d6a6db12..3f6afeacd 100644 --- a/clients/python/sdk/superposition_sdk/serialize.py +++ b/clients/python/sdk/superposition_sdk/serialize.py @@ -1941,16 +1941,12 @@ async def _serialize_import_config_json(input: ImportConfigJsonInput, config: Co headers.extend(Fields([Field(name="x-workspace", values=[input.workspace_id])])) if input.org_id: headers.extend(Fields([Field(name="x-org-id", values=[input.org_id])])) - if input.mode: - headers.extend(Fields([Field(name="x-import-mode", values=[input.mode])])) - if input.overwrite is not None: - headers.extend(Fields([Field(name="x-import-overwrite", values=[('true' if input.overwrite else 'false')])])) + if input.strategy: + headers.extend(Fields([Field(name="x-import-strategy", values=[input.strategy])])) if input.on_error: headers.extend(Fields([Field(name="x-import-on-error", values=[input.on_error])])) if input.dry_run is not None: headers.extend(Fields([Field(name="x-import-dry-run", values=[('true' if input.dry_run else 'false')])])) - if input.value_merge is not None: - headers.extend(Fields([Field(name="x-import-value-merge", values=[('true' if input.value_merge else 'false')])])) if input.config_tags: headers.extend(Fields([Field(name="x-config-tags", values=[input.config_tags])])) return _HTTPRequest( @@ -1987,16 +1983,12 @@ async def _serialize_import_config_toml(input: ImportConfigTomlInput, config: Co headers.extend(Fields([Field(name="x-workspace", values=[input.workspace_id])])) if input.org_id: headers.extend(Fields([Field(name="x-org-id", values=[input.org_id])])) - if input.mode: - headers.extend(Fields([Field(name="x-import-mode", values=[input.mode])])) - if input.overwrite is not None: - headers.extend(Fields([Field(name="x-import-overwrite", values=[('true' if input.overwrite else 'false')])])) + if input.strategy: + headers.extend(Fields([Field(name="x-import-strategy", values=[input.strategy])])) if input.on_error: headers.extend(Fields([Field(name="x-import-on-error", values=[input.on_error])])) if input.dry_run is not None: headers.extend(Fields([Field(name="x-import-dry-run", values=[('true' if input.dry_run else 'false')])])) - if input.value_merge is not None: - headers.extend(Fields([Field(name="x-import-value-merge", values=[('true' if input.value_merge else 'false')])])) if input.config_tags: headers.extend(Fields([Field(name="x-config-tags", values=[input.config_tags])])) return _HTTPRequest( diff --git a/crates/context_aware_config/src/api/config/import.rs b/crates/context_aware_config/src/api/config/import.rs index e65c56c0a..9a3c2465b 100644 --- a/crates/context_aware_config/src/api/config/import.rs +++ b/crates/context_aware_config/src/api/config/import.rs @@ -6,25 +6,22 @@ //! [`ConfigFormat::parse_into_detailed`] and then persisted to the workspace. //! //! The behaviour is controlled through request headers: -//! - `x-import-mode`: `merge` (default) upserts the entities in the file and -//! leaves everything else untouched; `replace` additionally deletes any -//! dimension/default-config/context that is absent from the file (mirror). -//! - `x-import-overwrite`: `true` (default) updates entities that already -//! exist; `false` skips them (only new entities are created). +//! - `x-import-strategy`: `upsert` (default) creates missing entities and +//! updates existing entities; `create_only` only creates missing entities and +//! skips existing entities; `replace` mirrors the file by also deleting any +//! dimension/default-config/context that is absent from it. //! - `x-import-on-error`: `abort` (default) fails the whole import on the //! first error; `continue` records per-entity errors and applies the rest. //! - `x-import-dry-run`: `true` parses, validates and computes the summary //! without persisting anything (the transaction is rolled back). -//! - `x-import-value-merge`: `true` deep-merges object-valued default configs -//! with their existing value instead of replacing them wholesale. use std::collections::HashSet; use actix_web::{HttpRequest, HttpResponse, web::Data}; use chrono::Utc; -use diesel::{Connection, ExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl}; +use diesel::{Connection, ExpressionMethods, QueryDsl, RunQueryDsl}; use serde::Serialize; -use serde_json::{Map, Value}; +use serde_json::Value; use service_utils::{ helpers::{WebhookData, execute_webhook_call, parse_config_tags}, service::types::{AppState, CustomHeaders, SchemaName, WorkspaceContext}, @@ -58,8 +55,9 @@ use crate::{ #[derive(Clone, Copy, PartialEq, Serialize)] #[serde(rename_all = "snake_case")] -pub enum ImportMode { - Merge, +pub enum ImportStrategy { + CreateOnly, + Upsert, Replace, } @@ -71,11 +69,9 @@ pub enum OnError { #[derive(Clone, Copy)] pub struct ImportOptions { - pub mode: ImportMode, - pub overwrite: bool, + pub strategy: ImportStrategy, pub on_error: OnError, pub dry_run: bool, - pub value_merge: bool, } fn header_bool(req: &HttpRequest, name: &str, default: bool) -> bool { @@ -88,17 +84,33 @@ fn header_bool(req: &HttpRequest, name: &str, default: bool) -> bool { impl ImportOptions { pub fn from_request(req: &HttpRequest) -> superposition::Result { - let mode = match req + for header in [ + "x-import-mode", + "x-import-overwrite", + "x-import-value-merge", + ] { + if req.headers().contains_key(header) { + return Err(bad_argument!( + "Header '{}' is no longer supported; use x-import-strategy", + header + )); + } + } + + let strategy = match req .headers() - .get("x-import-mode") + .get("x-import-strategy") .and_then(|v| v.to_str().ok()) { - None => ImportMode::Merge, - Some(s) if s.eq_ignore_ascii_case("merge") => ImportMode::Merge, - Some(s) if s.eq_ignore_ascii_case("replace") => ImportMode::Replace, + None => ImportStrategy::Upsert, + Some(s) if s.eq_ignore_ascii_case("create_only") => { + ImportStrategy::CreateOnly + } + Some(s) if s.eq_ignore_ascii_case("upsert") => ImportStrategy::Upsert, + Some(s) if s.eq_ignore_ascii_case("replace") => ImportStrategy::Replace, Some(s) => { return Err(bad_argument!( - "Invalid x-import-mode '{}', expected 'merge' or 'replace'", + "Invalid x-import-strategy '{}', expected 'create_only', 'upsert' or 'replace'", s )); } @@ -119,11 +131,9 @@ impl ImportOptions { } }; Ok(Self { - mode, - overwrite: header_bool(req, "x-import-overwrite", true), + strategy, on_error, dry_run: header_bool(req, "x-import-dry-run", false), - value_merge: header_bool(req, "x-import-value-merge", false), }) } } @@ -161,7 +171,7 @@ impl EntityReport { #[derive(Serialize)] pub struct ImportSummary { - pub mode: ImportMode, + pub strategy: ImportStrategy, pub dry_run: bool, #[serde(skip_serializing_if = "Option::is_none")] pub config_version: Option, @@ -173,7 +183,7 @@ pub struct ImportSummary { impl ImportSummary { fn new(opts: &ImportOptions) -> Self { Self { - mode: opts.mode, + strategy: opts.strategy, dry_run: opts.dry_run, config_version: None, dimensions: EntityReport::default(), @@ -279,45 +289,13 @@ fn import_description() -> Description { .unwrap_or_default() } -/// Deep-merge two JSON values, with `overlay` winning at the leaves. Objects -/// are merged key-by-key recursively; any other shape is replaced wholesale. -fn deep_merge(base: &Value, overlay: &Value) -> Value { - match (base, overlay) { - (Value::Object(base_map), Value::Object(overlay_map)) => { - let mut merged: Map = base_map.clone(); - for (k, v) in overlay_map { - let next = match merged.get(k) { - Some(existing) => deep_merge(existing, v), - None => v.clone(), - }; - merged.insert(k.clone(), next); - } - Value::Object(merged) - } - _ => overlay.clone(), - } -} - // --------------------------------------------------------------------------- // Pure builders / decisions (no database access — unit tested below) // --------------------------------------------------------------------------- /// Whether an entity that already exists should be left untouched. -fn should_skip(exists: bool, overwrite: bool) -> bool { - exists && !overwrite -} - -/// The value to persist for a default config, honouring the `value_merge` -/// option (deep-merge with the existing value when both are objects). -fn resolve_default_value( - existing: Option<&Value>, - incoming: &Value, - value_merge: bool, -) -> Value { - match (value_merge, existing) { - (true, Some(existing)) => deep_merge(existing, incoming), - _ => incoming.clone(), - } +fn should_skip(exists: bool, strategy: ImportStrategy) -> bool { + exists && strategy == ImportStrategy::CreateOnly } fn dimension_position( @@ -396,7 +374,7 @@ fn write_dimension( .get_result::(conn)? > 0; - if should_skip(exists, opts.overwrite) { + if should_skip(exists, opts.strategy) { return Ok(Outcome::Skipped); } @@ -435,19 +413,18 @@ fn write_default_config( opts: &ImportOptions, email: &str, ) -> superposition::Result { - let existing: Option = dc_dsl::default_configs + let exists = dc_dsl::default_configs .filter(dc_dsl::key.eq(key)) - .select(dc_dsl::value) + .count() .schema_name(schema_name) - .first::(conn) - .optional()?; - let exists = existing.is_some(); + .get_result::(conn)? + > 0; - if should_skip(exists, opts.overwrite) { + if should_skip(exists, opts.strategy) { return Ok(Outcome::Skipped); } - let value = resolve_default_value(existing.as_ref(), &info.value, opts.value_merge); + let value = info.value.clone(); let schema = build_schema(key, &info.schema)?; if exists { @@ -500,7 +477,7 @@ fn write_context( .get_result::(conn)? > 0; - if should_skip(exists, opts.overwrite) { + if should_skip(exists, opts.strategy) { return Ok(Outcome::Skipped); } @@ -582,10 +559,10 @@ pub async fn import_config( apply_outcome(&mut summary.contexts, opts.on_error, &ctx.id, res)?; } - // Replace (mirror) mode: delete anything in the workspace that is not + // Replace strategy: delete anything in the workspace that is not // present in the imported file. Contexts first, then the entities they // can reference. - if opts.mode == ImportMode::Replace { + if opts.strategy == ImportStrategy::Replace { let file_ctx_ids: HashSet<&String> = parsed.contexts.iter().map(|c| &c.id).collect(); let db_ctx_ids: Vec = ctx_dsl::contexts @@ -721,59 +698,49 @@ mod tests { } #[test] - fn deep_merge_combines_nested_objects() { - let base = json!({ "a": 1, "nested": { "x": 1, "y": 2 } }); - let overlay = json!({ "b": 2, "nested": { "y": 20, "z": 30 } }); - assert_eq!( - deep_merge(&base, &overlay), - json!({ "a": 1, "b": 2, "nested": { "x": 1, "y": 20, "z": 30 } }) - ); - } - - #[test] - fn deep_merge_overlay_wins_for_scalars_and_arrays() { - assert_eq!(deep_merge(&json!(1), &json!(2)), json!(2)); - assert_eq!(deep_merge(&json!([1, 2]), &json!([3])), json!([3])); - // a non-object overlay fully replaces an object base - assert_eq!(deep_merge(&json!({ "a": 1 }), &json!("x")), json!("x")); - } - - #[test] - fn options_default_to_safe_merge() { + fn options_default_to_upsert() { let req = TestRequest::default().to_http_request(); let opts = ImportOptions::from_request(&req).unwrap(); - assert!(opts.mode == ImportMode::Merge); - assert!(opts.overwrite); + assert!(opts.strategy == ImportStrategy::Upsert); assert!(opts.on_error == OnError::Abort); assert!(!opts.dry_run); - assert!(!opts.value_merge); } #[test] fn options_parsed_from_headers() { let req = TestRequest::default() - .insert_header(("x-import-mode", "replace")) - .insert_header(("x-import-overwrite", "false")) + .insert_header(("x-import-strategy", "replace")) .insert_header(("x-import-on-error", "continue")) .insert_header(("x-import-dry-run", "true")) - .insert_header(("x-import-value-merge", "true")) .to_http_request(); let opts = ImportOptions::from_request(&req).unwrap(); - assert!(opts.mode == ImportMode::Replace); - assert!(!opts.overwrite); + assert!(opts.strategy == ImportStrategy::Replace); assert!(opts.on_error == OnError::Continue); assert!(opts.dry_run); - assert!(opts.value_merge); } #[test] - fn invalid_mode_is_rejected() { + fn invalid_strategy_is_rejected() { let req = TestRequest::default() - .insert_header(("x-import-mode", "bogus")) + .insert_header(("x-import-strategy", "bogus")) .to_http_request(); assert!(ImportOptions::from_request(&req).is_err()); } + #[test] + fn removed_import_headers_are_rejected() { + for header in [ + "x-import-mode", + "x-import-overwrite", + "x-import-value-merge", + ] { + let req = TestRequest::default() + .insert_header((header, "true")) + .to_http_request(); + assert!(ImportOptions::from_request(&req).is_err()); + } + } + #[test] fn invalid_on_error_is_rejected() { let req = TestRequest::default() @@ -783,42 +750,11 @@ mod tests { } #[test] - fn should_skip_only_existing_without_overwrite() { - // skip only when the entity exists AND overwrite is disabled - assert!(should_skip(true, false)); - assert!(!should_skip(true, true)); // exists, but overwrite -> update - assert!(!should_skip(false, false)); // new -> always created - assert!(!should_skip(false, true)); - } - - #[test] - fn resolve_default_value_without_merge_replaces() { - let existing = json!({ "a": 1 }); - let incoming = json!({ "b": 2 }); - // value_merge disabled -> incoming wins wholesale - assert_eq!( - resolve_default_value(Some(&existing), &incoming, false), - json!({ "b": 2 }) - ); - } - - #[test] - fn resolve_default_value_with_merge_deep_merges_existing() { - let existing = json!({ "a": 1, "nested": { "x": 1 } }); - let incoming = json!({ "b": 2, "nested": { "y": 2 } }); - assert_eq!( - resolve_default_value(Some(&existing), &incoming, true), - json!({ "a": 1, "b": 2, "nested": { "x": 1, "y": 2 } }) - ); - } - - #[test] - fn resolve_default_value_with_merge_but_no_existing_uses_incoming() { - let incoming = json!({ "b": 2 }); - assert_eq!( - resolve_default_value(None, &incoming, true), - json!({ "b": 2 }) - ); + fn should_skip_only_existing_create_only_entities() { + assert!(should_skip(true, ImportStrategy::CreateOnly)); + assert!(!should_skip(false, ImportStrategy::CreateOnly)); + assert!(!should_skip(true, ImportStrategy::Upsert)); + assert!(!should_skip(true, ImportStrategy::Replace)); } #[test] @@ -872,18 +808,16 @@ mod tests { } #[test] - fn summary_serialises_with_mode_and_hides_empty_errors() { + fn summary_serialises_with_strategy_and_hides_empty_errors() { let opts = ImportOptions { - mode: ImportMode::Replace, - overwrite: true, + strategy: ImportStrategy::Replace, on_error: OnError::Abort, dry_run: true, - value_merge: false, }; let summary = ImportSummary::new(&opts); let value = serde_json::to_value(&summary).unwrap(); - assert_eq!(value["mode"], json!("replace")); + assert_eq!(value["strategy"], json!("replace")); assert_eq!(value["dry_run"], json!(true)); // config_version omitted until the import commits assert!(value.get("config_version").is_none()); diff --git a/crates/frontend/Cargo.toml b/crates/frontend/Cargo.toml index f3e6b97db..0f40da82d 100644 --- a/crates/frontend/Cargo.toml +++ b/crates/frontend/Cargo.toml @@ -34,6 +34,7 @@ superposition_types = { workspace = true, features = [ "experimentation", "api", ] } +toml = { workspace = true } url = { workspace = true } wasm-bindgen = "0.2.100" wasm-bindgen-futures = "0.4.50" @@ -41,9 +42,13 @@ web-sys = { version = "0.3.64", features = [ "Event", "Worker", "Blob", + "File", + "FileList", + "FileReader", "Window", "Element", "DomRect", + "HtmlInputElement", "HtmlElement", "UiEvent", "MouseEvent", diff --git a/crates/frontend/src/api.rs b/crates/frontend/src/api.rs index 4480ab8b3..63742a836 100644 --- a/crates/frontend/src/api.rs +++ b/crates/frontend/src/api.rs @@ -1,4 +1,5 @@ use reqwest::header::HeaderMap; +use serde::Deserialize; use serde_json::{Map, Value}; use superposition_types::{ Config, PaginatedResponse, @@ -23,7 +24,8 @@ use superposition_types::{ }; use crate::utils::{ - construct_request_headers, parse_json_response, request, use_host_server, + construct_request_headers, parse_json_response, request, request_raw_body, + use_host_server, }; pub mod casbin { @@ -561,6 +563,185 @@ pub async fn fetch_config( parse_json_response(response).await } +pub mod config_import { + use super::*; + + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub enum ImportFormat { + Json, + Toml, + } + + impl ImportFormat { + pub fn from_file_name(file_name: &str) -> Self { + if file_name.to_lowercase().ends_with(".json") { + Self::Json + } else { + Self::Toml + } + } + + fn content_type(&self) -> &'static str { + match self { + Self::Json => "application/json", + Self::Toml => "application/toml", + } + } + + fn path_segment(&self) -> &'static str { + match self { + Self::Json => "json", + Self::Toml => "toml", + } + } + } + + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub enum ImportStrategy { + CreateOnly, + Upsert, + Replace, + } + + impl ImportStrategy { + pub fn as_str(&self) -> &'static str { + match self { + Self::CreateOnly => "create_only", + Self::Upsert => "upsert", + Self::Replace => "replace", + } + } + } + + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub enum ImportOnError { + Abort, + Continue, + } + + impl ImportOnError { + fn as_str(&self) -> &'static str { + match self { + Self::Abort => "abort", + Self::Continue => "continue", + } + } + } + + #[derive(Clone, Debug)] + pub struct ImportOptions { + pub strategy: ImportStrategy, + pub on_error: ImportOnError, + pub dry_run: bool, + pub config_tags: String, + } + + #[derive(Clone, Debug, Default, Deserialize, PartialEq)] + pub struct ImportErrorItem { + pub id: String, + pub message: String, + } + + #[derive(Clone, Debug, Default, Deserialize, PartialEq)] + pub struct ImportEntityReport { + pub created: usize, + pub updated: usize, + pub skipped: usize, + pub deleted: usize, + #[serde(default)] + pub errors: Vec, + } + + impl ImportEntityReport { + pub fn total(&self) -> usize { + self.created + self.updated + self.skipped + self.deleted + } + } + + #[derive(Clone, Debug, Default, Deserialize, PartialEq)] + pub struct ImportSummary { + pub strategy: String, + pub dry_run: bool, + pub config_version: Option, + pub dimensions: ImportEntityReport, + pub default_configs: ImportEntityReport, + pub contexts: ImportEntityReport, + } + + impl ImportSummary { + pub fn total_changes(&self) -> usize { + self.dimensions.total() + self.default_configs.total() + self.contexts.total() + } + + pub fn total_deleted(&self) -> usize { + self.dimensions.deleted + self.default_configs.deleted + self.contexts.deleted + } + + pub fn total_errors(&self) -> usize { + self.dimensions.errors.len() + + self.default_configs.errors.len() + + self.contexts.errors.len() + } + } + + pub async fn import_config( + file_text: String, + format: ImportFormat, + options: ImportOptions, + workspace: &str, + org_id: &str, + ) -> Result { + let host = use_host_server(); + let url = format!("{host}/config/{}/import", format.path_segment()); + let dry_run = options.dry_run.to_string(); + let config_tags = options.config_tags.trim().to_string(); + let mut header_entries = vec![ + ("x-workspace", workspace), + ("x-org-id", org_id), + ("Content-Type", format.content_type()), + ("x-import-strategy", options.strategy.as_str()), + ("x-import-on-error", options.on_error.as_str()), + ("x-import-dry-run", dry_run.as_str()), + ]; + + if !config_tags.is_empty() { + header_entries.push(("x-config-tags", config_tags.as_str())); + } + + let response = request_raw_body( + url, + reqwest::Method::POST, + file_text, + construct_request_headers(&header_entries)?, + ) + .await?; + + parse_json_response(response).await + } + + pub async fn export_config( + format: ImportFormat, + workspace: &str, + org_id: &str, + ) -> Result { + let host = use_host_server(); + let url = format!("{host}/config/{}", format.path_segment()); + + let response = request( + url, + reqwest::Method::GET, + None::<()>, + construct_request_headers(&[ + ("x-workspace", workspace), + ("x-org-id", org_id), + ])?, + ) + .await?; + + response.text().await.map_err(|err| err.to_string()) + } +} + pub async fn fetch_context( pagination: &PaginationParams, context_filters: &ContextListFilters, diff --git a/crates/frontend/src/app.rs b/crates/frontend/src/app.rs index 23a8dd2e1..9418f1a91 100755 --- a/crates/frontend/src/app.rs +++ b/crates/frontend/src/app.rs @@ -28,6 +28,7 @@ use crate::pages::{ default_config_list::DefaultConfigList, experiment::ExperimentPage, home::Home, + import_config::ImportConfig, organisations::Organisations, override_page::{CreateOverride, EditOverride, OverridePage}, type_template::TypePage, @@ -342,6 +343,12 @@ pub fn App(app_envs: Envs) -> impl IntoView { view=Home /> + + Vec { icon: "ri-guide-fill".to_string(), label: "Overrides".to_string(), }, + AppRoute { + key: format!("{base}/admin/{org}/{workspace}/import"), + path: format!("{base}/admin/{org}/{workspace}/import"), + icon: "ri-file-upload-line".to_string(), + label: "Import".to_string(), + }, AppRoute { key: format!("{base}/admin/{org}/{workspace}/compare"), path: format!("{base}/admin/{org}/{workspace}/compare"), diff --git a/crates/frontend/src/pages.rs b/crates/frontend/src/pages.rs index 378cf3498..aa3b29a6c 100644 --- a/crates/frontend/src/pages.rs +++ b/crates/frontend/src/pages.rs @@ -14,6 +14,7 @@ pub mod experiment_groups; pub mod experiment_list; pub mod function; pub mod home; +pub mod import_config; pub mod not_found; pub mod organisations; pub mod override_page; diff --git a/crates/frontend/src/pages/import_config.rs b/crates/frontend/src/pages/import_config.rs new file mode 100644 index 000000000..317d1b875 --- /dev/null +++ b/crates/frontend/src/pages/import_config.rs @@ -0,0 +1,1263 @@ +use std::collections::{BTreeMap, BTreeSet}; + +use leptos::*; +use leptos_router::A; +use serde_json::{Map, Value}; +use wasm_bindgen::{JsCast, closure::Closure}; +use web_sys::{Event, FileReader, HtmlInputElement}; + +use crate::{ + api::config_import::{ + self, ImportEntityReport, ImportFormat, ImportOnError, ImportOptions, + ImportStrategy, ImportSummary, + }, + components::{ + alert::AlertType, + button::{Button, ButtonStyle}, + modal::PortalModal, + }, + providers::alert_provider::enqueue_alert, + types::{OrganisationId, Workspace}, + utils::use_url_base, +}; + +fn format_file_size(size: u64) -> String { + const KB: f64 = 1024.0; + const MB: f64 = KB * 1024.0; + let size = size as f64; + + if size >= MB { + format!("{:.1} MB", size / MB) + } else if size >= KB { + format!("{:.1} KB", size / KB) + } else { + format!("{} B", size as u64) + } +} + +fn strategy_choice_class(active: bool) -> String { + let state = if active { + "border-purple-400 bg-white text-purple-900 shadow-sm ring-1 ring-purple-100" + } else { + "border-transparent text-gray-700 hover:border-gray-200 hover:bg-white" + }; + + format!( + "min-h-[92px] rounded-md border px-4 py-3 text-left transition-colors {state}" + ) +} + +fn strategy_icon_class(active: bool) -> String { + let state = if active { + "bg-purple-50 text-purple-700" + } else { + "bg-gray-100 text-gray-600" + }; + + format!("mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-md {state}") +} + +fn parse_config_text(text: &str, format: ImportFormat) -> Result { + match format { + ImportFormat::Json => serde_json::from_str(text) + .map_err(|err| format!("Unable to parse JSON preview: {err}")), + ImportFormat::Toml => { + let value: toml::Value = toml::from_str(text) + .map_err(|err| format!("Unable to parse TOML preview: {err}"))?; + serde_json::to_value(value) + .map_err(|err| format!("Unable to normalize TOML preview: {err}")) + } + } +} + +fn object_section(config: &Value, section: &str) -> BTreeMap { + config + .get(section) + .and_then(Value::as_object) + .map(|items| { + items + .iter() + .map(|(key, value)| (key.clone(), value.clone())) + .collect() + }) + .unwrap_or_default() +} + +fn array_section(config: &Value, section: &str) -> Vec { + config + .get(section) + .and_then(Value::as_array) + .cloned() + .unwrap_or_default() +} + +fn value_preview(value: &Value) -> String { + match value { + Value::Null => "null".to_string(), + Value::Bool(value) => value.to_string(), + Value::Number(value) => value.to_string(), + Value::String(value) => value.clone(), + _ => serde_json::to_string(value).unwrap_or_else(|_| "-".to_string()), + } +} + +fn canonical_json(value: &Value) -> String { + serde_json::to_string(value).unwrap_or_default() +} + +fn description_preview(entry: &Value) -> String { + entry + .get("description") + .and_then(Value::as_str) + .filter(|description| !description.trim().is_empty()) + .map(ToString::to_string) + .unwrap_or_else(|| "-".to_string()) +} + +fn default_config_value_preview(entry: &Value) -> String { + entry + .get("value") + .map(value_preview) + .unwrap_or_else(|| value_preview(entry)) +} + +fn context_preview(context: &Value) -> String { + let Some(context) = context.as_object() else { + return "-".to_string(); + }; + + if context.is_empty() { + return "default".to_string(); + } + + context + .iter() + .map(|(key, value)| format!("{key} = {}", value_preview(value))) + .collect::>() + .join(", ") +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum ReviewAction { + Create, + Update, + Delete, + Skip, +} + +impl ReviewAction { + fn label(self) -> &'static str { + match self { + Self::Create => "Create", + Self::Update => "Update", + Self::Delete => "Delete", + Self::Skip => "Skip", + } + } + + fn class(self) -> &'static str { + match self { + Self::Create => "border-emerald-100 bg-emerald-50 text-emerald-700", + Self::Update => "border-blue-100 bg-blue-50 text-blue-700", + Self::Delete => "border-rose-100 bg-rose-50 text-rose-700", + Self::Skip => "border-gray-200 bg-gray-50 text-gray-600", + } + } +} + +#[derive(Clone, Debug)] +struct ReviewRow { + name: String, + action: ReviewAction, + description: String, + scope: String, + value: String, +} + +#[derive(Clone, Debug, Default)] +struct ReviewTables { + dimensions: Vec, + default_configs: Vec, + overrides: Vec, +} + +#[derive(Clone, Debug)] +struct OverrideEntry { + name: String, + scope: String, + value: String, + raw: Value, +} + +fn review_action( + imported: Option<&Value>, + current: Option<&Value>, + strategy: ImportStrategy, +) -> Option { + match (imported, current) { + (Some(_), None) => Some(ReviewAction::Create), + (Some(_), Some(_)) if strategy == ImportStrategy::CreateOnly => { + Some(ReviewAction::Skip) + } + (Some(_), Some(_)) => Some(ReviewAction::Update), + (None, Some(_)) if strategy == ImportStrategy::Replace => { + Some(ReviewAction::Delete) + } + (None, Some(_)) => None, + (None, None) => None, + } +} + +fn build_map_review_rows( + imported_config: &Value, + current_config: &Value, + section: &str, + strategy: ImportStrategy, + row_builder: fn(&str, ReviewAction, &Value) -> ReviewRow, +) -> Vec { + let imported = object_section(imported_config, section); + let current = object_section(current_config, section); + let mut keys = BTreeSet::new(); + keys.extend(imported.keys().cloned()); + if strategy == ImportStrategy::Replace { + keys.extend(current.keys().cloned()); + } + + keys.into_iter() + .filter_map(|name| { + let imported_entry = imported.get(&name); + let current_entry = current.get(&name); + let action = review_action(imported_entry, current_entry, strategy)?; + let entry = imported_entry.or(current_entry)?; + + Some(row_builder(&name, action, entry)) + }) + .collect() +} + +fn dimension_review_row(name: &str, action: ReviewAction, entry: &Value) -> ReviewRow { + ReviewRow { + name: name.to_string(), + action, + description: description_preview(entry), + scope: String::new(), + value: String::new(), + } +} + +fn default_config_review_row( + name: &str, + action: ReviewAction, + entry: &Value, +) -> ReviewRow { + ReviewRow { + name: name.to_string(), + action, + description: String::new(), + scope: String::new(), + value: default_config_value_preview(entry), + } +} + +fn flatten_overrides(config: &Value) -> BTreeMap { + let mut entries = BTreeMap::new(); + + for override_block in array_section(config, "overrides") { + let Some(object) = override_block.as_object() else { + continue; + }; + let context = object + .get("_context_") + .cloned() + .unwrap_or_else(|| Value::Object(Map::new())); + let context_key = canonical_json(&context); + let scope = context_preview(&context); + + for (name, value) in object { + if name == "_context_" { + continue; + } + + let mut raw = Map::new(); + raw.insert("_context_".to_string(), context.clone()); + raw.insert(name.clone(), value.clone()); + entries.insert( + format!("{context_key}::{name}"), + OverrideEntry { + name: name.clone(), + scope: scope.clone(), + value: value_preview(value), + raw: Value::Object(raw), + }, + ); + } + } + + entries +} + +fn build_override_review_rows( + imported_config: &Value, + current_config: &Value, + strategy: ImportStrategy, +) -> Vec { + let imported = flatten_overrides(imported_config); + let current = flatten_overrides(current_config); + let mut keys = BTreeSet::new(); + keys.extend(imported.keys().cloned()); + if strategy == ImportStrategy::Replace { + keys.extend(current.keys().cloned()); + } + + keys.into_iter() + .filter_map(|key| { + let imported_entry = imported.get(&key); + let current_entry = current.get(&key); + let imported_raw = imported_entry.map(|entry| &entry.raw); + let current_raw = current_entry.map(|entry| &entry.raw); + let action = review_action(imported_raw, current_raw, strategy)?; + let entry = imported_entry.or(current_entry)?; + + Some(ReviewRow { + name: entry.name.clone(), + action, + description: String::new(), + scope: entry.scope.clone(), + value: entry.value.clone(), + }) + }) + .collect() +} + +fn build_review_tables( + import_text: &str, + current_text: &str, + format: ImportFormat, + strategy: ImportStrategy, +) -> Result { + let imported_config = parse_config_text(import_text, format)?; + let current_config = parse_config_text(current_text, format)?; + + Ok(ReviewTables { + dimensions: build_map_review_rows( + &imported_config, + ¤t_config, + "dimensions", + strategy, + dimension_review_row, + ), + default_configs: build_map_review_rows( + &imported_config, + ¤t_config, + "default-configs", + strategy, + default_config_review_row, + ), + overrides: build_override_review_rows( + &imported_config, + ¤t_config, + strategy, + ), + }) +} + +#[derive(Clone, Debug)] +struct SummaryError { + section: &'static str, + id: String, + message: String, +} + +fn collect_errors(summary: &ImportSummary) -> Vec { + let mut errors = Vec::new(); + for (section, report) in [ + ("Dimensions", &summary.dimensions), + ("Default Config", &summary.default_configs), + ("Overrides", &summary.contexts), + ] { + for error in &report.errors { + errors.push(SummaryError { + section, + id: error.id.clone(), + message: error.message.clone(), + }); + } + } + errors +} + +fn metric_text(value: usize, label: &str) -> String { + format!("{value} {label}") +} + +#[component] +fn SummaryStripItem( + #[prop(into)] title: String, + #[prop(into)] icon: String, + report: ImportEntityReport, +) -> impl IntoView { + view! { +

+ + + +
+
{title}
+
+ + {report.created} + " created" + + + {report.updated} + " updated" + + + {report.skipped} + " skipped" + + + {report.deleted} + " deleted" + +
+
+
+ } +} + +#[component] +fn SummaryStrip(summary: ImportSummary) -> impl IntoView { + view! { +
+ + + +
+ } +} + +#[component] +fn ImportSummaryPanel( + summary: ImportSummary, + #[prop(into)] heading: String, + #[prop(into, default = String::new())] version_href: String, +) -> impl IntoView { + let total_changes = summary.total_changes(); + let total_deleted = summary.total_deleted(); + let total_errors = summary.total_errors(); + let has_deleted = total_deleted > 0; + let has_errors = total_errors > 0; + let strategy = summary.strategy.clone(); + let config_version = summary.config_version.clone(); + let errors = StoredValue::new(collect_errors(&summary)); + + view! { +
+
+
+

{heading}

+
+ {strategy} + + {if summary.dry_run { "Preview" } else { "Applied" }} + + + {metric_text(total_changes, "total")} + + + + {metric_text(total_deleted, "deleted")} + + + + + {metric_text(total_errors, "errors")} + + +
+
+ + + "Open Config Version" + + + +
+ + +
+
+ + "Errors" +
+
+ +
+ {format!("{}: {}", error.section, error.id)} +
+
{error.message}
+
+ } + } + /> +
+ +
+
+ } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum ReviewTableKind { + Dimensions, + DefaultConfig, + Overrides, +} + +#[component] +fn ActionBadge(action: ReviewAction) -> impl IntoView { + view! { + + {action.label()} + + } +} + +#[component] +fn ReviewTableSection( + #[prop(into)] title: String, + #[prop(into)] icon: String, + rows: Vec, + kind: ReviewTableKind, +) -> impl IntoView { + let row_count = rows.len(); + let rows = StoredValue::new(rows); + + view! { +
+
+
+ +

+ {format!("{title} ({row_count})")} +

+
+
+
+ + + + + + {match kind { + ReviewTableKind::Dimensions => view! { + + }.into_view(), + ReviewTableKind::DefaultConfig => view! { + + }.into_view(), + ReviewTableKind::Overrides => view! { + <> + + + + }.into_view(), + }} + + + + + + + } + } + > + view! { + + + + + + }.into_view(), + ReviewTableKind::DefaultConfig => view! { + + + + + + }.into_view(), + ReviewTableKind::Overrides => view! { + + + + + + + }.into_view(), + } + } + /> + + +
"Name""Action""Description""Value""Scope""Value"
+ "No changes in this section." +
{row.name}{row.description}
{row.name}{row.value}
{row.name}{row.scope}{row.value}
+
+
+ {format!("Showing {row_count} of {row_count}")} +
+
+ } +} + +#[component] +fn ReviewTablesPanel(tables: ReviewTables) -> impl IntoView { + view! { +
+ + + +
+ } +} + +#[component] +pub fn ImportConfig() -> impl IntoView { + let workspace = use_context::>().unwrap(); + let org = use_context::>().unwrap(); + let base = StoredValue::new(use_url_base()); + + let file_name_rws = RwSignal::new(String::new()); + let file_size_rws = RwSignal::new(None::); + let file_text_rws = RwSignal::new(String::new()); + let format_rws = RwSignal::new(ImportFormat::Toml); + let strategy_rws = RwSignal::new(ImportStrategy::Upsert); + let continue_on_error_rws = RwSignal::new(false); + let tags_rws = RwSignal::new(String::new()); + let preview_rws = RwSignal::new(None::); + let applied_rws = RwSignal::new(None::); + let review_tables_rws = RwSignal::new(None::); + let preview_loading_rws = RwSignal::new(false); + let apply_loading_rws = RwSignal::new(false); + let show_confirm_rws = RwSignal::new(false); + let file_input_ref = create_node_ref::(); + + let clear_results = move || { + preview_rws.set(None); + applied_rws.set(None); + review_tables_rws.set(None); + show_confirm_rws.set(false); + }; + + let clear_file = move |_| { + file_name_rws.set(String::new()); + file_size_rws.set(None); + file_text_rws.set(String::new()); + clear_results(); + if let Some(input) = file_input_ref.get() { + input.set_value(""); + } + }; + + let open_file_picker = move |_| { + if let Some(input) = file_input_ref.get() { + input.click(); + } + }; + + let submit_import = Callback::new(move |dry_run: bool| { + let file_text = file_text_rws.get_untracked(); + if file_text.trim().is_empty() { + enqueue_alert( + "Choose a config file before importing.".to_string(), + AlertType::Error, + 3000, + ); + return; + } + + let workspace = workspace.get_untracked().0; + let org_id = org.get_untracked().0; + let format = format_rws.get_untracked(); + let strategy = strategy_rws.get_untracked(); + let on_error = if continue_on_error_rws.get_untracked() { + ImportOnError::Continue + } else { + ImportOnError::Abort + }; + let options = ImportOptions { + strategy, + on_error, + dry_run, + config_tags: tags_rws.get_untracked(), + }; + + if dry_run { + preview_loading_rws.set(true); + } else { + apply_loading_rws.set(true); + show_confirm_rws.set(false); + } + + spawn_local(async move { + let import_text = file_text.clone(); + let result = config_import::import_config( + file_text, format, options, &workspace, &org_id, + ) + .await; + + match result { + Ok(summary) if dry_run => { + match config_import::export_config(format, &workspace, &org_id).await + { + Ok(current_config) => { + match build_review_tables( + &import_text, + ¤t_config, + format, + strategy, + ) { + Ok(tables) => review_tables_rws.set(Some(tables)), + Err(error) => { + review_tables_rws.set(None); + enqueue_alert(error, AlertType::Error, 5000); + } + } + } + Err(error) => { + review_tables_rws.set(None); + enqueue_alert(error, AlertType::Error, 5000); + } + } + preview_rws.set(Some(summary)); + enqueue_alert( + "Import preview is ready.".to_string(), + AlertType::Success, + 3000, + ); + } + Ok(summary) => { + preview_rws.set(None); + review_tables_rws.set(None); + applied_rws.set(Some(summary)); + enqueue_alert( + "Config import applied.".to_string(), + AlertType::Success, + 3000, + ); + } + Err(error) => { + enqueue_alert(error, AlertType::Error, 5000); + } + } + + if dry_run { + preview_loading_rws.set(false); + } else { + apply_loading_rws.set(false); + } + }); + }); + + let on_file_change = + move |ev: Event| { + let input = event_target::(&ev); + let Some(file) = input.files().and_then(|files| files.get(0)) else { + return; + }; + + let file_name = file.name(); + file_name_rws.set(file_name.clone()); + file_size_rws.set(Some(file.size() as u64)); + format_rws.set(ImportFormat::from_file_name(&file_name)); + file_text_rws.set(String::new()); + clear_results(); + + let Ok(reader) = FileReader::new() else { + enqueue_alert( + "Unable to read the selected file.".to_string(), + AlertType::Error, + 3000, + ); + return; + }; + let reader_for_load = reader.clone(); + let onload = Closure::::new(move |_| match reader_for_load + .result() + .ok() + .and_then(|value| value.as_string()) + { + Some(text) => file_text_rws.set(text), + None => enqueue_alert( + "Unable to read the selected file as text.".to_string(), + AlertType::Error, + 3000, + ), + }); + + reader.set_onload(Some(onload.as_ref().unchecked_ref())); + let file_blob: &web_sys::Blob = file.as_ref(); + if reader.read_as_text(file_blob).is_err() { + enqueue_alert( + "Unable to start reading the selected file.".to_string(), + AlertType::Error, + 3000, + ); + } + onload.forget(); + }; + + let apply_preview = move |_| { + let requires_confirmation = preview_rws.with(|summary| { + summary + .as_ref() + .map(|summary| { + strategy_rws.get_untracked() == ImportStrategy::Replace + || summary.total_deleted() > 0 + }) + .unwrap_or(false) + }); + + if requires_confirmation { + show_confirm_rws.set(true); + } else { + submit_import.call(false); + } + }; + + view! { +
+
+
+
+

"Import Config"

+

+ "Import dimensions, default config, and overrides from a Superposition config file." +

+
+
+ +
+
+
+ + +
+
+ + + +
+
+ {move || { + if file_name_rws.with(String::is_empty) { + "No file selected".to_string() + } else { + file_name_rws.get() + } + }} +
+
+ "Choose a Superposition config file." + } + > + + {move || { + file_size_rws + .get() + .map(format_file_size) + .unwrap_or_default() + }} + + +
+
+
+
+ + +
+
+
+ +
+ +
+ + + +
+
+ +
+ +
+ "Advanced Options" +
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ + + {move || { + preview_rws + .get() + .map(|_| { + view! { +
+
+
+
+

+ "Review Changes" +

+ + + {move || file_name_rws.get()} + + +
+
+ + {move || { + review_tables_rws + .get() + .map(|tables| { + view! { + + } + }) + }} + +
+
+ } + }) + }} +
+ + + {move || { + applied_rws + .get() + .map(|summary| { + let href = summary.config_version.as_ref().map(|version| { + format!( + "{}/admin/{}/{}/config/versions/{}", + base.get_value(), + org.get_untracked().0, + workspace.get_untracked().0, + version, + ) + }); + view! { +
+
+ +
+
+ } + }) + }} +
+ + + +
+
+
+ +
+
"Confirm workspace changes"
+
+ {move || { + let deleted = preview_rws + .with(|summary| { + summary + .as_ref() + .map(ImportSummary::total_deleted) + .unwrap_or_default() + }); + if deleted > 0 { + format!( + "This import will delete {deleted} item(s) missing from the file.", + ) + } else { + "This import will apply the previewed replace operation." + .to_string() + } + }} +
+
+
+
+
+
+
+
+
+
+
+ } +} diff --git a/crates/frontend/src/types.rs b/crates/frontend/src/types.rs index ba6412b33..b98f00816 100644 --- a/crates/frontend/src/types.rs +++ b/crates/frontend/src/types.rs @@ -329,6 +329,8 @@ pub enum RouteSegment { Experiments, #[strum(serialize = "function")] Function, + #[strum(serialize = "import")] + Import, #[strum(serialize = "resolve")] Resolve, #[strum(serialize = "secrets")] diff --git a/crates/frontend/src/utils.rs b/crates/frontend/src/utils.rs index 69a5f52ed..b8338f431 100644 --- a/crates/frontend/src/utils.rs +++ b/crates/frontend/src/utils.rs @@ -304,6 +304,59 @@ where request_with_skip_error(url, method, body, headers, &[]).await } +pub async fn request_raw_body( + url: String, + method: reqwest::Method, + body: String, + headers: HeaderMap, +) -> Result { + let ssr_headers = use_context::>().flatten(); + let cookie = ssr_headers.and_then(|h| h.cookie.clone()); + + let mut request_builder = + HTTP_CLIENT.request(method, url).headers(headers).body(body); + + if let Some(cookie_value) = cookie { + request_builder = request_builder.header(reqwest::header::COOKIE, cookie_value); + } + + let response = request_builder + .send() + .await + .map_err(|err| err.to_string())?; + + let status = response.status(); + + if status.is_client_error() { + let error_msg = response + .json::() + .await + .map_or(String::from("Something went wrong"), |error| error.message); + logging::error!("{}", error_msg); + enqueue_alert(error_msg.clone(), AlertType::Error, 5000); + return Err(error_msg); + } + if status.is_server_error() { + if status == 512 { + enqueue_alert( + "Webhook Call Failed, Please Check the Logs.".to_owned(), + AlertType::Error, + 5000, + ); + } else { + let error_msg = response + .json::() + .await + .map_or(String::from("Something went wrong"), |error| error.message); + logging::error!("{}", error_msg); + enqueue_alert(error_msg.clone(), AlertType::Error, 5000); + return Err(error_msg); + } + } + + Ok(response) +} + pub fn unwrap_option_or_default_with_error(option: Option, default: T) -> T { option.unwrap_or_else(|| { enqueue_alert( diff --git a/crates/superposition_sdk/src/client/customize.rs b/crates/superposition_sdk/src/client/customize.rs index fee84257e..0b5da876d 100644 --- a/crates/superposition_sdk/src/client/customize.rs +++ b/crates/superposition_sdk/src/client/customize.rs @@ -83,6 +83,8 @@ + + diff --git a/crates/superposition_sdk/src/client/import_config_json.rs b/crates/superposition_sdk/src/client/import_config_json.rs index ca296b2c9..7618a0583 100644 --- a/crates/superposition_sdk/src/client/import_config_json.rs +++ b/crates/superposition_sdk/src/client/import_config_json.rs @@ -5,15 +5,13 @@ impl super::Client { /// - The fluent builder is configurable: /// - [`workspace_id(impl Into)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::workspace_id) / [`set_workspace_id(Option)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::set_workspace_id):
required: **true**
(undocumented)
/// - [`org_id(impl Into)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::org_id) / [`set_org_id(Option)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::set_org_id):
required: **true**
(undocumented)
- /// - [`mode(ImportMode)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::mode) / [`set_mode(Option)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::set_mode):
required: **false**
Whether to merge (default) or replace existing workspace config.
- /// - [`overwrite(bool)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::overwrite) / [`set_overwrite(Option)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::set_overwrite):
required: **false**
When false, entities that already exist are skipped instead of updated. Defaults to true.
+ /// - [`strategy(ImportStrategy)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::strategy) / [`set_strategy(Option)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::set_strategy):
required: **false**
How the import applies file entities to the workspace. Defaults to upsert.
/// - [`on_error(ImportOnError)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::on_error) / [`set_on_error(Option)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::set_on_error):
required: **false**
Whether to abort (default) or continue on per-entity errors.
/// - [`dry_run(bool)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::dry_run) / [`set_dry_run(Option)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::set_dry_run):
required: **false**
When true, validates and summarises the import without persisting anything. Defaults to false.
- /// - [`value_merge(bool)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::value_merge) / [`set_value_merge(Option)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::set_value_merge):
required: **false**
When true, deep-merges object-valued default-configs with the existing value. Defaults to false.
/// - [`config_tags(impl Into)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::config_tags) / [`set_config_tags(Option)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::set_config_tags):
required: **false**
(undocumented)
/// - [`json_config(impl Into)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::json_config) / [`set_json_config(Option)`](crate::operation::import_config_json::builders::ImportConfigJsonFluentBuilder::set_json_config):
required: **true**
(undocumented)
/// - On success, responds with [`ImportConfigJsonOutput`](crate::operation::import_config_json::ImportConfigJsonOutput) with field(s): - /// - [`mode(String)`](crate::operation::import_config_json::ImportConfigJsonOutput::mode): (undocumented) + /// - [`strategy(String)`](crate::operation::import_config_json::ImportConfigJsonOutput::strategy): (undocumented) /// - [`dry_run(bool)`](crate::operation::import_config_json::ImportConfigJsonOutput::dry_run): (undocumented) /// - [`config_version(Option)`](crate::operation::import_config_json::ImportConfigJsonOutput::config_version): (undocumented) /// - [`dimensions(ImportEntityReport)`](crate::operation::import_config_json::ImportConfigJsonOutput::dimensions): Per-entity outcome counts for an import. diff --git a/crates/superposition_sdk/src/client/import_config_toml.rs b/crates/superposition_sdk/src/client/import_config_toml.rs index 8acfedd4d..f690b4dd4 100644 --- a/crates/superposition_sdk/src/client/import_config_toml.rs +++ b/crates/superposition_sdk/src/client/import_config_toml.rs @@ -5,15 +5,13 @@ impl super::Client { /// - The fluent builder is configurable: /// - [`workspace_id(impl Into)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::workspace_id) / [`set_workspace_id(Option)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::set_workspace_id):
required: **true**
(undocumented)
/// - [`org_id(impl Into)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::org_id) / [`set_org_id(Option)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::set_org_id):
required: **true**
(undocumented)
- /// - [`mode(ImportMode)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::mode) / [`set_mode(Option)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::set_mode):
required: **false**
Whether to merge (default) or replace existing workspace config.
- /// - [`overwrite(bool)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::overwrite) / [`set_overwrite(Option)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::set_overwrite):
required: **false**
When false, entities that already exist are skipped instead of updated. Defaults to true.
+ /// - [`strategy(ImportStrategy)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::strategy) / [`set_strategy(Option)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::set_strategy):
required: **false**
How the import applies file entities to the workspace. Defaults to upsert.
/// - [`on_error(ImportOnError)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::on_error) / [`set_on_error(Option)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::set_on_error):
required: **false**
Whether to abort (default) or continue on per-entity errors.
/// - [`dry_run(bool)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::dry_run) / [`set_dry_run(Option)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::set_dry_run):
required: **false**
When true, validates and summarises the import without persisting anything. Defaults to false.
- /// - [`value_merge(bool)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::value_merge) / [`set_value_merge(Option)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::set_value_merge):
required: **false**
When true, deep-merges object-valued default-configs with the existing value. Defaults to false.
/// - [`config_tags(impl Into)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::config_tags) / [`set_config_tags(Option)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::set_config_tags):
required: **false**
(undocumented)
/// - [`toml_config(impl Into)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::toml_config) / [`set_toml_config(Option)`](crate::operation::import_config_toml::builders::ImportConfigTomlFluentBuilder::set_toml_config):
required: **true**
(undocumented)
/// - On success, responds with [`ImportConfigTomlOutput`](crate::operation::import_config_toml::ImportConfigTomlOutput) with field(s): - /// - [`mode(String)`](crate::operation::import_config_toml::ImportConfigTomlOutput::mode): (undocumented) + /// - [`strategy(String)`](crate::operation::import_config_toml::ImportConfigTomlOutput::strategy): (undocumented) /// - [`dry_run(bool)`](crate::operation::import_config_toml::ImportConfigTomlOutput::dry_run): (undocumented) /// - [`config_version(Option)`](crate::operation::import_config_toml::ImportConfigTomlOutput::config_version): (undocumented) /// - [`dimensions(ImportEntityReport)`](crate::operation::import_config_toml::ImportConfigTomlOutput::dimensions): Per-entity outcome counts for an import. diff --git a/crates/superposition_sdk/src/operation/import_config_json/_import_config_json_input.rs b/crates/superposition_sdk/src/operation/import_config_json/_import_config_json_input.rs index 04e9b4b09..3efd5e3b2 100644 --- a/crates/superposition_sdk/src/operation/import_config_json/_import_config_json_input.rs +++ b/crates/superposition_sdk/src/operation/import_config_json/_import_config_json_input.rs @@ -7,16 +7,12 @@ pub struct ImportConfigJsonInput { pub workspace_id: ::std::option::Option<::std::string::String>, #[allow(missing_docs)] // documentation missing in model pub org_id: ::std::option::Option<::std::string::String>, - /// Whether to merge (default) or replace existing workspace config. - pub mode: ::std::option::Option, - /// When false, entities that already exist are skipped instead of updated. Defaults to true. - pub overwrite: ::std::option::Option, + /// How the import applies file entities to the workspace. Defaults to upsert. + pub strategy: ::std::option::Option, /// Whether to abort (default) or continue on per-entity errors. pub on_error: ::std::option::Option, /// When true, validates and summarises the import without persisting anything. Defaults to false. pub dry_run: ::std::option::Option, - /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. - pub value_merge: ::std::option::Option, #[allow(missing_docs)] // documentation missing in model pub config_tags: ::std::option::Option<::std::string::String>, #[allow(missing_docs)] // documentation missing in model @@ -31,13 +27,9 @@ impl ImportConfigJsonInput { pub fn org_id(&self) -> ::std::option::Option<&str> { self.org_id.as_deref() } - /// Whether to merge (default) or replace existing workspace config. - pub fn mode(&self) -> ::std::option::Option<&crate::types::ImportMode> { - self.mode.as_ref() - } - /// When false, entities that already exist are skipped instead of updated. Defaults to true. - pub fn overwrite(&self) -> ::std::option::Option { - self.overwrite + /// How the import applies file entities to the workspace. Defaults to upsert. + pub fn strategy(&self) -> ::std::option::Option<&crate::types::ImportStrategy> { + self.strategy.as_ref() } /// Whether to abort (default) or continue on per-entity errors. pub fn on_error(&self) -> ::std::option::Option<&crate::types::ImportOnError> { @@ -47,10 +39,6 @@ impl ImportConfigJsonInput { pub fn dry_run(&self) -> ::std::option::Option { self.dry_run } - /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. - pub fn value_merge(&self) -> ::std::option::Option { - self.value_merge - } #[allow(missing_docs)] // documentation missing in model pub fn config_tags(&self) -> ::std::option::Option<&str> { self.config_tags.as_deref() @@ -73,11 +61,9 @@ impl ImportConfigJsonInput { pub struct ImportConfigJsonInputBuilder { pub(crate) workspace_id: ::std::option::Option<::std::string::String>, pub(crate) org_id: ::std::option::Option<::std::string::String>, - pub(crate) mode: ::std::option::Option, - pub(crate) overwrite: ::std::option::Option, + pub(crate) strategy: ::std::option::Option, pub(crate) on_error: ::std::option::Option, pub(crate) dry_run: ::std::option::Option, - pub(crate) value_merge: ::std::option::Option, pub(crate) config_tags: ::std::option::Option<::std::string::String>, pub(crate) json_config: ::std::option::Option<::std::string::String>, } @@ -110,31 +96,18 @@ impl ImportConfigJsonInputBuilder { pub fn get_org_id(&self) -> &::std::option::Option<::std::string::String> { &self.org_id } - /// Whether to merge (default) or replace existing workspace config. - pub fn mode(mut self, input: crate::types::ImportMode) -> Self { - self.mode = ::std::option::Option::Some(input); - self - } - /// Whether to merge (default) or replace existing workspace config. - pub fn set_mode(mut self, input: ::std::option::Option) -> Self { - self.mode = input; self - } - /// Whether to merge (default) or replace existing workspace config. - pub fn get_mode(&self) -> &::std::option::Option { - &self.mode - } - /// When false, entities that already exist are skipped instead of updated. Defaults to true. - pub fn overwrite(mut self, input: bool) -> Self { - self.overwrite = ::std::option::Option::Some(input); + /// How the import applies file entities to the workspace. Defaults to upsert. + pub fn strategy(mut self, input: crate::types::ImportStrategy) -> Self { + self.strategy = ::std::option::Option::Some(input); self } - /// When false, entities that already exist are skipped instead of updated. Defaults to true. - pub fn set_overwrite(mut self, input: ::std::option::Option) -> Self { - self.overwrite = input; self + /// How the import applies file entities to the workspace. Defaults to upsert. + pub fn set_strategy(mut self, input: ::std::option::Option) -> Self { + self.strategy = input; self } - /// When false, entities that already exist are skipped instead of updated. Defaults to true. - pub fn get_overwrite(&self) -> &::std::option::Option { - &self.overwrite + /// How the import applies file entities to the workspace. Defaults to upsert. + pub fn get_strategy(&self) -> &::std::option::Option { + &self.strategy } /// Whether to abort (default) or continue on per-entity errors. pub fn on_error(mut self, input: crate::types::ImportOnError) -> Self { @@ -162,19 +135,6 @@ impl ImportConfigJsonInputBuilder { pub fn get_dry_run(&self) -> &::std::option::Option { &self.dry_run } - /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. - pub fn value_merge(mut self, input: bool) -> Self { - self.value_merge = ::std::option::Option::Some(input); - self - } - /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. - pub fn set_value_merge(mut self, input: ::std::option::Option) -> Self { - self.value_merge = input; self - } - /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. - pub fn get_value_merge(&self) -> &::std::option::Option { - &self.value_merge - } #[allow(missing_docs)] // documentation missing in model pub fn config_tags(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { self.config_tags = ::std::option::Option::Some(input.into()); @@ -210,16 +170,12 @@ impl ImportConfigJsonInputBuilder { , org_id: self.org_id , - mode: self.mode - , - overwrite: self.overwrite + strategy: self.strategy , on_error: self.on_error , dry_run: self.dry_run , - value_merge: self.value_merge - , config_tags: self.config_tags , json_config: self.json_config diff --git a/crates/superposition_sdk/src/operation/import_config_json/_import_config_json_output.rs b/crates/superposition_sdk/src/operation/import_config_json/_import_config_json_output.rs index 545eb9796..eb197239e 100644 --- a/crates/superposition_sdk/src/operation/import_config_json/_import_config_json_output.rs +++ b/crates/superposition_sdk/src/operation/import_config_json/_import_config_json_output.rs @@ -5,7 +5,7 @@ #[derive(::std::clone::Clone, ::std::cmp::PartialEq, ::std::fmt::Debug)] pub struct ImportConfigJsonOutput { #[allow(missing_docs)] // documentation missing in model - pub mode: ::std::string::String, + pub strategy: ::std::string::String, #[allow(missing_docs)] // documentation missing in model pub dry_run: bool, #[allow(missing_docs)] // documentation missing in model @@ -19,8 +19,8 @@ pub struct ImportConfigJsonOutput { } impl ImportConfigJsonOutput { #[allow(missing_docs)] // documentation missing in model - pub fn mode(&self) -> &str { - use std::ops::Deref; self.mode.deref() + pub fn strategy(&self) -> &str { + use std::ops::Deref; self.strategy.deref() } #[allow(missing_docs)] // documentation missing in model pub fn dry_run(&self) -> bool { @@ -54,7 +54,7 @@ impl ImportConfigJsonOutput { #[derive(::std::clone::Clone, ::std::cmp::PartialEq, ::std::default::Default, ::std::fmt::Debug)] #[non_exhaustive] pub struct ImportConfigJsonOutputBuilder { - pub(crate) mode: ::std::option::Option<::std::string::String>, + pub(crate) strategy: ::std::option::Option<::std::string::String>, pub(crate) dry_run: ::std::option::Option, pub(crate) config_version: ::std::option::Option<::std::string::String>, pub(crate) dimensions: ::std::option::Option, @@ -64,17 +64,17 @@ pub struct ImportConfigJsonOutputBuilder { impl ImportConfigJsonOutputBuilder { #[allow(missing_docs)] // documentation missing in model /// This field is required. - pub fn mode(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { - self.mode = ::std::option::Option::Some(input.into()); + pub fn strategy(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { + self.strategy = ::std::option::Option::Some(input.into()); self } #[allow(missing_docs)] // documentation missing in model - pub fn set_mode(mut self, input: ::std::option::Option<::std::string::String>) -> Self { - self.mode = input; self + pub fn set_strategy(mut self, input: ::std::option::Option<::std::string::String>) -> Self { + self.strategy = input; self } #[allow(missing_docs)] // documentation missing in model - pub fn get_mode(&self) -> &::std::option::Option<::std::string::String> { - &self.mode + pub fn get_strategy(&self) -> &::std::option::Option<::std::string::String> { + &self.strategy } #[allow(missing_docs)] // documentation missing in model /// This field is required. @@ -147,7 +147,7 @@ impl ImportConfigJsonOutputBuilder { } /// Consumes the builder and constructs a [`ImportConfigJsonOutput`](crate::operation::import_config_json::ImportConfigJsonOutput). /// This method will fail if any of the following fields are not set: - /// - [`mode`](crate::operation::import_config_json::builders::ImportConfigJsonOutputBuilder::mode) + /// - [`strategy`](crate::operation::import_config_json::builders::ImportConfigJsonOutputBuilder::strategy) /// - [`dry_run`](crate::operation::import_config_json::builders::ImportConfigJsonOutputBuilder::dry_run) /// - [`dimensions`](crate::operation::import_config_json::builders::ImportConfigJsonOutputBuilder::dimensions) /// - [`default_configs`](crate::operation::import_config_json::builders::ImportConfigJsonOutputBuilder::default_configs) @@ -155,9 +155,9 @@ impl ImportConfigJsonOutputBuilder { pub fn build(self) -> ::std::result::Result { ::std::result::Result::Ok( crate::operation::import_config_json::ImportConfigJsonOutput { - mode: self.mode + strategy: self.strategy .ok_or_else(|| - ::aws_smithy_types::error::operation::BuildError::missing_field("mode", "mode was not specified but it is required when building ImportConfigJsonOutput") + ::aws_smithy_types::error::operation::BuildError::missing_field("strategy", "strategy was not specified but it is required when building ImportConfigJsonOutput") )? , dry_run: self.dry_run diff --git a/crates/superposition_sdk/src/operation/import_config_json/builders.rs b/crates/superposition_sdk/src/operation/import_config_json/builders.rs index 49a29a460..feb7925cd 100644 --- a/crates/superposition_sdk/src/operation/import_config_json/builders.rs +++ b/crates/superposition_sdk/src/operation/import_config_json/builders.rs @@ -124,33 +124,19 @@ impl ImportConfigJsonFluentBuilder { pub fn get_org_id(&self) -> &::std::option::Option<::std::string::String> { self.inner.get_org_id() } - /// Whether to merge (default) or replace existing workspace config. - pub fn mode(mut self, input: crate::types::ImportMode) -> Self { - self.inner = self.inner.mode(input); + /// How the import applies file entities to the workspace. Defaults to upsert. + pub fn strategy(mut self, input: crate::types::ImportStrategy) -> Self { + self.inner = self.inner.strategy(input); self } - /// Whether to merge (default) or replace existing workspace config. - pub fn set_mode(mut self, input: ::std::option::Option) -> Self { - self.inner = self.inner.set_mode(input); + /// How the import applies file entities to the workspace. Defaults to upsert. + pub fn set_strategy(mut self, input: ::std::option::Option) -> Self { + self.inner = self.inner.set_strategy(input); self } - /// Whether to merge (default) or replace existing workspace config. - pub fn get_mode(&self) -> &::std::option::Option { - self.inner.get_mode() - } - /// When false, entities that already exist are skipped instead of updated. Defaults to true. - pub fn overwrite(mut self, input: bool) -> Self { - self.inner = self.inner.overwrite(input); - self - } - /// When false, entities that already exist are skipped instead of updated. Defaults to true. - pub fn set_overwrite(mut self, input: ::std::option::Option) -> Self { - self.inner = self.inner.set_overwrite(input); - self - } - /// When false, entities that already exist are skipped instead of updated. Defaults to true. - pub fn get_overwrite(&self) -> &::std::option::Option { - self.inner.get_overwrite() + /// How the import applies file entities to the workspace. Defaults to upsert. + pub fn get_strategy(&self) -> &::std::option::Option { + self.inner.get_strategy() } /// Whether to abort (default) or continue on per-entity errors. pub fn on_error(mut self, input: crate::types::ImportOnError) -> Self { @@ -180,20 +166,6 @@ impl ImportConfigJsonFluentBuilder { pub fn get_dry_run(&self) -> &::std::option::Option { self.inner.get_dry_run() } - /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. - pub fn value_merge(mut self, input: bool) -> Self { - self.inner = self.inner.value_merge(input); - self - } - /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. - pub fn set_value_merge(mut self, input: ::std::option::Option) -> Self { - self.inner = self.inner.set_value_merge(input); - self - } - /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. - pub fn get_value_merge(&self) -> &::std::option::Option { - self.inner.get_value_merge() - } #[allow(missing_docs)] // documentation missing in model pub fn config_tags(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { self.inner = self.inner.config_tags(input.into()); diff --git a/crates/superposition_sdk/src/operation/import_config_toml/_import_config_toml_input.rs b/crates/superposition_sdk/src/operation/import_config_toml/_import_config_toml_input.rs index f14f1fcf1..58add37bc 100644 --- a/crates/superposition_sdk/src/operation/import_config_toml/_import_config_toml_input.rs +++ b/crates/superposition_sdk/src/operation/import_config_toml/_import_config_toml_input.rs @@ -7,16 +7,12 @@ pub struct ImportConfigTomlInput { pub workspace_id: ::std::option::Option<::std::string::String>, #[allow(missing_docs)] // documentation missing in model pub org_id: ::std::option::Option<::std::string::String>, - /// Whether to merge (default) or replace existing workspace config. - pub mode: ::std::option::Option, - /// When false, entities that already exist are skipped instead of updated. Defaults to true. - pub overwrite: ::std::option::Option, + /// How the import applies file entities to the workspace. Defaults to upsert. + pub strategy: ::std::option::Option, /// Whether to abort (default) or continue on per-entity errors. pub on_error: ::std::option::Option, /// When true, validates and summarises the import without persisting anything. Defaults to false. pub dry_run: ::std::option::Option, - /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. - pub value_merge: ::std::option::Option, #[allow(missing_docs)] // documentation missing in model pub config_tags: ::std::option::Option<::std::string::String>, #[allow(missing_docs)] // documentation missing in model @@ -31,13 +27,9 @@ impl ImportConfigTomlInput { pub fn org_id(&self) -> ::std::option::Option<&str> { self.org_id.as_deref() } - /// Whether to merge (default) or replace existing workspace config. - pub fn mode(&self) -> ::std::option::Option<&crate::types::ImportMode> { - self.mode.as_ref() - } - /// When false, entities that already exist are skipped instead of updated. Defaults to true. - pub fn overwrite(&self) -> ::std::option::Option { - self.overwrite + /// How the import applies file entities to the workspace. Defaults to upsert. + pub fn strategy(&self) -> ::std::option::Option<&crate::types::ImportStrategy> { + self.strategy.as_ref() } /// Whether to abort (default) or continue on per-entity errors. pub fn on_error(&self) -> ::std::option::Option<&crate::types::ImportOnError> { @@ -47,10 +39,6 @@ impl ImportConfigTomlInput { pub fn dry_run(&self) -> ::std::option::Option { self.dry_run } - /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. - pub fn value_merge(&self) -> ::std::option::Option { - self.value_merge - } #[allow(missing_docs)] // documentation missing in model pub fn config_tags(&self) -> ::std::option::Option<&str> { self.config_tags.as_deref() @@ -73,11 +61,9 @@ impl ImportConfigTomlInput { pub struct ImportConfigTomlInputBuilder { pub(crate) workspace_id: ::std::option::Option<::std::string::String>, pub(crate) org_id: ::std::option::Option<::std::string::String>, - pub(crate) mode: ::std::option::Option, - pub(crate) overwrite: ::std::option::Option, + pub(crate) strategy: ::std::option::Option, pub(crate) on_error: ::std::option::Option, pub(crate) dry_run: ::std::option::Option, - pub(crate) value_merge: ::std::option::Option, pub(crate) config_tags: ::std::option::Option<::std::string::String>, pub(crate) toml_config: ::std::option::Option<::std::string::String>, } @@ -110,31 +96,18 @@ impl ImportConfigTomlInputBuilder { pub fn get_org_id(&self) -> &::std::option::Option<::std::string::String> { &self.org_id } - /// Whether to merge (default) or replace existing workspace config. - pub fn mode(mut self, input: crate::types::ImportMode) -> Self { - self.mode = ::std::option::Option::Some(input); - self - } - /// Whether to merge (default) or replace existing workspace config. - pub fn set_mode(mut self, input: ::std::option::Option) -> Self { - self.mode = input; self - } - /// Whether to merge (default) or replace existing workspace config. - pub fn get_mode(&self) -> &::std::option::Option { - &self.mode - } - /// When false, entities that already exist are skipped instead of updated. Defaults to true. - pub fn overwrite(mut self, input: bool) -> Self { - self.overwrite = ::std::option::Option::Some(input); + /// How the import applies file entities to the workspace. Defaults to upsert. + pub fn strategy(mut self, input: crate::types::ImportStrategy) -> Self { + self.strategy = ::std::option::Option::Some(input); self } - /// When false, entities that already exist are skipped instead of updated. Defaults to true. - pub fn set_overwrite(mut self, input: ::std::option::Option) -> Self { - self.overwrite = input; self + /// How the import applies file entities to the workspace. Defaults to upsert. + pub fn set_strategy(mut self, input: ::std::option::Option) -> Self { + self.strategy = input; self } - /// When false, entities that already exist are skipped instead of updated. Defaults to true. - pub fn get_overwrite(&self) -> &::std::option::Option { - &self.overwrite + /// How the import applies file entities to the workspace. Defaults to upsert. + pub fn get_strategy(&self) -> &::std::option::Option { + &self.strategy } /// Whether to abort (default) or continue on per-entity errors. pub fn on_error(mut self, input: crate::types::ImportOnError) -> Self { @@ -162,19 +135,6 @@ impl ImportConfigTomlInputBuilder { pub fn get_dry_run(&self) -> &::std::option::Option { &self.dry_run } - /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. - pub fn value_merge(mut self, input: bool) -> Self { - self.value_merge = ::std::option::Option::Some(input); - self - } - /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. - pub fn set_value_merge(mut self, input: ::std::option::Option) -> Self { - self.value_merge = input; self - } - /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. - pub fn get_value_merge(&self) -> &::std::option::Option { - &self.value_merge - } #[allow(missing_docs)] // documentation missing in model pub fn config_tags(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { self.config_tags = ::std::option::Option::Some(input.into()); @@ -210,16 +170,12 @@ impl ImportConfigTomlInputBuilder { , org_id: self.org_id , - mode: self.mode - , - overwrite: self.overwrite + strategy: self.strategy , on_error: self.on_error , dry_run: self.dry_run , - value_merge: self.value_merge - , config_tags: self.config_tags , toml_config: self.toml_config diff --git a/crates/superposition_sdk/src/operation/import_config_toml/_import_config_toml_output.rs b/crates/superposition_sdk/src/operation/import_config_toml/_import_config_toml_output.rs index 36a83f51d..38a4997a5 100644 --- a/crates/superposition_sdk/src/operation/import_config_toml/_import_config_toml_output.rs +++ b/crates/superposition_sdk/src/operation/import_config_toml/_import_config_toml_output.rs @@ -5,7 +5,7 @@ #[derive(::std::clone::Clone, ::std::cmp::PartialEq, ::std::fmt::Debug)] pub struct ImportConfigTomlOutput { #[allow(missing_docs)] // documentation missing in model - pub mode: ::std::string::String, + pub strategy: ::std::string::String, #[allow(missing_docs)] // documentation missing in model pub dry_run: bool, #[allow(missing_docs)] // documentation missing in model @@ -19,8 +19,8 @@ pub struct ImportConfigTomlOutput { } impl ImportConfigTomlOutput { #[allow(missing_docs)] // documentation missing in model - pub fn mode(&self) -> &str { - use std::ops::Deref; self.mode.deref() + pub fn strategy(&self) -> &str { + use std::ops::Deref; self.strategy.deref() } #[allow(missing_docs)] // documentation missing in model pub fn dry_run(&self) -> bool { @@ -54,7 +54,7 @@ impl ImportConfigTomlOutput { #[derive(::std::clone::Clone, ::std::cmp::PartialEq, ::std::default::Default, ::std::fmt::Debug)] #[non_exhaustive] pub struct ImportConfigTomlOutputBuilder { - pub(crate) mode: ::std::option::Option<::std::string::String>, + pub(crate) strategy: ::std::option::Option<::std::string::String>, pub(crate) dry_run: ::std::option::Option, pub(crate) config_version: ::std::option::Option<::std::string::String>, pub(crate) dimensions: ::std::option::Option, @@ -64,17 +64,17 @@ pub struct ImportConfigTomlOutputBuilder { impl ImportConfigTomlOutputBuilder { #[allow(missing_docs)] // documentation missing in model /// This field is required. - pub fn mode(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { - self.mode = ::std::option::Option::Some(input.into()); + pub fn strategy(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { + self.strategy = ::std::option::Option::Some(input.into()); self } #[allow(missing_docs)] // documentation missing in model - pub fn set_mode(mut self, input: ::std::option::Option<::std::string::String>) -> Self { - self.mode = input; self + pub fn set_strategy(mut self, input: ::std::option::Option<::std::string::String>) -> Self { + self.strategy = input; self } #[allow(missing_docs)] // documentation missing in model - pub fn get_mode(&self) -> &::std::option::Option<::std::string::String> { - &self.mode + pub fn get_strategy(&self) -> &::std::option::Option<::std::string::String> { + &self.strategy } #[allow(missing_docs)] // documentation missing in model /// This field is required. @@ -147,7 +147,7 @@ impl ImportConfigTomlOutputBuilder { } /// Consumes the builder and constructs a [`ImportConfigTomlOutput`](crate::operation::import_config_toml::ImportConfigTomlOutput). /// This method will fail if any of the following fields are not set: - /// - [`mode`](crate::operation::import_config_toml::builders::ImportConfigTomlOutputBuilder::mode) + /// - [`strategy`](crate::operation::import_config_toml::builders::ImportConfigTomlOutputBuilder::strategy) /// - [`dry_run`](crate::operation::import_config_toml::builders::ImportConfigTomlOutputBuilder::dry_run) /// - [`dimensions`](crate::operation::import_config_toml::builders::ImportConfigTomlOutputBuilder::dimensions) /// - [`default_configs`](crate::operation::import_config_toml::builders::ImportConfigTomlOutputBuilder::default_configs) @@ -155,9 +155,9 @@ impl ImportConfigTomlOutputBuilder { pub fn build(self) -> ::std::result::Result { ::std::result::Result::Ok( crate::operation::import_config_toml::ImportConfigTomlOutput { - mode: self.mode + strategy: self.strategy .ok_or_else(|| - ::aws_smithy_types::error::operation::BuildError::missing_field("mode", "mode was not specified but it is required when building ImportConfigTomlOutput") + ::aws_smithy_types::error::operation::BuildError::missing_field("strategy", "strategy was not specified but it is required when building ImportConfigTomlOutput") )? , dry_run: self.dry_run diff --git a/crates/superposition_sdk/src/operation/import_config_toml/builders.rs b/crates/superposition_sdk/src/operation/import_config_toml/builders.rs index 169656a72..95936095b 100644 --- a/crates/superposition_sdk/src/operation/import_config_toml/builders.rs +++ b/crates/superposition_sdk/src/operation/import_config_toml/builders.rs @@ -124,33 +124,19 @@ impl ImportConfigTomlFluentBuilder { pub fn get_org_id(&self) -> &::std::option::Option<::std::string::String> { self.inner.get_org_id() } - /// Whether to merge (default) or replace existing workspace config. - pub fn mode(mut self, input: crate::types::ImportMode) -> Self { - self.inner = self.inner.mode(input); + /// How the import applies file entities to the workspace. Defaults to upsert. + pub fn strategy(mut self, input: crate::types::ImportStrategy) -> Self { + self.inner = self.inner.strategy(input); self } - /// Whether to merge (default) or replace existing workspace config. - pub fn set_mode(mut self, input: ::std::option::Option) -> Self { - self.inner = self.inner.set_mode(input); + /// How the import applies file entities to the workspace. Defaults to upsert. + pub fn set_strategy(mut self, input: ::std::option::Option) -> Self { + self.inner = self.inner.set_strategy(input); self } - /// Whether to merge (default) or replace existing workspace config. - pub fn get_mode(&self) -> &::std::option::Option { - self.inner.get_mode() - } - /// When false, entities that already exist are skipped instead of updated. Defaults to true. - pub fn overwrite(mut self, input: bool) -> Self { - self.inner = self.inner.overwrite(input); - self - } - /// When false, entities that already exist are skipped instead of updated. Defaults to true. - pub fn set_overwrite(mut self, input: ::std::option::Option) -> Self { - self.inner = self.inner.set_overwrite(input); - self - } - /// When false, entities that already exist are skipped instead of updated. Defaults to true. - pub fn get_overwrite(&self) -> &::std::option::Option { - self.inner.get_overwrite() + /// How the import applies file entities to the workspace. Defaults to upsert. + pub fn get_strategy(&self) -> &::std::option::Option { + self.inner.get_strategy() } /// Whether to abort (default) or continue on per-entity errors. pub fn on_error(mut self, input: crate::types::ImportOnError) -> Self { @@ -180,20 +166,6 @@ impl ImportConfigTomlFluentBuilder { pub fn get_dry_run(&self) -> &::std::option::Option { self.inner.get_dry_run() } - /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. - pub fn value_merge(mut self, input: bool) -> Self { - self.inner = self.inner.value_merge(input); - self - } - /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. - pub fn set_value_merge(mut self, input: ::std::option::Option) -> Self { - self.inner = self.inner.set_value_merge(input); - self - } - /// When true, deep-merges object-valued default-configs with the existing value. Defaults to false. - pub fn get_value_merge(&self) -> &::std::option::Option { - self.inner.get_value_merge() - } #[allow(missing_docs)] // documentation missing in model pub fn config_tags(mut self, input: impl ::std::convert::Into<::std::string::String>) -> Self { self.inner = self.inner.config_tags(input.into()); diff --git a/crates/superposition_sdk/src/protocol_serde/shape_import_config_json.rs b/crates/superposition_sdk/src/protocol_serde/shape_import_config_json.rs index 6d48cd5af..ae4e7807c 100644 --- a/crates/superposition_sdk/src/protocol_serde/shape_import_config_json.rs +++ b/crates/superposition_sdk/src/protocol_serde/shape_import_config_json.rs @@ -69,34 +69,21 @@ pub fn ser_import_config_json_headers( })?; builder = builder.header("x-org-id", header_value); } - if let ::std::option::Option::Some(inner_5) = &input.mode { + if let ::std::option::Option::Some(inner_5) = &input.strategy { let formatted_6 = inner_5.as_str(); let header_value = formatted_6; let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { - ::aws_smithy_types::error::operation::BuildError::invalid_field("mode", format!( + ::aws_smithy_types::error::operation::BuildError::invalid_field("strategy", format!( "`{}` cannot be used as a header value: {}", &header_value, err )) })?; - builder = builder.header("x-import-mode", header_value); + builder = builder.header("x-import-strategy", header_value); } - if let ::std::option::Option::Some(inner_7) = &input.overwrite { - let mut encoder = ::aws_smithy_types::primitive::Encoder::from(*inner_7); - let formatted_8 = encoder.encode(); + if let ::std::option::Option::Some(inner_7) = &input.on_error { + let formatted_8 = inner_7.as_str(); let header_value = formatted_8; - let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { - ::aws_smithy_types::error::operation::BuildError::invalid_field("overwrite", format!( - "`{}` cannot be used as a header value: {}", - &header_value, - err - )) - })?; - builder = builder.header("x-import-overwrite", header_value); - } - if let ::std::option::Option::Some(inner_9) = &input.on_error { - let formatted_10 = inner_9.as_str(); - let header_value = formatted_10; let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { ::aws_smithy_types::error::operation::BuildError::invalid_field("on_error", format!( "`{}` cannot be used as a header value: {}", @@ -106,10 +93,10 @@ pub fn ser_import_config_json_headers( })?; builder = builder.header("x-import-on-error", header_value); } - if let ::std::option::Option::Some(inner_11) = &input.dry_run { - let mut encoder = ::aws_smithy_types::primitive::Encoder::from(*inner_11); - let formatted_12 = encoder.encode(); - let header_value = formatted_12; + if let ::std::option::Option::Some(inner_9) = &input.dry_run { + let mut encoder = ::aws_smithy_types::primitive::Encoder::from(*inner_9); + let formatted_10 = encoder.encode(); + let header_value = formatted_10; let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { ::aws_smithy_types::error::operation::BuildError::invalid_field("dry_run", format!( "`{}` cannot be used as a header value: {}", @@ -119,22 +106,9 @@ pub fn ser_import_config_json_headers( })?; builder = builder.header("x-import-dry-run", header_value); } - if let ::std::option::Option::Some(inner_13) = &input.value_merge { - let mut encoder = ::aws_smithy_types::primitive::Encoder::from(*inner_13); - let formatted_14 = encoder.encode(); - let header_value = formatted_14; - let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { - ::aws_smithy_types::error::operation::BuildError::invalid_field("value_merge", format!( - "`{}` cannot be used as a header value: {}", - &header_value, - err - )) - })?; - builder = builder.header("x-import-value-merge", header_value); - } - if let ::std::option::Option::Some(inner_15) = &input.config_tags { - let formatted_16 = inner_15.as_str(); - let header_value = formatted_16; + if let ::std::option::Option::Some(inner_11) = &input.config_tags { + let formatted_12 = inner_11.as_str(); + let header_value = formatted_12; let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { ::aws_smithy_types::error::operation::BuildError::invalid_field("config_tags", format!( "`{}` cannot be used as a header value: {}", @@ -185,8 +159,8 @@ pub(crate) fn de_import_config_json(value: &[u8], mut builder: crate::operation: ::aws_smithy_json::deserialize::token::expect_bool_or_null(tokens.next())? ); } - "mode" => { - builder = builder.set_mode( + "strategy" => { + builder = builder.set_strategy( ::aws_smithy_json::deserialize::token::expect_string_or_null(tokens.next())?.map(|s| s.to_unescaped().map(|u| u.into_owned() diff --git a/crates/superposition_sdk/src/protocol_serde/shape_import_config_toml.rs b/crates/superposition_sdk/src/protocol_serde/shape_import_config_toml.rs index 00cea09fa..777a21cb1 100644 --- a/crates/superposition_sdk/src/protocol_serde/shape_import_config_toml.rs +++ b/crates/superposition_sdk/src/protocol_serde/shape_import_config_toml.rs @@ -69,34 +69,21 @@ pub fn ser_import_config_toml_headers( })?; builder = builder.header("x-org-id", header_value); } - if let ::std::option::Option::Some(inner_5) = &input.mode { + if let ::std::option::Option::Some(inner_5) = &input.strategy { let formatted_6 = inner_5.as_str(); let header_value = formatted_6; let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { - ::aws_smithy_types::error::operation::BuildError::invalid_field("mode", format!( + ::aws_smithy_types::error::operation::BuildError::invalid_field("strategy", format!( "`{}` cannot be used as a header value: {}", &header_value, err )) })?; - builder = builder.header("x-import-mode", header_value); + builder = builder.header("x-import-strategy", header_value); } - if let ::std::option::Option::Some(inner_7) = &input.overwrite { - let mut encoder = ::aws_smithy_types::primitive::Encoder::from(*inner_7); - let formatted_8 = encoder.encode(); + if let ::std::option::Option::Some(inner_7) = &input.on_error { + let formatted_8 = inner_7.as_str(); let header_value = formatted_8; - let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { - ::aws_smithy_types::error::operation::BuildError::invalid_field("overwrite", format!( - "`{}` cannot be used as a header value: {}", - &header_value, - err - )) - })?; - builder = builder.header("x-import-overwrite", header_value); - } - if let ::std::option::Option::Some(inner_9) = &input.on_error { - let formatted_10 = inner_9.as_str(); - let header_value = formatted_10; let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { ::aws_smithy_types::error::operation::BuildError::invalid_field("on_error", format!( "`{}` cannot be used as a header value: {}", @@ -106,10 +93,10 @@ pub fn ser_import_config_toml_headers( })?; builder = builder.header("x-import-on-error", header_value); } - if let ::std::option::Option::Some(inner_11) = &input.dry_run { - let mut encoder = ::aws_smithy_types::primitive::Encoder::from(*inner_11); - let formatted_12 = encoder.encode(); - let header_value = formatted_12; + if let ::std::option::Option::Some(inner_9) = &input.dry_run { + let mut encoder = ::aws_smithy_types::primitive::Encoder::from(*inner_9); + let formatted_10 = encoder.encode(); + let header_value = formatted_10; let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { ::aws_smithy_types::error::operation::BuildError::invalid_field("dry_run", format!( "`{}` cannot be used as a header value: {}", @@ -119,22 +106,9 @@ pub fn ser_import_config_toml_headers( })?; builder = builder.header("x-import-dry-run", header_value); } - if let ::std::option::Option::Some(inner_13) = &input.value_merge { - let mut encoder = ::aws_smithy_types::primitive::Encoder::from(*inner_13); - let formatted_14 = encoder.encode(); - let header_value = formatted_14; - let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { - ::aws_smithy_types::error::operation::BuildError::invalid_field("value_merge", format!( - "`{}` cannot be used as a header value: {}", - &header_value, - err - )) - })?; - builder = builder.header("x-import-value-merge", header_value); - } - if let ::std::option::Option::Some(inner_15) = &input.config_tags { - let formatted_16 = inner_15.as_str(); - let header_value = formatted_16; + if let ::std::option::Option::Some(inner_11) = &input.config_tags { + let formatted_12 = inner_11.as_str(); + let header_value = formatted_12; let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { ::aws_smithy_types::error::operation::BuildError::invalid_field("config_tags", format!( "`{}` cannot be used as a header value: {}", @@ -185,8 +159,8 @@ pub(crate) fn de_import_config_toml(value: &[u8], mut builder: crate::operation: ::aws_smithy_json::deserialize::token::expect_bool_or_null(tokens.next())? ); } - "mode" => { - builder = builder.set_mode( + "strategy" => { + builder = builder.set_strategy( ::aws_smithy_json::deserialize::token::expect_string_or_null(tokens.next())?.map(|s| s.to_unescaped().map(|u| u.into_owned() diff --git a/crates/superposition_sdk/src/serde_util.rs b/crates/superposition_sdk/src/serde_util.rs index 23c67fe75..c8b709d6c 100644 --- a/crates/superposition_sdk/src/serde_util.rs +++ b/crates/superposition_sdk/src/serde_util.rs @@ -585,7 +585,7 @@ if builder.enable_change_reason_validation.is_none() { builder.enable_change_rea } pub(crate) fn import_config_json_output_output_correct_errors(mut builder: crate::operation::import_config_json::builders::ImportConfigJsonOutputBuilder) -> crate::operation::import_config_json::builders::ImportConfigJsonOutputBuilder { - if builder.mode.is_none() { builder.mode = Some(Default::default()) } + if builder.strategy.is_none() { builder.strategy = Some(Default::default()) } if builder.dry_run.is_none() { builder.dry_run = Some(Default::default()) } if builder.dimensions.is_none() { builder.dimensions = { let builder = crate::types::builders::ImportEntityReportBuilder::default(); crate::serde_util::import_entity_report_correct_errors(builder).build().ok() } } if builder.default_configs.is_none() { builder.default_configs = { let builder = crate::types::builders::ImportEntityReportBuilder::default(); crate::serde_util::import_entity_report_correct_errors(builder).build().ok() } } @@ -594,7 +594,7 @@ if builder.contexts.is_none() { builder.contexts = { let builder = crate::types: } pub(crate) fn import_config_toml_output_output_correct_errors(mut builder: crate::operation::import_config_toml::builders::ImportConfigTomlOutputBuilder) -> crate::operation::import_config_toml::builders::ImportConfigTomlOutputBuilder { - if builder.mode.is_none() { builder.mode = Some(Default::default()) } + if builder.strategy.is_none() { builder.strategy = Some(Default::default()) } if builder.dry_run.is_none() { builder.dry_run = Some(Default::default()) } if builder.dimensions.is_none() { builder.dimensions = { let builder = crate::types::builders::ImportEntityReportBuilder::default(); crate::serde_util::import_entity_report_correct_errors(builder).build().ok() } } if builder.default_configs.is_none() { builder.default_configs = { let builder = crate::types::builders::ImportEntityReportBuilder::default(); crate::serde_util::import_entity_report_correct_errors(builder).build().ok() } } @@ -1274,6 +1274,12 @@ if builder.enable_change_reason_validation.is_none() { builder.enable_change_rea builder } +pub(crate) fn import_error_item_correct_errors(mut builder: crate::types::builders::ImportErrorItemBuilder) -> crate::types::builders::ImportErrorItemBuilder { + if builder.id.is_none() { builder.id = Some(Default::default()) } +if builder.message.is_none() { builder.message = Some(Default::default()) } + builder + } + pub(crate) fn resolve_explanation_timeline_item_correct_errors(mut builder: crate::types::builders::ResolveExplanationTimelineItemBuilder) -> crate::types::builders::ResolveExplanationTimelineItemBuilder { if builder.context_id.is_none() { builder.context_id = Some(Default::default()) } if builder.condition.is_none() { builder.condition = Some(Default::default()) } @@ -1282,9 +1288,3 @@ if builder.value_before.is_none() { builder.value_before = Some(Default::default if builder.value_after.is_none() { builder.value_after = Some(Default::default()) } builder } - -pub(crate) fn import_error_item_correct_errors(mut builder: crate::types::builders::ImportErrorItemBuilder) -> crate::types::builders::ImportErrorItemBuilder { - if builder.id.is_none() { builder.id = Some(Default::default()) } -if builder.message.is_none() { builder.message = Some(Default::default()) } - builder - } diff --git a/crates/superposition_sdk/src/types.rs b/crates/superposition_sdk/src/types.rs index cbdac46db..e5c4b566a 100644 --- a/crates/superposition_sdk/src/types.rs +++ b/crates/superposition_sdk/src/types.rs @@ -121,7 +121,7 @@ pub use crate::types::_import_error_item::ImportErrorItem; pub use crate::types::_import_on_error::ImportOnError; -pub use crate::types::_import_mode::ImportMode; +pub use crate::types::_import_strategy::ImportStrategy; mod _audit_action; @@ -191,10 +191,10 @@ mod _import_entity_report; mod _import_error_item; -mod _import_mode; - mod _import_on_error; +mod _import_strategy; + mod _list_versions_member; mod _merge_strategy; diff --git a/crates/superposition_sdk/src/types/_import_mode.rs b/crates/superposition_sdk/src/types/_import_strategy.rs similarity index 59% rename from crates/superposition_sdk/src/types/_import_mode.rs rename to crates/superposition_sdk/src/types/_import_strategy.rs index d916c5e61..e7d71b738 100644 --- a/crates/superposition_sdk/src/types/_import_mode.rs +++ b/crates/superposition_sdk/src/types/_import_strategy.rs @@ -1,6 +1,6 @@ // Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT. -/// When writing a match expression against `ImportMode`, it is important to ensure +/// When writing a match expression against `ImportStrategy`, it is important to ensure /// your code is forward-compatible. That is, if a match arm handles a case for a /// feature that is supported by the service but has not been represented as an enum /// variant in a current version of SDK, your code should continue to work when you @@ -10,80 +10,85 @@ /// Here is an example of how you can make a match expression forward-compatible: /// /// ```text -/// # let importmode = unimplemented!(); -/// match importmode { -/// ImportMode::Merge => { /* ... */ }, -/// ImportMode::Replace => { /* ... */ }, +/// # let importstrategy = unimplemented!(); +/// match importstrategy { +/// ImportStrategy::CreateOnly => { /* ... */ }, +/// ImportStrategy::Replace => { /* ... */ }, +/// ImportStrategy::Upsert => { /* ... */ }, /// other @ _ if other.as_str() == "NewFeature" => { /* handles a case for `NewFeature` */ }, /// _ => { /* ... */ }, /// } /// ``` -/// The above code demonstrates that when `importmode` represents +/// The above code demonstrates that when `importstrategy` represents /// `NewFeature`, the execution path will lead to the second last match arm, -/// even though the enum does not contain a variant `ImportMode::NewFeature` +/// even though the enum does not contain a variant `ImportStrategy::NewFeature` /// in the current version of SDK. The reason is that the variable `other`, /// created by the `@` operator, is bound to -/// `ImportMode::Unknown(UnknownVariantValue("NewFeature".to_owned()))` +/// `ImportStrategy::Unknown(UnknownVariantValue("NewFeature".to_owned()))` /// and calling `as_str` on it yields `"NewFeature"`. /// This match expression is forward-compatible when executed with a newer -/// version of SDK where the variant `ImportMode::NewFeature` is defined. -/// Specifically, when `importmode` represents `NewFeature`, +/// version of SDK where the variant `ImportStrategy::NewFeature` is defined. +/// Specifically, when `importstrategy` represents `NewFeature`, /// the execution path will hit the second last match arm as before by virtue of -/// calling `as_str` on `ImportMode::NewFeature` also yielding `"NewFeature"`. +/// calling `as_str` on `ImportStrategy::NewFeature` also yielding `"NewFeature"`. /// /// Explicitly matching on the `Unknown` variant should /// be avoided for two reasons: /// - The inner data `UnknownVariantValue` is opaque, and no further information can be extracted. /// - It might inadvertently shadow other intended match arms. /// -/// How an import treats workspace entities that are not present in the imported file. +/// How an import applies file entities to the workspace. #[non_exhaustive] #[derive(::std::clone::Clone, ::std::cmp::Eq, ::std::cmp::Ord, ::std::cmp::PartialEq, ::std::cmp::PartialOrd, ::std::fmt::Debug, ::std::hash::Hash)] -pub enum ImportMode { - /// Upsert the entities in the file and leave everything else untouched. - Merge, - /// Mirror the file: additionally delete dimensions, default-configs and contexts that are absent from it. +pub enum ImportStrategy { + /// Create entities that are present in the file but missing from the workspace. Existing entities are skipped. Nothing is deleted. + CreateOnly, + /// Mirror the file: create missing entities, update existing entities, and delete dimensions, default-configs and contexts that are absent from it. Replace, + /// Create missing entities and update existing entities from the file. Entities absent from the file are left untouched. + Upsert, /// `Unknown` contains new variants that have been added since this code was generated. #[deprecated(note = "Don't directly match on `Unknown`. See the docs on this enum for the correct way to handle unknown variants.")] Unknown(crate::primitives::sealed_enum_unknown::UnknownVariantValue) } -impl ::std::convert::From<&str> for ImportMode { +impl ::std::convert::From<&str> for ImportStrategy { fn from(s: &str) -> Self { match s { - "merge" => ImportMode::Merge, -"replace" => ImportMode::Replace, -other => ImportMode::Unknown(crate::primitives::sealed_enum_unknown::UnknownVariantValue(other.to_owned())) + "create_only" => ImportStrategy::CreateOnly, +"replace" => ImportStrategy::Replace, +"upsert" => ImportStrategy::Upsert, +other => ImportStrategy::Unknown(crate::primitives::sealed_enum_unknown::UnknownVariantValue(other.to_owned())) } } } -impl ::std::str::FromStr for ImportMode { +impl ::std::str::FromStr for ImportStrategy { type Err = ::std::convert::Infallible; fn from_str(s: &str) -> ::std::result::Result::Err> { - ::std::result::Result::Ok(ImportMode::from(s)) + ::std::result::Result::Ok(ImportStrategy::from(s)) } } -impl ImportMode { +impl ImportStrategy { /// Returns the `&str` value of the enum member. pub fn as_str(&self) -> &str { match self { - ImportMode::Merge => "merge", - ImportMode::Replace => "replace", - ImportMode::Unknown(value) => value.as_str() + ImportStrategy::CreateOnly => "create_only", + ImportStrategy::Replace => "replace", + ImportStrategy::Upsert => "upsert", + ImportStrategy::Unknown(value) => value.as_str() } } /// Returns all the `&str` representations of the enum members. pub const fn values() -> &'static [&'static str] { - &["merge", "replace"] + &["create_only", "replace", "upsert"] } } -impl ::std::convert::AsRef for ImportMode { +impl ::std::convert::AsRef for ImportStrategy { fn as_ref(&self) -> &str { self.as_str() } } -impl ImportMode { +impl ImportStrategy { /// Parses the enum value while disallowing unknown variants. /// /// Unknown variants will result in an error. @@ -95,12 +100,13 @@ impl ImportMode { } } } -impl ::std::fmt::Display for ImportMode { +impl ::std::fmt::Display for ImportStrategy { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { match self { - ImportMode::Merge => write!(f, "merge"), -ImportMode::Replace => write!(f, "replace"), -ImportMode::Unknown(value) => write!(f, "{}", value) + ImportStrategy::CreateOnly => write!(f, "create_only"), +ImportStrategy::Replace => write!(f, "replace"), +ImportStrategy::Upsert => write!(f, "upsert"), +ImportStrategy::Unknown(value) => write!(f, "{}", value) } } } diff --git a/docs/docs/api/Superposition.openapi.json b/docs/docs/api/Superposition.openapi.json index 5b8a62622..5a8f2552f 100644 --- a/docs/docs/api/Superposition.openapi.json +++ b/docs/docs/api/Superposition.openapi.json @@ -341,14 +341,6 @@ "description": "When true, validates and summarises the import without persisting anything. Defaults to false." } }, - { - "name": "x-import-mode", - "in": "header", - "description": "Whether to merge (default) or replace existing workspace config.", - "schema": { - "$ref": "#/components/schemas/ImportMode" - } - }, { "name": "x-import-on-error", "in": "header", @@ -358,21 +350,11 @@ } }, { - "name": "x-import-overwrite", + "name": "x-import-strategy", "in": "header", - "description": "When false, entities that already exist are skipped instead of updated. Defaults to true.", + "description": "How the import applies file entities to the workspace. Defaults to upsert.", "schema": { - "type": "boolean", - "description": "When false, entities that already exist are skipped instead of updated. Defaults to true." - } - }, - { - "name": "x-import-value-merge", - "in": "header", - "description": "When true, deep-merges object-valued default-configs with the existing value. Defaults to false.", - "schema": { - "type": "boolean", - "description": "When true, deep-merges object-valued default-configs with the existing value. Defaults to false." + "$ref": "#/components/schemas/ImportStrategy" } }, { @@ -882,14 +864,6 @@ "description": "When true, validates and summarises the import without persisting anything. Defaults to false." } }, - { - "name": "x-import-mode", - "in": "header", - "description": "Whether to merge (default) or replace existing workspace config.", - "schema": { - "$ref": "#/components/schemas/ImportMode" - } - }, { "name": "x-import-on-error", "in": "header", @@ -899,21 +873,11 @@ } }, { - "name": "x-import-overwrite", + "name": "x-import-strategy", "in": "header", - "description": "When false, entities that already exist are skipped instead of updated. Defaults to true.", + "description": "How the import applies file entities to the workspace. Defaults to upsert.", "schema": { - "type": "boolean", - "description": "When false, entities that already exist are skipped instead of updated. Defaults to true." - } - }, - { - "name": "x-import-value-merge", - "in": "header", - "description": "When true, deep-merges object-valued default-configs with the existing value. Defaults to false.", - "schema": { - "type": "boolean", - "description": "When true, deep-merges object-valued default-configs with the existing value. Defaults to false." + "$ref": "#/components/schemas/ImportStrategy" } }, { @@ -10341,7 +10305,7 @@ "type": "object", "description": "Summary of what an import created, updated, skipped or deleted.", "properties": { - "mode": { + "strategy": { "type": "string" }, "dry_run": { @@ -10365,7 +10329,7 @@ "default_configs", "dimensions", "dry_run", - "mode" + "strategy" ] }, "ImportConfigTomlInputPayload": { @@ -10375,7 +10339,7 @@ "type": "object", "description": "Summary of what an import created, updated, skipped or deleted.", "properties": { - "mode": { + "strategy": { "type": "string" }, "dry_run": { @@ -10399,7 +10363,7 @@ "default_configs", "dimensions", "dry_run", - "mode" + "strategy" ] }, "ImportEntityReport": { @@ -10447,14 +10411,6 @@ "message" ] }, - "ImportMode": { - "type": "string", - "description": "How an import treats workspace entities that are not present in the imported file.", - "enum": [ - "merge", - "replace" - ] - }, "ImportOnError": { "type": "string", "description": "How an import reacts when an individual entity fails to apply.", @@ -10463,6 +10419,15 @@ "continue" ] }, + "ImportStrategy": { + "type": "string", + "description": "How an import applies file entities to the workspace.", + "enum": [ + "create_only", + "upsert", + "replace" + ] + }, "InternalServerErrorResponseContent": { "type": "object", "properties": { diff --git a/docs/docs/api/import-config-json.ParamsDetails.json b/docs/docs/api/import-config-json.ParamsDetails.json index 139c2e881..85c1c6b92 100644 --- a/docs/docs/api/import-config-json.ParamsDetails.json +++ b/docs/docs/api/import-config-json.ParamsDetails.json @@ -1 +1 @@ -{"parameters":[{"name":"x-config-tags","in":"header","schema":{"type":"string"}},{"name":"x-import-dry-run","in":"header","description":"When true, validates and summarises the import without persisting anything. Defaults to false.","schema":{"type":"boolean","description":"When true, validates and summarises the import without persisting anything. Defaults to false."}},{"name":"x-import-mode","in":"header","description":"Whether to merge (default) or replace existing workspace config.","schema":{"type":"string","description":"How an import treats workspace entities that are not present in the imported file.","enum":["merge","replace"],"title":"ImportMode"}},{"name":"x-import-on-error","in":"header","description":"Whether to abort (default) or continue on per-entity errors.","schema":{"type":"string","description":"How an import reacts when an individual entity fails to apply.","enum":["abort","continue"],"title":"ImportOnError"}},{"name":"x-import-overwrite","in":"header","description":"When false, entities that already exist are skipped instead of updated. Defaults to true.","schema":{"type":"boolean","description":"When false, entities that already exist are skipped instead of updated. Defaults to true."}},{"name":"x-import-value-merge","in":"header","description":"When true, deep-merges object-valued default-configs with the existing value. Defaults to false.","schema":{"type":"boolean","description":"When true, deep-merges object-valued default-configs with the existing value. Defaults to false."}},{"name":"x-org-id","in":"header","schema":{"type":"string"},"required":true},{"name":"x-workspace","in":"header","schema":{"type":"string"},"required":true}]} \ No newline at end of file +{"parameters":[{"name":"x-config-tags","in":"header","schema":{"type":"string"}},{"name":"x-import-dry-run","in":"header","description":"When true, validates and summarises the import without persisting anything. Defaults to false.","schema":{"type":"boolean","description":"When true, validates and summarises the import without persisting anything. Defaults to false."}},{"name":"x-import-on-error","in":"header","description":"Whether to abort (default) or continue on per-entity errors.","schema":{"type":"string","description":"How an import reacts when an individual entity fails to apply.","enum":["abort","continue"],"title":"ImportOnError"}},{"name":"x-import-strategy","in":"header","description":"How the import applies file entities to the workspace. Defaults to upsert.","schema":{"type":"string","description":"How an import applies file entities to the workspace.","enum":["create_only","upsert","replace"],"title":"ImportStrategy"}},{"name":"x-org-id","in":"header","schema":{"type":"string"},"required":true},{"name":"x-workspace","in":"header","schema":{"type":"string"},"required":true}]} \ No newline at end of file diff --git a/docs/docs/api/import-config-json.StatusCodes.json b/docs/docs/api/import-config-json.StatusCodes.json index de2664cca..9b151e3fb 100644 --- a/docs/docs/api/import-config-json.StatusCodes.json +++ b/docs/docs/api/import-config-json.StatusCodes.json @@ -1 +1 @@ -{"responses":{"200":{"description":"ImportConfigJson 200 response","content":{"application/json":{"schema":{"type":"object","description":"Summary of what an import created, updated, skipped or deleted.","properties":{"mode":{"type":"string"},"dry_run":{"type":"boolean"},"config_version":{"type":"string"},"dimensions":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"message":{"type":"string"}},"required":["id","message"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"},"default_configs":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"message":{"type":"string"}},"required":["id","message"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"},"contexts":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"message":{"type":"string"}},"required":["id","message"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"}},"required":["contexts","default_configs","dimensions","dry_run","mode"],"title":"ImportConfigJsonResponseContent"}}}},"500":{"description":"InternalServerError 500 response","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"title":"InternalServerErrorResponseContent"}}}}}} \ No newline at end of file +{"responses":{"200":{"description":"ImportConfigJson 200 response","content":{"application/json":{"schema":{"type":"object","description":"Summary of what an import created, updated, skipped or deleted.","properties":{"strategy":{"type":"string"},"dry_run":{"type":"boolean"},"config_version":{"type":"string"},"dimensions":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"message":{"type":"string"}},"required":["id","message"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"},"default_configs":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"message":{"type":"string"}},"required":["id","message"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"},"contexts":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"message":{"type":"string"}},"required":["id","message"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"}},"required":["contexts","default_configs","dimensions","dry_run","strategy"],"title":"ImportConfigJsonResponseContent"}}}},"500":{"description":"InternalServerError 500 response","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"title":"InternalServerErrorResponseContent"}}}}}} \ No newline at end of file diff --git a/docs/docs/api/import-config-json.api.mdx b/docs/docs/api/import-config-json.api.mdx index 7fbd7cf01..61f75d969 100644 --- a/docs/docs/api/import-config-json.api.mdx +++ b/docs/docs/api/import-config-json.api.mdx @@ -5,7 +5,7 @@ description: "Imports a full config from a JSON document, persisting dimensions, sidebar_label: "ImportConfigJson" hide_title: true hide_table_of_contents: true -api: eJztWEtPIzkQ/itWn2akBNCsuHCbx0rLSLMgwmoPCCGnXUkM3XaP7Sa0UP77VtnuV7qBLExWq9FcIG2X6/HVVy7bj4kAmxpZOKlVcpKc5oU2zjLOFmWWsVSrhVyyhdE5Dn2dnf3JhE7LHJSbsAKMldZJtWRC4pBFFXbCBCx4mblpWIuqlCA9Dh5Qr1Sox+KSDJgzXFmekmXGFw4Mu+eZFNxrdCtoTB0kk0SjNU6ip6Jx87O38NWi55PEwPcSrPukRZWcPCbeonL0kywfFhmXir5suoKc+/GqANRlnUGDqMFJl8GI8lNVlO6cV5nmItlsNsGWNICeOFMCDhTc8BwwBJucXD0mCj9Q0UPEYOr40qIBciBZARdg8OspR9BAR4P0zkyFqaamVAMl/ez9vQLFyKVJDSUE/G2Z59xIi58EbFDK1tKtdOm6ieSqciv8ccC+hDTiAs0WPLNwMObzXOsMuPqvPRnFKNcCXgYIrRrSlINZAnsX2fqeacMMIEtSYPAQfVhrc2cLGgqJHIWg4U/f1B96jTHUAToDHCNoFSI3pZMeBe4YN8CUxvgNWJyhMmnRAcEWMvP4gypzZFjinfek9x4n19vs/UZYjKKk1RSM0WZ3pPicIughRdUlVQkMSxdzNvXRVMwrtq9HCUFKCSUiD40qIe+lKHnGooEFl5nnAS+KrOoi4p3E79qzISRn6ncf9zgq92DWRrodCKQCCSfbKczQe1EF8viE2jtZFJg8qazDKaYXrCyoEkSf01Qm/7649uLEKDZYwCVMa8rtuP8IgCKssUzPbyGNesSgP1Dte7Y3ZecFf+AOtB9f+lhps5xKsfsmP2wiHWXNNvEGfdc0YgtsyWBJ/sPREf0ba/dtq2MoxeplsZpiF6WKk6lvwYe31HLHemlAd5CLmd/0KyLf2tO0qfiU9kUQk5qUk4awuMsIyICIivoKQwcAIjpZ8zv9GAbYJm+oTQ7psfHBYJg399RjtBpf35xjXg7qvN33sHWlOqcuUSrkyAJ9b0Iceh9j7ljAPWyO6UUHIgyjcxGZ0bkI1ehc2JU7U9wYXhG1HOSjgfb9lWIUqxys5cuxPPTIeJX4sqilB9uy35RP0ZPto9VVA1QbXgtCC9VQo0/KBdDvgI2v3ZtY5L8y+9Nktr5W/ErpT5LSbZ11fodF3Nur241/EnrDwEzb4i5id/scW9vGR3I82h1RwiiezcBg0/Cxs+Mf1CC3Gtpz6WkiGbozFgytoPsm4heiLsPFmX3jCm3QhZrwwevqStNdutDWO8TdCr8OA8A+gsNQFJRISEs8HVf+dmtzPCVVB7yQByvnik/cyvRjSauvrukYsz0PeA41jcB1q21G6AQAntbZIELjw/vD5eU589KMoziVeEhBfVbyZwCa90X5tGe7mPHiz9nxAj5nUi20VxpzNysx24i0jGuaQwieyz4cT49+mx4dk4eUjJx77sTTYOAvC6lk8bGj513nseP/+34T0e28xdAGajJyP1DvKh7QUPg2RBnph4xZEUdR4vERcwl/mWyzoeHvJRjiJP6850byOWFNJKwPzERXIS1NINXDlelp8J5y8w6qkdccfzOgVcT53Y3s/Y3m2RAGz0mvj+KtDym7+BmfdN7u5GveMHZxsPOa8hY+7OlFYacIOi8fb6f0vq79u0TSf6d4ZSzvLuL54z17yWxz5d+/qe6DQM9aEIjtf3pJOlqJnq5a9mOaQuE6UoNzy6Z7Ojg/m12i8Dw+q4ebd2L4ml4f8S/5sdn8A3gcgOU= +api: eJztWFFv2zgM/iuCnzYgTosd+tK3bXfAdcCuRdPDPQRFoVh0otaWfJLczAj834+UZceunTbotsMw7KW1ZYr8+JEixewiATYxsnBSq+g8usgLbZxlnKVllrFEq1SuWWp0jkufFpd/MaGTMgflZqwAY6V1Uq2ZkLhkUYWdMQEpLzMXN3tRlRKkx8EX1CsV6rG4JQPmDFeWJ2SZ8dSBYY88k4J7jW4Dnal5NIs0WuMkeiE6mB+9hU8Wkc8iA/+WYN0HLarofBd5i8rRI1k+KTIuFb3ZZAM59+tVAajLOoMGUYOTLoMJ5ReqKN0VrzLNRVTXdWNLGkAkzpSACwU3PAd0wUbny12k8AUVfQkcxI6vLRogANEGuACDb4eAoIGeBunBxMJUsSnVSMkwev9sQDGCNGuphIZ/W+Y5N9LiKxHbKGVb6Ta6dP1AclW5DT7M2e9NGHGDZinPLMynMK+0zoCr/xvJJEdaxWCMNi+ThJYNaeMrMv4mZOxbpo3PVKlKYJiUiCbGHJKuYl6xnaSgy5+hmT/1Fn1oHTSAiW7ZlmihVSXkoxQlz1gwkHKZeQ95UWQVGQJV5phNkQeJ7y2y6PZpql6qP7zfk6wgPCR/Xb3ACsHtBYRQSIxRKvGgeoj0hvBIZqvNgy14AsPYlIUF417P0ZE2e9QkSKuDO60ycq+x70sBnvZkgqhFy8WQKW3WsRTHH9BxAegp64B+hb5bWrEFllOwJP/u9JT+TZXqfZliKMXabSFfQgX0zCa+fJ7cU7mcqoN6dQ+JG8Vo4Q9sxXSK2ctdL14N+2KGgRfNg32QRQGCzpGADOuhoGgVhoo3BdPbbWMwxQOWuTsqc+MCU3uH0NW7R6oRWk3v7/rQy45d7U83lp5E54CHv1SYyyni79wcexD87lnAdFxhiGvKQXHwW2Bn8luga/JbU3t6n7gx3J9nB/mko0O8UkxylYO1fA2TDaiXkMvIH41WenSmfOm5QCRPW+OyI2rv3p6EPVVjjT4o10DPDTe+xtyFC8WvyP40kW2vhb9C+pOE9KnONr7jQzyo1fvCP9v3h5Gpfau7Dl3uY2hxtffmbLJLooRRPFuAwcbh/Wdn36hRDiP3bIg6T8ZwppyhHTQzIIeN12Uz/LDPXKENGoqIHxw5NprmoUJbD4i7Db6dNCR7D06ag0HEQlIajJWfUGyON+5qzgs53zhXfOBWJu9L2r28pevM0+/ADZhO4HavbUHshM5+UGfHCK2Pb4E3N1fMSzOO4nTMmxC0dyZ/D6Dv/mAeRnaMGS/+nB0v4GMmVaq90hC7RYnRRqZl2NNdRPB+9u4sPv0tPj0jhBSMnPvcCbfCJn9ZE0oWBtYBut7A+uPO4IHd3jxNRdRkBL9JvWW4pKHwfeNlSD/MmA3lKErsdhhL+NtkdU3LOLUbykl8fMTBkK+Ia0rC9uJM6SqkpQ+Y6n4AfIa8QzAfoJqYyNHZkmQjyvnjjXz3OftZF0Y/Cbzei68ZhI/B2BvJXwnym86lx0DuzcuvhPzmOrTAt+wlm93k+f1N9efSgbVGIHSf+IZ07CUGulrZ90kChetJjdpm3W9OV5eLGxRehV/mci1oj+FbGtXxL+Go6/8AMlIv9A== sidebar_class_name: "post api-method" info_path: docs/api/superposition custom_edit_url: null diff --git a/docs/docs/api/import-config-toml.ParamsDetails.json b/docs/docs/api/import-config-toml.ParamsDetails.json index 139c2e881..85c1c6b92 100644 --- a/docs/docs/api/import-config-toml.ParamsDetails.json +++ b/docs/docs/api/import-config-toml.ParamsDetails.json @@ -1 +1 @@ -{"parameters":[{"name":"x-config-tags","in":"header","schema":{"type":"string"}},{"name":"x-import-dry-run","in":"header","description":"When true, validates and summarises the import without persisting anything. Defaults to false.","schema":{"type":"boolean","description":"When true, validates and summarises the import without persisting anything. Defaults to false."}},{"name":"x-import-mode","in":"header","description":"Whether to merge (default) or replace existing workspace config.","schema":{"type":"string","description":"How an import treats workspace entities that are not present in the imported file.","enum":["merge","replace"],"title":"ImportMode"}},{"name":"x-import-on-error","in":"header","description":"Whether to abort (default) or continue on per-entity errors.","schema":{"type":"string","description":"How an import reacts when an individual entity fails to apply.","enum":["abort","continue"],"title":"ImportOnError"}},{"name":"x-import-overwrite","in":"header","description":"When false, entities that already exist are skipped instead of updated. Defaults to true.","schema":{"type":"boolean","description":"When false, entities that already exist are skipped instead of updated. Defaults to true."}},{"name":"x-import-value-merge","in":"header","description":"When true, deep-merges object-valued default-configs with the existing value. Defaults to false.","schema":{"type":"boolean","description":"When true, deep-merges object-valued default-configs with the existing value. Defaults to false."}},{"name":"x-org-id","in":"header","schema":{"type":"string"},"required":true},{"name":"x-workspace","in":"header","schema":{"type":"string"},"required":true}]} \ No newline at end of file +{"parameters":[{"name":"x-config-tags","in":"header","schema":{"type":"string"}},{"name":"x-import-dry-run","in":"header","description":"When true, validates and summarises the import without persisting anything. Defaults to false.","schema":{"type":"boolean","description":"When true, validates and summarises the import without persisting anything. Defaults to false."}},{"name":"x-import-on-error","in":"header","description":"Whether to abort (default) or continue on per-entity errors.","schema":{"type":"string","description":"How an import reacts when an individual entity fails to apply.","enum":["abort","continue"],"title":"ImportOnError"}},{"name":"x-import-strategy","in":"header","description":"How the import applies file entities to the workspace. Defaults to upsert.","schema":{"type":"string","description":"How an import applies file entities to the workspace.","enum":["create_only","upsert","replace"],"title":"ImportStrategy"}},{"name":"x-org-id","in":"header","schema":{"type":"string"},"required":true},{"name":"x-workspace","in":"header","schema":{"type":"string"},"required":true}]} \ No newline at end of file diff --git a/docs/docs/api/import-config-toml.StatusCodes.json b/docs/docs/api/import-config-toml.StatusCodes.json index d332a7ea8..b16aa8f06 100644 --- a/docs/docs/api/import-config-toml.StatusCodes.json +++ b/docs/docs/api/import-config-toml.StatusCodes.json @@ -1 +1 @@ -{"responses":{"200":{"description":"ImportConfigToml 200 response","content":{"application/json":{"schema":{"type":"object","description":"Summary of what an import created, updated, skipped or deleted.","properties":{"mode":{"type":"string"},"dry_run":{"type":"boolean"},"config_version":{"type":"string"},"dimensions":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"message":{"type":"string"}},"required":["id","message"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"},"default_configs":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"message":{"type":"string"}},"required":["id","message"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"},"contexts":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"message":{"type":"string"}},"required":["id","message"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"}},"required":["contexts","default_configs","dimensions","dry_run","mode"],"title":"ImportConfigTomlResponseContent"}}}},"500":{"description":"InternalServerError 500 response","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"title":"InternalServerErrorResponseContent"}}}}}} \ No newline at end of file +{"responses":{"200":{"description":"ImportConfigToml 200 response","content":{"application/json":{"schema":{"type":"object","description":"Summary of what an import created, updated, skipped or deleted.","properties":{"strategy":{"type":"string"},"dry_run":{"type":"boolean"},"config_version":{"type":"string"},"dimensions":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"message":{"type":"string"}},"required":["id","message"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"},"default_configs":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"message":{"type":"string"}},"required":["id","message"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"},"contexts":{"type":"object","description":"Per-entity outcome counts for an import.","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"deleted":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"message":{"type":"string"}},"required":["id","message"],"title":"ImportErrorItem"}}},"required":["created","deleted","skipped","updated"],"title":"ImportEntityReport"}},"required":["contexts","default_configs","dimensions","dry_run","strategy"],"title":"ImportConfigTomlResponseContent"}}}},"500":{"description":"InternalServerError 500 response","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"title":"InternalServerErrorResponseContent"}}}}}} \ No newline at end of file diff --git a/docs/docs/api/import-config-toml.api.mdx b/docs/docs/api/import-config-toml.api.mdx index 00f2c8cce..97b99f324 100644 --- a/docs/docs/api/import-config-toml.api.mdx +++ b/docs/docs/api/import-config-toml.api.mdx @@ -5,7 +5,7 @@ description: "Imports a full config from a TOML document, persisting dimensions, sidebar_label: "ImportConfigToml" hide_title: true hide_table_of_contents: true -api: eJztWN1P4zgQ/1esPO1KLaA98cLbfpy0SIdA0NM9IITceNJ6Seys7VAi1P/9ZmwnTZoAPdiuTqt9gcYez8dvfuOx/ZgIsKmRpZNaJSfJaVFq4yzjLKvynKVaZXLBMqMLHJqdn/3FhE6rApSbsBKMldZJtWBC4pBFFXbCBGS8yt00rEVVSpAeBw+oVyrUY3FJDswZrixPyTLjmQPD7nkuBfca3RJaUwfJJNFojZPoqWjd/OwtzHSRo4CB7xVY90mLOjl5TLxF5egnWT4scy4Vfdl0CQX343UJqMs6gwZRg5MuhxHlp6qs3AWvc81Fsl6vgy1pAD1xpgIcKLnhBWAINjm5fkwUfqCih4jB1PGFRQPkQLIELsDg11OOoIGOBumdmQpTT02lBkr62ftnCYqRS5MGSgj426oouJEWPwnYoJStpFvqynUTyVXtlvjjgH0JacQFmmU8t3Aw5vNc6xy4+tmejGJUaAEvA4RWDWkqwCyAvYtsfc+0YQaQJSkweIg+rLS5syUNhUSOQtDyp2/qq15hDE2AzgDHCDYKkZvSSY8Cd4wbYEpj/AYszlCZbNABwTKZe/xBVQUyLPHOe9J7j5ObbfaeERajKGk1BWO02R0pPqcIekhRdUlVAcPSxZxNfTQ184rt61FCkFJCichDo0rIeykqnrNoIOMy9zzgZZnXXUS8k/jdeDaE5Fz96eMeR+UezMpItwOBVCDhZDuFOXov6kAen1B7J8sSkyeVdTjFdMaqkipB9DlNZfLfi2svToxigwVcwbSh3I77jwAowxrL9PwbpFGPGPQHqn3P9rbsvOAP3IH240sfK20WUyl23+SHTaSjrN0m3qDvhkZsiS0ZLMl/ODqif2PtftPqGEqxZlmspthFqeJk6lvw4Terx3tpQHeQiyu/6ddEvpWnaVvxKe2LICYNKSctYXGXEZADERX1lYYOAER0suZ3+jEMsE3eUpsc0mPtg8Ewb++px2g1vr49x7wc1MVm38PWleqCukSlkCMZ+t6GOPQ+xtyxgHvYHNOLDkQYRuciMqNzEarRubArd6a4MbwmajkoRgPt+yvFKFYFWMsXY3nokfE68WXRSA+2Zb8pn6In20er6xaoTXgbEDZQDTX6pFwC/Q7Y+Nq9jUX+O7O/TGaba8XvlP4iKd3W2eR3WMS9vXqz8U9CbxiY2bS4y9jdPsfWtvaRHI92R5QwiudXYLBp+NjZ8Q9qkFsN7bn0tJEM3RkLhlbQfRPxC1FX4eLMzrhCG3ShJnzwurrUdJcutfUOcbfEr8MA8KFDqA5DUVAiIa3wdFz7260t8JRUH/BSHiydKz9xK9OPFa2+vqFjzPY84DnUtAI3G21XhE4A4GmdLSI0Prw/zGYXzEszjuJU4iEFzVnJnwFo3hfl057tYsaLP2fHC/icSZVprzTm7qrCbCPSMq5pDyF4LvtwPD36Y3p0TB5SMgruuRNPg4G/LKSSxceOnnedx47/7/tNRLfzFkMbqMnJ/UC963hAI+EQZaQfMmZJHEWJx0fMJfxt8vWahr9XYIiT+POeG8nnhDWRsDkwE12FtDSBVA9XpqfBe8rNO6hHXnP8zYBWEed3N7L3N5pnQxg8J70+irc+pOziZ3zSebuTr3nD2MXBzmvKW/iwpxeFnSLovHy8ndL7uvbvEkn/neKVsby7jOeP9+wls+2Vf/+mug8CPWtBILb/6Yx0bCR6uhrZj2kKpetIDc4t6+7p4OL8aobC8/isHm7eieEren3Ev+THev0vbv2A9w== +api: eJztWN1v2zgM/1cEP21A3BQ79KVv+zjgCtzQosnhHoKiUCw60WpLPkluZgT+34+U5Y/UTht02+Ew7KW1ZYr88UeKFLOPBNjEyMJJraLL6CovtHGWcZaWWcYSrVK5YanROS4trz//yYROyhyUm7ECjJXWSbVhQuKSRRV2xgSkvMxc3OxFVUqQHgdfUa9UqMfilgyYM1xZnpBlxlMHhj3yTAruNbotdKbOolmk0Ron0SvRwfzoLSx1nqGAgX9KsO6DFlV0uY+8ReXokSzPi4xLRW822ULO/XpVAOqyzqBB1OCky2BC+ZUqSnfDq0xzEdV13diSBhCJMyXgQsENzwFdsNHlah8pfEFFXwMHseMbiwYIQLQFLsDg2zEgaGCgQXowsTBVbEo1UnIYvb+3oBhBmrVUQsO/LfOcG2nxlYhtlLKddFtdumEguarcFh/O2KcmjLhBs5RnFs6mMK+1zoCr/xrJJEdaxWCMNi+ThJYNaeNrMv4mZOxbpo3PVKlKYJiUiCbGHJKuYl6xnaSgy59DM3/oHfrQOmgAE92yHdFCq0rIRylKnrFgIOUy8x7yosgqMgSqzDGbIg8S31tk0d3TVL1Wv3u/J1lBeEj+pnqBFYI7CAihkBijVOJB9RDpDeGRzE6bB1vwBA5jUxYWjHs9RyfaHFCTIK0O7rXKyL3Gvi8FeNqTCaIWLReHTGmziaU4/YCOC8BAWQf0G/Td0YotsJyCJfl35+f0b6pU92WKoRRrt4V8CRXQM5v48jn/YvV0HdTrL5C4UYwW/sBWTKeYvdwN4tWwL2YYeNE82AdZFCDoHAnIsB4KilZhqHhTML3dNgZTPGCZu6cyNy4wtXcIXb1/pBqh1fT+rg+97NhNf7qx9CQ6Bzz8pcJcThF/5+bYg+D3wAKm4xpDXFMOiqPfAjuT3wJdk9+a2jP4xI3h/jw7yCcdPcQrxSRXOVjLNzDZgAYJuYr80WilR2fKl54rRPK0Na46onr3ehJ6qsYafVBugZ4bbnyNuQ8Xil+R/Wki214Lf4X0JwnpU51tfMeH+KBW94V/1veHkam+1d2GLvcxtLjae3Mx2SVRwiieLcBg4/D+s4vv1CgPI/dsiDpPxnCmnKEdNDMgh43XZTP8sM9coQ0aiogfHDm2muahQlsPiLstvs0bkucOqZo3B4OIhaQ0GCs/odgcb9zVGS/k2da54gO3Mnlf0u7VHV1nnn4HbsB0Ane9tgWxEzr7UZ0dI7Q+vgUulzfMSzOO4nTMmxC0dyZ/D6Dv/mAeR3aKGS/+nB0v4GMmVaq90hC7RYnRRqZl2NNdRPB+9u4iPv8tPr8ghBSMnPvcCbfCJn9ZE0oWBtYDdIOB9f87gwd2B/M0FVGTEfwm9VbhkkbCjZch/TBjtpSjKLHfYyzhL5PVNS3j1G4oJ/HxEQdDviauKQnbizOlq5CWPmCq+wHwGfKOwXyAamIiR2dLko0o50838sPn7GddGP0k8HovvmUQPgXjYCR/JcjvOpeeAnkwL78S8pvb0ALfspdsdpPnjzc1nEsPrDUCofvES9LRSxzoamXfJwkUbiA1apv1sDndXC+WKLwOv8zlWtAew3c0quNfwlHX/wIAyTAG sidebar_class_name: "post api-method" info_path: docs/api/superposition custom_edit_url: null diff --git a/docs/docs/superposition-config-file/import-export.md b/docs/docs/superposition-config-file/import-export.md index a93019d1c..7dea9bfe7 100644 --- a/docs/docs/superposition-config-file/import-export.md +++ b/docs/docs/superposition-config-file/import-export.md @@ -55,7 +55,7 @@ On success you get a JSON summary of what changed: ```json { - "mode": "merge", + "strategy": "upsert", "dry_run": false, "config_version": "7231...", "dimensions": { "created": 2, "updated": 1, "skipped": 0, "deleted": 0 }, @@ -68,13 +68,11 @@ On success you get a JSON summary of what changed: Behaviour is controlled with request headers: -| Header | Values | Default | Effect | -| ---------------------- | ----------------------- | ------- | -------------------------------------------------------------------------------------------------------- | -| `x-import-mode` | `merge` \| `replace` | `merge` | `merge` upserts what's in the file and leaves everything else alone. `replace` additionally **deletes** any dimension/default-config/context that is _not_ in the file (mirror the file exactly). | -| `x-import-overwrite` | `true` \| `false` | `true` | When `false`, entities that already exist are **skipped** (only new ones are created). | -| `x-import-on-error` | `abort` \| `continue` | `abort` | `abort` rolls the whole import back on the first error. `continue` applies everything that's valid and returns the per-entity errors in the summary. | -| `x-import-dry-run` | `true` \| `false` | `false` | Parse, validate and compute the summary **without writing anything**. Great for previewing an import. | -| `x-import-value-merge` | `true` \| `false` | `false` | For object-valued default configs, deep-merge with the existing value instead of replacing it wholesale. | +| Header | Values | Default | Effect | +| ------------------- | ----------------------------------- | -------- | -------------------------------------------------------------------------------------------------------- | +| `x-import-strategy` | `create_only` \| `upsert` \| `replace` | `upsert` | `create_only` creates missing entities and skips existing ones. `upsert` creates missing entities and updates existing ones. `replace` mirrors the file and **deletes** any dimension/default-config/context that is _not_ in the file. | +| `x-import-on-error` | `abort` \| `continue` | `abort` | `abort` rolls the whole import back on the first error. `continue` applies everything that's valid and returns the per-entity errors in the summary. | +| `x-import-dry-run` | `true` \| `false` | `false` | Parse, validate and compute the summary **without writing anything**. Great for previewing an import. | When `x-import-on-error: continue` is used, failed entities appear under an `errors` array in the relevant section of the summary: @@ -89,11 +87,11 @@ When `x-import-on-error: continue` is used, failed entities appear under an ### Tips - Use `x-import-dry-run: true` first to see exactly what an import would do. -- Use `x-import-mode: replace` to make a workspace _exactly_ match a file - (e.g. restoring from a backup). Use the default `merge` to layer a file on top +- Use `x-import-strategy: replace` to make a workspace _exactly_ match a file + (e.g. restoring from a backup). Use the default `upsert` to layer a file on top of existing config without removing anything. -- Use `x-import-overwrite: false` to seed only the keys/dimensions/contexts that - don't exist yet, without touching anything already configured. +- Use `x-import-strategy: create_only` to seed only the keys/dimensions/contexts + that don't exist yet, without touching anything already configured. - `x-config-tags` is honoured and recorded against the config version the import creates, just like other write endpoints. - The endpoints are available in the generated SDKs as the `ImportConfigToml` @@ -108,8 +106,8 @@ default-configs through their dedicated APIs after importing if needed. :::caution The imported file is validated as a self-contained config (every context only references dimensions/keys defined in the same file), and dimension **positions** -are taken verbatim from the file. In `merge` mode this means the file's positions +are taken verbatim from the file. In `upsert` strategy this means the file's positions should be consistent with the dimensions already in the workspace. To avoid position clashes entirely, export the workspace, edit, and import back — or use -`replace` mode, which makes the workspace match the file exactly. +`replace` strategy, which makes the workspace match the file exactly. ::: diff --git a/smithy/models/config.smithy b/smithy/models/config.smithy index 35d41d2d9..31a324ee4 100644 --- a/smithy/models/config.smithy +++ b/smithy/models/config.smithy @@ -183,12 +183,15 @@ operation GetConfigJson { } } -@documentation("How an import treats workspace entities that are not present in the imported file.") -enum ImportMode { - @documentation("Upsert the entities in the file and leave everything else untouched.") - MERGE = "merge" +@documentation("How an import applies file entities to the workspace.") +enum ImportStrategy { + @documentation("Create entities that are present in the file but missing from the workspace. Existing entities are skipped. Nothing is deleted.") + CREATE_ONLY = "create_only" - @documentation("Mirror the file: additionally delete dimensions, default-configs and contexts that are absent from it.") + @documentation("Create missing entities and update existing entities from the file. Entities absent from the file are left untouched.") + UPSERT = "upsert" + + @documentation("Mirror the file: create missing entities, update existing entities, and delete dimensions, default-configs and contexts that are absent from it.") REPLACE = "replace" } @@ -234,7 +237,7 @@ structure ImportErrorItem { structure ImportConfigOutput { @required @notProperty - mode: String + strategy: String @required @notProperty @@ -261,15 +264,10 @@ structure ImportConfigOutput { @tags(["Configuration Management"]) operation ImportConfigToml { input := with [WorkspaceMixin] { - @documentation("Whether to merge (default) or replace existing workspace config.") - @httpHeader("x-import-mode") - @notProperty - mode: ImportMode - - @documentation("When false, entities that already exist are skipped instead of updated. Defaults to true.") - @httpHeader("x-import-overwrite") + @documentation("How the import applies file entities to the workspace. Defaults to upsert.") + @httpHeader("x-import-strategy") @notProperty - overwrite: Boolean + strategy: ImportStrategy @documentation("Whether to abort (default) or continue on per-entity errors.") @httpHeader("x-import-on-error") @@ -281,11 +279,6 @@ operation ImportConfigToml { @notProperty dry_run: Boolean - @documentation("When true, deep-merges object-valued default-configs with the existing value. Defaults to false.") - @httpHeader("x-import-value-merge") - @notProperty - value_merge: Boolean - @httpHeader("x-config-tags") @notProperty config_tags: String @@ -304,15 +297,10 @@ operation ImportConfigToml { @tags(["Configuration Management"]) operation ImportConfigJson { input := with [WorkspaceMixin] { - @documentation("Whether to merge (default) or replace existing workspace config.") - @httpHeader("x-import-mode") + @documentation("How the import applies file entities to the workspace. Defaults to upsert.") + @httpHeader("x-import-strategy") @notProperty - mode: ImportMode - - @documentation("When false, entities that already exist are skipped instead of updated. Defaults to true.") - @httpHeader("x-import-overwrite") - @notProperty - overwrite: Boolean + strategy: ImportStrategy @documentation("Whether to abort (default) or continue on per-entity errors.") @httpHeader("x-import-on-error") @@ -324,11 +312,6 @@ operation ImportConfigJson { @notProperty dry_run: Boolean - @documentation("When true, deep-merges object-valued default-configs with the existing value. Defaults to false.") - @httpHeader("x-import-value-merge") - @notProperty - value_merge: Boolean - @httpHeader("x-config-tags") @notProperty config_tags: String diff --git a/tests/src/config_import.test.ts b/tests/src/config_import.test.ts index 72901416c..e1661f5d2 100644 --- a/tests/src/config_import.test.ts +++ b/tests/src/config_import.test.ts @@ -9,13 +9,13 @@ import { ImportConfigJsonCommand, ImportConfigTomlCommand, type ImportConfigOutput, - ImportMode, + ImportStrategy, type ImportOnError, } from "@juspay/superposition-sdk"; import { superpositionClient, ENV } from "../env.ts"; import { describe, test, expect, beforeAll } from "bun:test"; -// Import in `replace` mode mirrors the *entire* workspace, so these tests run in +// Import with the `replace` strategy mirrors the *entire* workspace, so these tests run in // their own dedicated workspace to avoid clobbering data created by other suites. const IMPORT_WORKSPACE = "importtestws"; @@ -29,11 +29,9 @@ const DRYRUN_KEY = `imp_dryrun_${suffix}`; const TOML_KEY = `imp_toml_${suffix}`; type ImportOpts = { - mode?: ImportMode; - overwrite?: boolean; + strategy?: ImportStrategy; on_error?: ImportOnError; dry_run?: boolean; - value_merge?: boolean; }; async function importConfig( @@ -144,7 +142,7 @@ async function countContexts(): Promise { } beforeAll(async () => { - // Dedicated workspace so `replace`-mode imports can't affect other suites. + // Dedicated workspace so `replace` strategy imports can't affect other suites. try { await superpositionClient.send( new CreateWorkspaceCommand({ @@ -173,7 +171,7 @@ beforeAll(async () => { }); describe("Config import - JSON", () => { - test("merge import creates dimensions, default-configs and contexts", async () => { + test("upsert import creates dimensions, default-configs and contexts", async () => { const { status, summary } = await importConfig( "json", buildJsonConfig({ includeFlag: true }), @@ -181,7 +179,7 @@ describe("Config import - JSON", () => { expect(status).toBe(200); expect(summary).toBeDefined(); - expect(summary!.mode).toBe("merge"); + expect(summary!.strategy).toBe("upsert"); expect(summary!.dry_run).toBe(false); expect(summary!.config_version).toBeDefined(); expect(summary!.dimensions.created).toBeGreaterThanOrEqual(2); @@ -211,11 +209,11 @@ describe("Config import - JSON", () => { expect(summary!.dimensions.updated).toBeGreaterThanOrEqual(2); }); - test("overwrite=false skips entities that already exist", async () => { + test("create_only skips entities that already exist", async () => { const { status, summary } = await importConfig( "json", buildJsonConfig({ includeFlag: true }), - { overwrite: false }, + { strategy: ImportStrategy.CREATE_ONLY }, ); expect(status).toBe(200); @@ -225,7 +223,7 @@ describe("Config import - JSON", () => { expect(summary!.dimensions.skipped).toBeGreaterThanOrEqual(2); }); - test("value-merge deep-merges object default-config values", async () => { + test("upsert replaces object default-config values wholesale", async () => { const body = JSON.stringify({ "default-configs": { [FLAG]: { @@ -237,19 +235,15 @@ describe("Config import - JSON", () => { overrides: [], }); - const { status, summary } = await importConfig("json", body, { - value_merge: true, - }); + const { status, summary } = await importConfig("json", body); expect(status).toBe(200); expect(summary!.default_configs.updated).toBeGreaterThanOrEqual(1); const value = await getDefaultConfigValue(FLAG); - // existing { enabled, mode:"a", nested:{x:1} } deep-merged with the import expect(value).toEqual({ - enabled: true, mode: "b", - nested: { x: 1, y: 2 }, + nested: { y: 2 }, }); }); @@ -276,16 +270,16 @@ describe("Config import - JSON", () => { expect(keys).not.toContain(DRYRUN_KEY); }); - test("replace mode deletes entities absent from the file", async () => { - // Drop FLAG from the document; replace mode should remove it. + test("replace strategy deletes entities absent from the file", async () => { + // Drop FLAG from the document; replace strategy should remove it. const { status, summary } = await importConfig( "json", buildJsonConfig({ includeFlag: false }), - { mode: ImportMode.REPLACE }, + { strategy: ImportStrategy.REPLACE }, ); expect(status).toBe(200); - expect(summary!.mode).toBe("replace"); + expect(summary!.strategy).toBe("replace"); expect(summary!.default_configs.deleted).toBeGreaterThanOrEqual(1); const keys = await listDefaultConfigKeys(); @@ -333,7 +327,7 @@ describe("Config import - JSON", () => { }); describe("Config import - TOML", () => { - test("merge import via the TOML endpoint", async () => { + test("upsert import via the TOML endpoint", async () => { const toml = [ "[default-configs]", `${TOML_KEY} = { value = 5, schema = { type = "number" } }`, From 00fc2d055edb9b435816d11e5b3b616a30cea2b6 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 24 Jun 2026 03:24:53 +0000 Subject: [PATCH 14/15] style(frontend): leptosfmt the import config page `make check` runs `leptosfmt --check`, which flagged the newly added import_config page as unformatted. Apply leptosfmt (no logic change). --- crates/frontend/src/pages/import_config.rs | 194 ++++++++++++--------- 1 file changed, 116 insertions(+), 78 deletions(-) diff --git a/crates/frontend/src/pages/import_config.rs b/crates/frontend/src/pages/import_config.rs index 317d1b875..569e56fb4 100644 --- a/crates/frontend/src/pages/import_config.rs +++ b/crates/frontend/src/pages/import_config.rs @@ -490,10 +490,7 @@ fn ImportSummaryPanel( - + "Open Config Version" @@ -541,9 +538,7 @@ fn ActionBadge(action: ReviewAction) -> impl IntoView { - {action.label()} - + )>{action.label()} } } @@ -574,18 +569,21 @@ fn ReviewTableSection( "Name" "Action" {match kind { - ReviewTableKind::Dimensions => view! { - "Description" - }.into_view(), - ReviewTableKind::DefaultConfig => view! { - "Value" - }.into_view(), - ReviewTableKind::Overrides => view! { - <> - "Scope" - "Value" - - }.into_view(), + ReviewTableKind::Dimensions => { + view! { "Description" }.into_view() + } + ReviewTableKind::DefaultConfig => { + view! { "Value" }.into_view() + } + ReviewTableKind::Overrides => { + view! { + <> + "Scope" + "Value" + + } + .into_view() + } }} @@ -623,28 +621,53 @@ fn ReviewTableSection( } children=move |row| { match kind { - ReviewTableKind::Dimensions => view! { - - {row.name} - - {row.description} - - }.into_view(), - ReviewTableKind::DefaultConfig => view! { - - {row.name} - - {row.value} - - }.into_view(), - ReviewTableKind::Overrides => view! { - - {row.name} - - {row.scope} - {row.value} - - }.into_view(), + ReviewTableKind::Dimensions => { + view! { + + + {row.name} + + + + + {row.description} + + } + .into_view() + } + ReviewTableKind::DefaultConfig => { + view! { + + + {row.name} + + + + + + {row.value} + + + } + .into_view() + } + ReviewTableKind::Overrides => { + view! { + + + {row.name} + + + + + {row.scope} + + {row.value} + + + } + .into_view() + } } } /> @@ -932,8 +955,8 @@ pub fn ImportConfig() -> impl IntoView {
"Choose a Superposition config file." + fallback=move || { + view! { "Choose a Superposition config file." } } > @@ -976,14 +999,18 @@ pub fn ImportConfig() -> impl IntoView {
- + {move || { review_tables_rws .get() .map(|tables| { - view! { - - } + view! { } }) }} @@ -1173,20 +1206,25 @@ pub fn ImportConfig() -> impl IntoView { }} - + {move || { applied_rws .get() .map(|summary| { - let href = summary.config_version.as_ref().map(|version| { - format!( - "{}/admin/{}/{}/config/versions/{}", - base.get_value(), - org.get_untracked().0, - workspace.get_untracked().0, - version, - ) - }); + let href = summary + .config_version + .as_ref() + .map(|version| { + format!( + "{}/admin/{}/{}/config/versions/{}", + base.get_value(), + org.get_untracked().0, + workspace.get_untracked().0, + version, + ) + }); view! {
From 4b66bf6c59b07f1d24bdd73fc50b1b0f93670c8d Mon Sep 17 00:00:00 2001 From: Saurav Suman Date: Tue, 23 Jun 2026 17:29:47 +0530 Subject: [PATCH 15/15] feat: add frontend changes for import and simplified backend behavior --- .../sdk/Io/Superposition/Model/ImportStrategy.hs | 6 ++++++ .../client/SuperpositionAsyncClientImpl.java | 3 ++- .../client/SuperpositionClientImpl.java | 3 ++- crates/service_utils/src/observability.rs | 5 ++++- .../src/observability/instrumentation.rs | 10 ++++++++-- .../src/observability/saturation/db_pool.rs | 6 ++---- .../tests/observability_integration.rs | 11 +++++++++-- crates/superposition/src/app_state.rs | 13 +++++++------ crates/superposition/src/main.rs | 6 +++--- crates/superposition_sdk/src/serde_util.rs | 1 + crates/superposition_types/src/api/config.rs | 2 +- 11 files changed, 45 insertions(+), 21 deletions(-) diff --git a/clients/haskell/sdk/Io/Superposition/Model/ImportStrategy.hs b/clients/haskell/sdk/Io/Superposition/Model/ImportStrategy.hs index 819250ea3..305e40d11 100644 --- a/clients/haskell/sdk/Io/Superposition/Model/ImportStrategy.hs +++ b/clients/haskell/sdk/Io/Superposition/Model/ImportStrategy.hs @@ -32,6 +32,9 @@ instance Data.Aeson.FromJSON ImportStrategy where "upsert" -> pure UPSERT "replace" -> pure REPLACE _ -> fail $ "Unknown value for ImportStrategy: " <> Data.Text.unpack v + + + instance Io.Superposition.Utility.SerDe ImportStrategy where serializeElement CREATE_ONLY = Data.Text.Encoding.encodeUtf8 $ Data.Text.pack "create_only" serializeElement UPSERT = Data.Text.Encoding.encodeUtf8 $ Data.Text.pack "upsert" @@ -41,3 +44,6 @@ instance Io.Superposition.Utility.SerDe ImportStrategy where "upsert" -> Right UPSERT "replace" -> Right REPLACE e -> Left ("Failed to de-serialize ImportStrategy, encountered unknown variant: " ++ (show bs)) + + + diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClientImpl.java b/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClientImpl.java index 5a591a2bb..4b915542e 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClientImpl.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClientImpl.java @@ -284,9 +284,9 @@ @SmithyGenerated final class SuperpositionAsyncClientImpl extends Client implements SuperpositionAsyncClient { private static final TypeRegistry TYPE_REGISTRY = TypeRegistry.builder() - .putType(NotAuthorizedException.$ID, NotAuthorizedException.class, NotAuthorizedException::builder) .putType(ValidationException.$ID, ValidationException.class, ValidationException::builder) .putType(AccessDeniedException.$ID, AccessDeniedException.class, AccessDeniedException::builder) + .putType(NotAuthorizedException.$ID, NotAuthorizedException.class, NotAuthorizedException::builder) .putType(InternalFailureException.$ID, InternalFailureException.class, InternalFailureException::builder) .putType(UnknownOperationException.$ID, UnknownOperationException.class, UnknownOperationException::builder) .putType(MalformedRequestException.$ID, MalformedRequestException.class, MalformedRequestException::builder) @@ -658,3 +658,4 @@ protected TypeRegistry typeRegistry() { return TYPE_REGISTRY; } } + diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClientImpl.java b/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClientImpl.java index 2129ecfcc..963c91779 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClientImpl.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClientImpl.java @@ -284,9 +284,9 @@ @SmithyGenerated final class SuperpositionClientImpl extends Client implements SuperpositionClient { private static final TypeRegistry TYPE_REGISTRY = TypeRegistry.builder() - .putType(NotAuthorizedException.$ID, NotAuthorizedException.class, NotAuthorizedException::builder) .putType(ValidationException.$ID, ValidationException.class, ValidationException::builder) .putType(AccessDeniedException.$ID, AccessDeniedException.class, AccessDeniedException::builder) + .putType(NotAuthorizedException.$ID, NotAuthorizedException.class, NotAuthorizedException::builder) .putType(InternalFailureException.$ID, InternalFailureException.class, InternalFailureException::builder) .putType(UnknownOperationException.$ID, UnknownOperationException.class, UnknownOperationException::builder) .putType(MalformedRequestException.$ID, MalformedRequestException.class, MalformedRequestException::builder) @@ -1103,3 +1103,4 @@ protected TypeRegistry typeRegistry() { return TYPE_REGISTRY; } } + diff --git a/crates/service_utils/src/observability.rs b/crates/service_utils/src/observability.rs index 82598dc7c..83bba8bff 100644 --- a/crates/service_utils/src/observability.rs +++ b/crates/service_utils/src/observability.rs @@ -85,7 +85,10 @@ impl Observability { // histogram's cardinality. let drop_body_size_view = |i: &Instrument| match i.name() { "http.server.request.body.size" | "http.server.response.body.size" => { - Stream::builder().with_aggregation(Aggregation::Drop).build().ok() + Stream::builder() + .with_aggregation(Aggregation::Drop) + .build() + .ok() } _ => None, }; diff --git a/crates/service_utils/src/observability/instrumentation.rs b/crates/service_utils/src/observability/instrumentation.rs index 80b99f294..a6cda497b 100644 --- a/crates/service_utils/src/observability/instrumentation.rs +++ b/crates/service_utils/src/observability/instrumentation.rs @@ -92,7 +92,10 @@ fn normalize_method(m: &actix_web::http::Method) -> &'static str { } match_known!( m.as_str(), - ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "TRACE", "CONNECT"], + [ + "GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "TRACE", + "CONNECT" + ], "_OTHER" ) } @@ -142,7 +145,10 @@ mod tests { #[test] fn formatter_maps_unmatched_to_not_found_sentinel() { - assert_eq!(CardinalityBoundedFormatter.format("default"), ROUTE_NOT_FOUND); + assert_eq!( + CardinalityBoundedFormatter.format("default"), + ROUTE_NOT_FOUND + ); } #[test] diff --git a/crates/service_utils/src/observability/saturation/db_pool.rs b/crates/service_utils/src/observability/saturation/db_pool.rs index 34ddfb153..2008c29fb 100644 --- a/crates/service_utils/src/observability/saturation/db_pool.rs +++ b/crates/service_utils/src/observability/saturation/db_pool.rs @@ -39,10 +39,8 @@ pub fn register(meter: &Meter, pool: DbPoolHandle, pool_name: &'static str) { .u64_observable_gauge("db.client.connections.max") .with_description("Configured maximum size of the DB connection pool.") .with_callback(move |observer| { - observer.observe( - pool.max_size() as u64, - std::slice::from_ref(&max_pool_name), - ); + observer + .observe(pool.max_size() as u64, std::slice::from_ref(&max_pool_name)); }) .build(); } diff --git a/crates/service_utils/tests/observability_integration.rs b/crates/service_utils/tests/observability_integration.rs index 99e426a7e..33e839246 100644 --- a/crates/service_utils/tests/observability_integration.rs +++ b/crates/service_utils/tests/observability_integration.rs @@ -154,11 +154,18 @@ async fn runtime_tokio_metrics_appear_after_register_observers() { .lines() .find(|l| l.starts_with("runtime_tokio_workers ")) .unwrap_or_else(|| panic!("no runtime_tokio_workers in:\n{body}")); - let workers: f64 = workers_line.rsplit_once(' ').unwrap().1.trim().parse().unwrap(); + let workers: f64 = workers_line + .rsplit_once(' ') + .unwrap() + .1 + .trim() + .parse() + .unwrap(); assert!(workers >= 1.0, "expected >=1 worker, got {workers}"); assert!( - body.lines().any(|l| l.starts_with("runtime_tokio_global_queue_depth ")), + body.lines() + .any(|l| l.starts_with("runtime_tokio_global_queue_depth ")), "no runtime_tokio_global_queue_depth in:\n{body}" ); assert!( diff --git a/crates/superposition/src/app_state.rs b/crates/superposition/src/app_state.rs index d6d84d978..60a89b515 100644 --- a/crates/superposition/src/app_state.rs +++ b/crates/superposition/src/app_state.rs @@ -102,12 +102,13 @@ pub async fn get( }, snowflake_generator, app_env, - tenant_middleware_exclusion_list: - get_from_env_unsafe::("TENANT_MIDDLEWARE_EXCLUSION_LIST") - .expect("TENANT_MIDDLEWARE_EXCLUSION_LIST is not set") - .split(',') - .map(String::from) - .collect::>(), + tenant_middleware_exclusion_list: get_from_env_unsafe::( + "TENANT_MIDDLEWARE_EXCLUSION_LIST", + ) + .expect("TENANT_MIDDLEWARE_EXCLUSION_LIST is not set") + .split(',') + .map(String::from) + .collect::>(), service_prefix, superposition_token: get_superposition_token(kms_client, &app_env).await, redis: redis_pool, diff --git a/crates/superposition/src/main.rs b/crates/superposition/src/main.rs index 7938688b2..d41698556 100644 --- a/crates/superposition/src/main.rs +++ b/crates/superposition/src/main.rs @@ -32,9 +32,9 @@ use service_utils::{ workspace_context::OrgWorkspaceMiddlewareFactory, }, observability::{ - FredPoolStats, Observability, ObservabilityConfig, RedisStats, SdkMeterProvider, - build_request_metrics_middleware, set_label_config, - SaturationDeps, register_observers, spawn_metrics_server, + FredPoolStats, Observability, ObservabilityConfig, RedisStats, SaturationDeps, + SdkMeterProvider, build_request_metrics_middleware, register_observers, + set_label_config, spawn_metrics_server, }, service::types::AppEnv, }; diff --git a/crates/superposition_sdk/src/serde_util.rs b/crates/superposition_sdk/src/serde_util.rs index c8b709d6c..87da23131 100644 --- a/crates/superposition_sdk/src/serde_util.rs +++ b/crates/superposition_sdk/src/serde_util.rs @@ -1288,3 +1288,4 @@ if builder.value_before.is_none() { builder.value_before = Some(Default::default if builder.value_after.is_none() { builder.value_after = Some(Default::default()) } builder } + diff --git a/crates/superposition_types/src/api/config.rs b/crates/superposition_types/src/api/config.rs index 059e345d0..7e2e6b8b0 100644 --- a/crates/superposition_types/src/api/config.rs +++ b/crates/superposition_types/src/api/config.rs @@ -9,8 +9,8 @@ use serde_json::{Map, Value}; use superposition_derives::{IsEmpty, QueryParam}; use crate::{ - IsEmpty, custom_query::{CommaSeparatedStringQParams, QueryParam}, + IsEmpty, }; #[derive(Deserialize)]