Skip to content

Commit 694a7cf

Browse files
authored
Output a redirect map when merging multipath operations (#4545) (#4546)
1 parent ab247d6 commit 694a7cf

File tree

10 files changed

+94
-29
lines changed

10 files changed

+94
-29
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ transform-to-openapi: ## Generate the OpenAPI definition from the compiled schem
5353
@npm run transform-to-openapi -- --schema output/schema/schema.json --flavor serverless --output output/openapi/elasticsearch-serverless-openapi.json
5454

5555
transform-to-openapi-for-docs: ## Generate the OpenAPI definition tailored for API docs generation
56-
@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
56+
@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
5757

5858
filter-for-serverless: ## Generate the serverless version from the compiled schema
5959
@npm run --prefix compiler filter-by-availability -- --serverless --visibility=public --input ../output/schema/schema.json --output ../output/output/openapi/elasticsearch-serverless-openapi.json

compiler-rs/clients_schema_to_openapi/src/cli.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,22 @@ pub struct Cli {
3131
/// merge endpoints with multiple paths into a single OpenAPI operation [default = false]
3232
#[argh(switch)]
3333
pub merge_multipath_endpoints: bool,
34+
35+
/// output a redirection map when merging multipath endpoints
36+
#[argh(switch)]
37+
pub multipath_redirects: bool,
38+
}
39+
40+
impl Cli {
41+
pub fn redirect_path(&self, output: &PathBuf) -> Option<String> {
42+
if self.multipath_redirects {
43+
let path = output.to_string_lossy();
44+
let path = path.rsplit_once('.').unwrap().0;
45+
Some(format!("{}.redirects.csv", path))
46+
} else {
47+
None
48+
}
49+
}
3450
}
3551

3652
use derive_more::FromStr;
@@ -57,6 +73,7 @@ impl From<Cli> for Configuration {
5773
flavor,
5874
lift_enum_descriptions: cli.lift_enum_descriptions,
5975
merge_multipath_endpoints: cli.merge_multipath_endpoints,
76+
multipath_redirects: cli.multipath_redirects,
6077
namespaces: if cli.namespace.is_empty() {
6178
None
6279
} else {

compiler-rs/clients_schema_to_openapi/src/components.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
// specific language governing permissions and limitations
1616
// under the License.
1717

18+
use std::collections::BTreeMap;
1819
use clients_schema::TypeName;
1920
use openapiv3::{Components, Parameter, ReferenceOr, RequestBody, Response, Schema, StatusCode};
2021
use crate::Configuration;
@@ -32,11 +33,19 @@ pub struct TypesAndComponents<'a> {
3233
pub config: &'a Configuration,
3334
pub model: &'a clients_schema::IndexedModel,
3435
pub components: &'a mut Components,
36+
// Redirections (if paths multipaths endpoints are merged)
37+
pub redirects: Option<BTreeMap<String, String>>,
3538
}
3639

3740
impl<'a> TypesAndComponents<'a> {
3841
pub fn new(config: &'a Configuration, model: &'a clients_schema::IndexedModel, components: &'a mut Components) -> TypesAndComponents<'a> {
39-
TypesAndComponents { config, model, components }
42+
let redirects = if config.merge_multipath_endpoints && config.multipath_redirects {
43+
Some(BTreeMap::new())
44+
} else {
45+
None
46+
};
47+
48+
TypesAndComponents { config, model, components, redirects }
4049
}
4150

4251
pub fn add_request_body(&mut self, endpoint: &str, body: RequestBody) -> ReferenceOr<RequestBody> {

compiler-rs/clients_schema_to_openapi/src/lib.rs

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,18 @@ pub struct Configuration {
4040
/// be the longest one (with values for all optional parameters), and the other paths will be added
4141
/// at the beginning of the operation's description.
4242
pub merge_multipath_endpoints: bool,
43+
44+
/// Should we output a redirect map when merging multipath endpoints?
45+
pub multipath_redirects: bool,
46+
}
47+
48+
pub struct OpenApiConversion {
49+
pub openapi: OpenAPI,
50+
pub redirects: Option<String>,
4351
}
4452

4553
/// Convert an API model into an OpenAPI v3 schema, optionally filtered for a given flavor
46-
pub fn convert_schema(mut schema: IndexedModel, config: Configuration) -> anyhow::Result<OpenAPI> {
54+
pub fn convert_schema(mut schema: IndexedModel, config: Configuration) -> anyhow::Result<OpenApiConversion> {
4755
// Expand generics
4856
schema = clients_schema::transform::expand_generics(schema, ExpandConfig::default())?;
4957

@@ -73,7 +81,7 @@ pub fn convert_schema(mut schema: IndexedModel, config: Configuration) -> anyhow
7381
/// Note: there are ways to represent [generics in JSON Schema], but its unlikely that tooling will understand it.
7482
///
7583
/// [generics in JSON Schema]: https://json-schema.org/blog/posts/dynamicref-and-generics
76-
pub fn convert_expanded_schema(model: &IndexedModel, config: &Configuration) -> anyhow::Result<OpenAPI> {
84+
pub fn convert_expanded_schema(model: &IndexedModel, config: &Configuration) -> anyhow::Result<OpenApiConversion> {
7785
let mut openapi = OpenAPI {
7886
openapi: "3.0.3".into(),
7987
info: info(model),
@@ -129,7 +137,21 @@ pub fn convert_expanded_schema(model: &IndexedModel, config: &Configuration) ->
129137
// comp.security_schemes.sort_keys();
130138
// }
131139

132-
Ok(openapi)
140+
let redirects = if let Some(redirects) = tac.redirects {
141+
use std::fmt::Write;
142+
let mut result = String::new();
143+
for (source, target) in redirects.iter() {
144+
writeln!(&mut result, "{},{}", source, target)?;
145+
}
146+
Some(result)
147+
} else {
148+
None
149+
};
150+
151+
Ok(OpenApiConversion {
152+
openapi,
153+
redirects,
154+
})
133155
}
134156

135157
fn info(model: &IndexedModel) -> openapiv3::Info {

compiler-rs/clients_schema_to_openapi/src/main.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,14 @@ fn main() -> anyhow::Result<()> {
3434

3535
let schema = IndexedModel::from_reader(File::open(&cli.schema)?)?;
3636
let output = cli.output.clone();
37+
let redirect_path = cli.redirect_path(&cli.output);
3738
let openapi = clients_schema_to_openapi::convert_schema(schema, cli.into())?;
38-
serde_json::to_writer_pretty(File::create(&output)?, &openapi)?;
39+
serde_json::to_writer_pretty(File::create(&output)?, &openapi.openapi)?;
40+
41+
if let Some(redirects) = openapi.redirects {
42+
let path = redirect_path.unwrap();
43+
std::fs::write(path, &redirects)?;
44+
}
45+
3946
Ok(())
4047
}

compiler-rs/clients_schema_to_openapi/src/paths.rs

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -232,9 +232,27 @@ pub fn add_endpoint(
232232
};
233233

234234
//---- Merge multipath endpoints if asked for
235+
236+
let operation_id: String = endpoint
237+
.name
238+
.chars()
239+
.map(|x| match x {
240+
'_' | '.' => '-',
241+
_ => x,
242+
})
243+
.collect();
244+
235245
let mut new_endpoint: clients_schema::Endpoint;
236246

237247
let endpoint = if is_multipath && tac.config.merge_multipath_endpoints {
248+
249+
// Add redirects for operations that would have been generated otherwise
250+
if let Some(ref mut map) = &mut tac.redirects {
251+
for i in 1..endpoint.urls.len() {
252+
map.insert(format!("{operation_id}-{i}"), operation_id.clone());
253+
}
254+
}
255+
238256
new_endpoint = endpoint.clone();
239257
let endpoint = &mut new_endpoint;
240258

@@ -314,9 +332,9 @@ pub fn add_endpoint(
314332
parameters.append(&mut query_params.clone());
315333

316334
let sum_desc = split_summary_desc(&endpoint.description);
317-
335+
318336
let privilege_desc = add_privileges(&endpoint.privileges);
319-
337+
320338
let full_desc = match (sum_desc.description, privilege_desc) {
321339
(Some(a), Some(b)) => Some(a+ &b),
322340
(opt_a, opt_b) => opt_a.or(opt_b)
@@ -434,14 +452,7 @@ pub fn add_endpoint(
434452
};
435453

436454
let mut operation = operation.clone();
437-
let mut operation_id: String = endpoint
438-
.name
439-
.chars()
440-
.map(|x| match x {
441-
'_' | '.' => '-',
442-
_ => x,
443-
})
444-
.collect();
455+
let mut operation_id = operation_id.clone();
445456
if operation_counter != 0 {
446457
write!(&mut operation_id, "-{}", operation_counter)?;
447458
}
Binary file not shown.

compiler-rs/compiler-wasm-lib/src/lib.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,21 @@ pub fn convert0(cli: Cli, cwd: Option<String>) -> anyhow::Result<()> {
5959
Some(ref cwd) => PathBuf::from(cwd).join(&cli.output),
6060
None => cli.output.clone(),
6161
};
62+
let redirect_path = cli.redirect_path(&output);
6263

6364
let json = node_fs::read_file_sync_to_string(&input.to_string_lossy(), "utf8");
6465
let schema = IndexedModel::from_reader(json.as_bytes())?;
6566

6667
let openapi = clients_schema_to_openapi::convert_schema(schema, cli.into())?;
6768

68-
let result = serde_json::to_string_pretty(&openapi)?;
69+
let result = serde_json::to_string_pretty(&openapi.openapi)?;
6970
node_fs::write_file_sync(&output.to_string_lossy(), &result);
71+
72+
if let Some(redirects) = openapi.redirects {
73+
let path = redirect_path.unwrap();
74+
node_fs::write_file_sync(&path, &redirects);
75+
}
76+
7077
Ok(())
7178
}
7279

output/openapi/elasticsearch-openapi.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)