diff --git a/Makefile b/Makefile index f33e06f3e3..17b184730e 100644 --- a/Makefile +++ b/Makefile @@ -56,7 +56,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 22a48aa9f7..5ba24f4c73 100644 --- a/compiler-rs/clients_schema_to_openapi/src/paths.rs +++ b/compiler-rs/clients_schema_to_openapi/src/paths.rs @@ -235,9 +235,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; @@ -317,9 +335,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) @@ -439,14 +457,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 706ad37970..1264153823 100644 --- a/compiler-rs/compiler-wasm-lib/pkg/compiler_wasm_lib.js +++ b/compiler-rs/compiler-wasm-lib/pkg/compiler_wasm_lib.js @@ -23,18 +23,9 @@ function getStringFromWasm0(ptr, len) { return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len)); } -const heap = new Array(128).fill(undefined); - -heap.push(undefined, null, true, false); - -let heap_next = heap.length; - -function addHeapObject(obj) { - if (heap_next === heap.length) heap.push(heap.length + 1); - const idx = heap_next; - heap_next = heap[idx]; - - heap[idx] = obj; +function addToExternrefTable0(obj) { + const idx = wasm.__externref_table_alloc(); + wasm.__wbindgen_export_3.set(idx, obj); return idx; } @@ -42,7 +33,8 @@ function handleError(f, args) { try { return f.apply(this, args); } catch (e) { - wasm.__wbindgen_exn_store(addHeapObject(e)); + const idx = addToExternrefTable0(e); + wasm.__wbindgen_exn_store(idx); } } @@ -111,33 +103,25 @@ function getDataViewMemory0() { return cachedDataViewMemory0; } -function getObject(idx) { return heap[idx]; } - -function dropObject(idx) { - if (idx < 132) return; - heap[idx] = heap_next; - heap_next = idx; -} - -function takeObject(idx) { - const ret = getObject(idx); - dropObject(idx); - return ret; -} - function isLikeNone(x) { return x === undefined || x === null; } function passArrayJsValueToWasm0(array, malloc) { const ptr = malloc(array.length * 4, 4) >>> 0; - const mem = getDataViewMemory0(); for (let i = 0; i < array.length; i++) { - mem.setUint32(ptr + 4 * i, addHeapObject(array[i]), true); + const add = addToExternrefTable0(array[i]); + getDataViewMemory0().setUint32(ptr + 4 * i, add, true); } WASM_VECTOR_LEN = array.length; return ptr; } + +function takeFromExternrefTable0(idx) { + const value = wasm.__wbindgen_export_3.get(idx); + wasm.__externref_table_dealloc(idx); + return value; +} /** * Convert schema.json to OpenAPI. The `cwd` argument is the current directory to be used * if not the system-defined one, as is the case when running with `npm rum --prefix compiler` @@ -145,20 +129,13 @@ function passArrayJsValueToWasm0(array, malloc) { * @param {string | null} [cwd] */ module.exports.convert_schema_to_openapi = function(args, cwd) { - try { - const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); - const ptr0 = passArrayJsValueToWasm0(args, wasm.__wbindgen_malloc); - const len0 = WASM_VECTOR_LEN; - var ptr1 = isLikeNone(cwd) ? 0 : passStringToWasm0(cwd, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - var len1 = WASM_VECTOR_LEN; - wasm.convert_schema_to_openapi(retptr, ptr0, len0, ptr1, len1); - var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true); - var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true); - if (r1) { - throw takeObject(r0); - } - } finally { - wasm.__wbindgen_add_to_stack_pointer(16); + const ptr0 = passArrayJsValueToWasm0(args, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + var ptr1 = isLikeNone(cwd) ? 0 : passStringToWasm0(cwd, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + var len1 = WASM_VECTOR_LEN; + const ret = wasm.convert_schema_to_openapi(ptr0, len0, ptr1, len1); + if (ret[1]) { + throw takeFromExternrefTable0(ret[0]); } }; @@ -221,7 +198,7 @@ module.exports.__wbg_measure_fb7825c11612c823 = function() { return handleError( module.exports.__wbg_new_8a6f238a6ece86ea = function() { const ret = new Error(); - return addHeapObject(ret); + return ret; }; module.exports.__wbg_readFileSync_691af69453e7d4ec = function(arg0, arg1, arg2, arg3, arg4) { @@ -233,7 +210,7 @@ module.exports.__wbg_readFileSync_691af69453e7d4ec = function(arg0, arg1, arg2, }; module.exports.__wbg_stack_0ed75d68575b0f3c = function(arg0, arg1) { - const ret = getObject(arg1).stack; + const ret = arg1.stack; const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); const len1 = WASM_VECTOR_LEN; getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); @@ -244,12 +221,19 @@ module.exports.__wbg_writeFileSync_d2c5ed0808e00dc9 = function(arg0, arg1, arg2, writeFileSync(getStringFromWasm0(arg0, arg1), getStringFromWasm0(arg2, arg3)); }; -module.exports.__wbindgen_object_drop_ref = function(arg0) { - takeObject(arg0); +module.exports.__wbindgen_init_externref_table = function() { + const table = wasm.__wbindgen_export_3; + const offset = table.grow(4); + table.set(0, undefined); + table.set(offset + 0, undefined); + table.set(offset + 1, null); + table.set(offset + 2, true); + table.set(offset + 3, false); + ; }; module.exports.__wbindgen_string_get = function(arg0, arg1) { - const obj = getObject(arg1); + const obj = arg1; const ret = typeof(obj) === 'string' ? obj : undefined; var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); var len1 = WASM_VECTOR_LEN; @@ -259,7 +243,7 @@ module.exports.__wbindgen_string_get = function(arg0, arg1) { module.exports.__wbindgen_string_new = function(arg0, arg1) { const ret = getStringFromWasm0(arg0, arg1); - return addHeapObject(ret); + return ret; }; module.exports.__wbindgen_throw = function(arg0, arg1) { @@ -274,3 +258,5 @@ const wasmInstance = new WebAssembly.Instance(wasmModule, imports); wasm = wasmInstance.exports; module.exports.__wasm = wasm; +wasm.__wbindgen_start(); + 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 840588c2f5..dc456e4c6a 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/pkg/compiler_wasm_lib_bg.wasm.d.ts b/compiler-rs/compiler-wasm-lib/pkg/compiler_wasm_lib_bg.wasm.d.ts index f9645942c4..2d4f44aee3 100644 --- a/compiler-rs/compiler-wasm-lib/pkg/compiler_wasm_lib_bg.wasm.d.ts +++ b/compiler-rs/compiler-wasm-lib/pkg/compiler_wasm_lib_bg.wasm.d.ts @@ -1,9 +1,12 @@ /* tslint:disable */ /* eslint-disable */ export const memory: WebAssembly.Memory; -export const convert_schema_to_openapi: (a: number, b: number, c: number, d: number, e: number) => void; +export const convert_schema_to_openapi: (a: number, b: number, c: number, d: number) => [number, number]; export const __wbindgen_free: (a: number, b: number, c: number) => void; export const __wbindgen_exn_store: (a: number) => void; +export const __externref_table_alloc: () => number; +export const __wbindgen_export_3: WebAssembly.Table; export const __wbindgen_malloc: (a: number, b: number) => number; export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number; -export const __wbindgen_add_to_stack_pointer: (a: number) => number; +export const __externref_table_dealloc: (a: number) => void; +export const __wbindgen_start: () => void; 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(()) }