diff --git a/Makefile b/Makefile index 72a308c098..ba01681ee8 100644 --- a/Makefile +++ b/Makefile @@ -53,7 +53,7 @@ transform-to-openapi: ## Generate the OpenAPI definition from the compiled schem @npm run transform-to-openapi -- --schema output/schema/schema.json --flavor serverless --output output/openapi/elasticsearch-serverless-openapi.json transform-to-openapi-for-docs: ## Generate the OpenAPI definition tailored for API docs generation - @npm run transform-to-openapi -- --schema output/schema/schema.json --flavor stack --lift-enum-descriptions --merge-multipath-endpoints --output output/openapi/elasticsearch-openapi-docs.json + @npm run transform-to-openapi -- --schema output/schema/schema.json --flavor stack --lift-enum-descriptions --merge-multipath-endpoints --multipath-redirects --output output/openapi/elasticsearch-openapi-docs.json filter-for-serverless: ## Generate the serverless version from the compiled schema @npm run --prefix compiler filter-by-availability -- --serverless --visibility=public --input ../output/schema/schema.json --output ../output/output/openapi/elasticsearch-serverless-openapi.json diff --git a/compiler-rs/clients_schema_to_openapi/src/cli.rs b/compiler-rs/clients_schema_to_openapi/src/cli.rs index 9ebf6d5be5..9342a15582 100644 --- a/compiler-rs/clients_schema_to_openapi/src/cli.rs +++ b/compiler-rs/clients_schema_to_openapi/src/cli.rs @@ -31,6 +31,22 @@ pub struct Cli { /// merge endpoints with multiple paths into a single OpenAPI operation [default = false] #[argh(switch)] pub merge_multipath_endpoints: bool, + + /// output a redirection map when merging multipath endpoints + #[argh(switch)] + pub multipath_redirects: bool, +} + +impl Cli { + pub fn redirect_path(&self, output: &PathBuf) -> Option { + if self.multipath_redirects { + let path = output.to_string_lossy(); + let path = path.rsplit_once('.').unwrap().0; + Some(format!("{}.redirects.csv", path)) + } else { + None + } + } } use derive_more::FromStr; @@ -57,6 +73,7 @@ impl From for Configuration { flavor, lift_enum_descriptions: cli.lift_enum_descriptions, merge_multipath_endpoints: cli.merge_multipath_endpoints, + multipath_redirects: cli.multipath_redirects, namespaces: if cli.namespace.is_empty() { None } else { diff --git a/compiler-rs/clients_schema_to_openapi/src/components.rs b/compiler-rs/clients_schema_to_openapi/src/components.rs index 973cdf8d81..0829f80e18 100644 --- a/compiler-rs/clients_schema_to_openapi/src/components.rs +++ b/compiler-rs/clients_schema_to_openapi/src/components.rs @@ -15,6 +15,7 @@ // specific language governing permissions and limitations // under the License. +use std::collections::BTreeMap; use clients_schema::TypeName; use openapiv3::{Components, Parameter, ReferenceOr, RequestBody, Response, Schema, StatusCode}; use crate::Configuration; @@ -32,11 +33,19 @@ pub struct TypesAndComponents<'a> { pub config: &'a Configuration, pub model: &'a clients_schema::IndexedModel, pub components: &'a mut Components, + // Redirections (if paths multipaths endpoints are merged) + pub redirects: Option>, } impl<'a> TypesAndComponents<'a> { pub fn new(config: &'a Configuration, model: &'a clients_schema::IndexedModel, components: &'a mut Components) -> TypesAndComponents<'a> { - TypesAndComponents { config, model, components } + let redirects = if config.merge_multipath_endpoints && config.multipath_redirects { + Some(BTreeMap::new()) + } else { + None + }; + + TypesAndComponents { config, model, components, redirects } } pub fn add_request_body(&mut self, endpoint: &str, body: RequestBody) -> ReferenceOr { diff --git a/compiler-rs/clients_schema_to_openapi/src/lib.rs b/compiler-rs/clients_schema_to_openapi/src/lib.rs index 68abf6002e..1c088329be 100644 --- a/compiler-rs/clients_schema_to_openapi/src/lib.rs +++ b/compiler-rs/clients_schema_to_openapi/src/lib.rs @@ -41,10 +41,18 @@ pub struct Configuration { /// be the longest one (with values for all optional parameters), and the other paths will be added /// at the beginning of the operation's description. pub merge_multipath_endpoints: bool, + + /// Should we output a redirect map when merging multipath endpoints? + pub multipath_redirects: bool, +} + +pub struct OpenApiConversion { + pub openapi: OpenAPI, + pub redirects: Option, } /// Convert an API model into an OpenAPI v3 schema, optionally filtered for a given flavor -pub fn convert_schema(mut schema: IndexedModel, config: Configuration) -> anyhow::Result { +pub fn convert_schema(mut schema: IndexedModel, config: Configuration) -> anyhow::Result { // Expand generics schema = clients_schema::transform::expand_generics(schema, ExpandConfig::default())?; @@ -74,7 +82,7 @@ pub fn convert_schema(mut schema: IndexedModel, config: Configuration) -> anyhow /// Note: there are ways to represent [generics in JSON Schema], but its unlikely that tooling will understand it. /// /// [generics in JSON Schema]: https://json-schema.org/blog/posts/dynamicref-and-generics -pub fn convert_expanded_schema(model: &IndexedModel, config: &Configuration) -> anyhow::Result { +pub fn convert_expanded_schema(model: &IndexedModel, config: &Configuration) -> anyhow::Result { let mut openapi = OpenAPI { openapi: "3.0.3".into(), info: info(model), @@ -130,7 +138,21 @@ pub fn convert_expanded_schema(model: &IndexedModel, config: &Configuration) -> // comp.security_schemes.sort_keys(); // } - Ok(openapi) + let redirects = if let Some(redirects) = tac.redirects { + use std::fmt::Write; + let mut result = String::new(); + for (source, target) in redirects.iter() { + writeln!(&mut result, "{},{}", source, target)?; + } + Some(result) + } else { + None + }; + + Ok(OpenApiConversion { + openapi, + redirects, + }) } fn info(model: &IndexedModel) -> openapiv3::Info { @@ -183,7 +205,7 @@ pub fn convert_availabilities(availabilities: &Option, flavor: & let exp_since = format!("Technical preview{since_str}"); result.insert("x-state".to_string(), Value::String(exp_since)); } - Stability::Stable => { + Stability::Stable => { let stable_since = format!("Generally available{since_str}"); result.insert("x-state".to_string(), Value::String(stable_since)); } diff --git a/compiler-rs/clients_schema_to_openapi/src/main.rs b/compiler-rs/clients_schema_to_openapi/src/main.rs index 1df140395a..2d9fb07405 100644 --- a/compiler-rs/clients_schema_to_openapi/src/main.rs +++ b/compiler-rs/clients_schema_to_openapi/src/main.rs @@ -34,7 +34,14 @@ fn main() -> anyhow::Result<()> { let schema = IndexedModel::from_reader(File::open(&cli.schema)?)?; let output = cli.output.clone(); + let redirect_path = cli.redirect_path(&cli.output); let openapi = clients_schema_to_openapi::convert_schema(schema, cli.into())?; - serde_json::to_writer_pretty(File::create(&output)?, &openapi)?; + serde_json::to_writer_pretty(File::create(&output)?, &openapi.openapi)?; + + if let Some(redirects) = openapi.redirects { + let path = redirect_path.unwrap(); + std::fs::write(path, &redirects)?; + } + Ok(()) } diff --git a/compiler-rs/clients_schema_to_openapi/src/paths.rs b/compiler-rs/clients_schema_to_openapi/src/paths.rs index 40c9ce8f33..b255630cbf 100644 --- a/compiler-rs/clients_schema_to_openapi/src/paths.rs +++ b/compiler-rs/clients_schema_to_openapi/src/paths.rs @@ -233,9 +233,27 @@ pub fn add_endpoint( }; //---- Merge multipath endpoints if asked for + + let operation_id: String = endpoint + .name + .chars() + .map(|x| match x { + '_' | '.' => '-', + _ => x, + }) + .collect(); + let mut new_endpoint: clients_schema::Endpoint; let endpoint = if is_multipath && tac.config.merge_multipath_endpoints { + + // Add redirects for operations that would have been generated otherwise + if let Some(ref mut map) = &mut tac.redirects { + for i in 1..endpoint.urls.len() { + map.insert(format!("{operation_id}-{i}"), operation_id.clone()); + } + } + new_endpoint = endpoint.clone(); let endpoint = &mut new_endpoint; @@ -315,9 +333,9 @@ pub fn add_endpoint( parameters.append(&mut query_params.clone()); let sum_desc = split_summary_desc(&endpoint.description); - + let privilege_desc = add_privileges(&endpoint.privileges); - + let full_desc = match (sum_desc.description, privilege_desc) { (Some(a), Some(b)) => Some(a+ &b), (opt_a, opt_b) => opt_a.or(opt_b) @@ -419,14 +437,7 @@ pub fn add_endpoint( }; let mut operation = operation.clone(); - let mut operation_id: String = endpoint - .name - .chars() - .map(|x| match x { - '_' | '.' => '-', - _ => x, - }) - .collect(); + let mut operation_id = operation_id.clone(); if operation_counter != 0 { write!(&mut operation_id, "-{}", operation_counter)?; } diff --git a/compiler-rs/compiler-wasm-lib/pkg/compiler_wasm_lib.js b/compiler-rs/compiler-wasm-lib/pkg/compiler_wasm_lib.js index 0ecc0163a0..6a48727a33 100644 --- a/compiler-rs/compiler-wasm-lib/pkg/compiler_wasm_lib.js +++ b/compiler-rs/compiler-wasm-lib/pkg/compiler_wasm_lib.js @@ -162,6 +162,14 @@ function handleError(f, args) { } } +module.exports.__wbg_readFileSync_2d82336a457bbeee = function(arg0, arg1, arg2, arg3, arg4) { + const ret = readFileSync(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); +}; + module.exports.__wbg_writeFileSync_bc6b1883f11970b9 = function(arg0, arg1, arg2, arg3) { writeFileSync(getStringFromWasm0(arg0, arg1), getStringFromWasm0(arg2, arg3)); }; @@ -171,14 +179,6 @@ module.exports.__wbindgen_string_new = function(arg0, arg1) { return addHeapObject(ret); }; -module.exports.__wbg_readFileSync_2d82336a457bbeee = function(arg0, arg1, arg2, arg3, arg4) { - const ret = readFileSync(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); - const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len1 = WASM_VECTOR_LEN; - getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); - getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); -}; - module.exports.__wbg_log_c9486ca5d8e2cbe8 = function(arg0, arg1) { let deferred0_0; let deferred0_1; diff --git a/compiler-rs/compiler-wasm-lib/pkg/compiler_wasm_lib_bg.wasm b/compiler-rs/compiler-wasm-lib/pkg/compiler_wasm_lib_bg.wasm index e509d6b488..18acdde457 100644 Binary files a/compiler-rs/compiler-wasm-lib/pkg/compiler_wasm_lib_bg.wasm and b/compiler-rs/compiler-wasm-lib/pkg/compiler_wasm_lib_bg.wasm differ diff --git a/compiler-rs/compiler-wasm-lib/src/lib.rs b/compiler-rs/compiler-wasm-lib/src/lib.rs index 08245c18c4..8f9abd022c 100644 --- a/compiler-rs/compiler-wasm-lib/src/lib.rs +++ b/compiler-rs/compiler-wasm-lib/src/lib.rs @@ -59,14 +59,21 @@ pub fn convert0(cli: Cli, cwd: Option) -> anyhow::Result<()> { Some(ref cwd) => PathBuf::from(cwd).join(&cli.output), None => cli.output.clone(), }; + let redirect_path = cli.redirect_path(&output); let json = node_fs::read_file_sync_to_string(&input.to_string_lossy(), "utf8"); let schema = IndexedModel::from_reader(json.as_bytes())?; let openapi = clients_schema_to_openapi::convert_schema(schema, cli.into())?; - let result = serde_json::to_string_pretty(&openapi)?; + let result = serde_json::to_string_pretty(&openapi.openapi)?; node_fs::write_file_sync(&output.to_string_lossy(), &result); + + if let Some(redirects) = openapi.redirects { + let path = redirect_path.unwrap(); + node_fs::write_file_sync(&path, &redirects); + } + Ok(()) }