Skip to content

Commit 147c77c

Browse files
authored
OpenAPI: update path separator (#4199) (#4249)
1 parent 7f4bf4b commit 147c77c

17 files changed

+25189
-23243
lines changed

compiler-rs/Cargo.lock

+12
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compiler-rs/clients_schema_to_openapi/.spectral.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,7 @@ rules:
77
oas3-api-servers: off # OpenAPI "servers" must be present and non-empty array.
88
info-contact: off # Info object must have "contact" object.
99
info-description: off # Info "description" must be present and non-empty string.
10+
operation-description: off # Info "description" must be present and non-empty string.
1011
operation-tag-defined: off # Operation tags must be defined in global tags.
12+
13+
# oas3-valid-media-example: off

compiler-rs/clients_schema_to_openapi/package-lock.json

+2,274-861
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compiler-rs/clients_schema_to_openapi/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"author": "",
1212
"license": "ISC",
1313
"dependencies": {
14-
"@stoplight/spectral-cli": "^6.10.1",
14+
"@stoplight/spectral-cli": "^6.14.3",
1515
"json-diff": "^1.0.6"
1616
}
1717
}

compiler-rs/clients_schema_to_openapi/src/components.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,12 @@ use openapiv3::{Components, Parameter, ReferenceOr, RequestBody, Response, Schem
2121
use crate::utils::SchemaName;
2222

2323
// Separator used to combine parts of a component path.
24-
// See https://github.com/elastic/elasticsearch-specification/issues/4183
25-
pub const SEPARATOR: char = ':';
24+
// OpenAPI says `$ref` must comply with RFC 3968 (escaping reserved chars),
25+
// but also restricts the keys in `components` to match `^[a-zA-Z0-9\.\-_]+$`.
26+
//
27+
// See https://spec.openapis.org/oas/v3.1.1.html#reference-object
28+
// and https://spec.openapis.org/oas/v3.1.1.html#fixed-fields-5
29+
pub const SEPARATOR: char = '-';
2630

2731
pub struct TypesAndComponents<'a> {
2832
pub model: &'a clients_schema::IndexedModel,

compiler-rs/clients_schema_to_openapi/src/paths.rs

+35-15
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ use openapiv3::{
2828
RequestBody, Response, Responses, StatusCode, Example
2929
};
3030
use clients_schema::SchemaExample;
31-
use serde_json::json;
3231

3332
use crate::components::TypesAndComponents;
3433

@@ -40,12 +39,12 @@ pub fn add_endpoint(
4039
out: &mut Paths,
4140
) -> anyhow::Result<()> {
4241
if endpoint.request.is_none() {
43-
tracing::warn!("Endpoint {} is missing a request -- ignored", &endpoint.name);
42+
// tracing::warn!("Endpoint {} is missing a request -- ignored", &endpoint.name);
4443
return Ok(());
4544
}
4645

4746
if endpoint.response.is_none() {
48-
tracing::warn!("Endpoint {} is missing a response -- ignored", &endpoint.name);
47+
// tracing::warn!("Endpoint {} is missing a response -- ignored", &endpoint.name);
4948
return Ok(());
5049
}
5150

@@ -122,28 +121,48 @@ pub fn add_endpoint(
122121
// This function converts the IndexMap<String, SchemaExample> examples of
123122
// schema.json to IndexMap<String, ReferenceOr<Example>> which is the format
124123
// that OpenAPI expects.
125-
fn get_openapi_examples(schema_examples: IndexMap<String, SchemaExample>) -> IndexMap<String, ReferenceOr<Example>> {
124+
fn get_openapi_examples(schema_examples: &IndexMap<String, SchemaExample>) -> IndexMap<String, ReferenceOr<Example>> {
126125
let mut openapi_examples = indexmap! {};
127126
for (name, schema_example) in schema_examples {
127+
let example = match &schema_example.value {
128+
None => None,
129+
// Examples should be objects - https://spec.openapis.org/oas/v3.1.1.html#example-object
130+
// Disabled for now, as some examples use multi-line json as in the Kibana console.
131+
Some(text) => Some(serde_json::Value::String(text.clone())),
132+
// Some(text) => {
133+
// match serde_json::from_str::<serde_json::Value>(&text) {
134+
// Ok(json) => {
135+
// Some(json)
136+
// }
137+
// // Cannot parse json: assume it's text (e.g. cat requests)
138+
// // but should be validated by looking at the media-type
139+
// Err(err) => {
140+
// tracing::warn!("Cannot parse example: {}\n{}", err, text);
141+
// Some(serde_json::Value::String(text.clone()))
142+
// }
143+
// }
144+
// }
145+
};
146+
128147
let openapi_example = Example {
129-
value: Some(json!(schema_example.value)),
148+
value: example,
130149
description: schema_example.description.clone(),
131150
summary: schema_example.summary.clone(),
132151
external_value: None,
133152
extensions: Default::default(),
134153
};
135154
openapi_examples.insert(name.clone(), ReferenceOr::Item(openapi_example));
136155
}
137-
return openapi_examples;
156+
openapi_examples
138157
}
139158

140-
141-
let mut request_examples: IndexMap<String, ReferenceOr<Example>> = indexmap! {};
142159
// If this endpoint request has examples in schema.json, convert them to the
143160
// OpenAPI format and add them to the endpoint request in the OpenAPI document.
144-
if request.examples.is_some() {
145-
request_examples = get_openapi_examples(request.examples.as_ref().unwrap().clone());
146-
}
161+
let request_examples = if let Some(examples) = &request.examples {
162+
get_openapi_examples(examples)
163+
} else {
164+
IndexMap::new()
165+
};
147166

148167
let request_body = tac.convert_request(request)?.map(|schema| {
149168
let media = MediaType {
@@ -176,12 +195,13 @@ pub fn add_endpoint(
176195
// FIXME: buggy for responses with no body
177196
// TODO: handle binary responses
178197
let response_def = tac.model.get_response(endpoint.response.as_ref().unwrap())?;
179-
let mut response_examples: IndexMap<String, ReferenceOr<Example>> = indexmap! {};
180198
// If this endpoint response has examples in schema.json, convert them to the
181199
// OpenAPI format and add them to the endpoint response in the OpenAPI document.
182-
if response_def.examples.is_some() {
183-
response_examples = get_openapi_examples(response_def.examples.as_ref().unwrap().clone());
184-
}
200+
let response_examples = if let Some(examples) = &response_def.examples {
201+
get_openapi_examples(examples)
202+
} else {
203+
IndexMap::new()
204+
};
185205
let response = Response {
186206
description: "".to_string(),
187207
headers: Default::default(),

compiler-rs/clients_schema_to_openapi/src/utils.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,15 @@ pub trait SchemaName {
4343
}
4444

4545
impl SchemaName for TypeName {
46+
// Use '.' as the separator: names and paths must be RFC 3986 compliant,
47+
// and ':' output by `TypeName.toString()` is a reserved character.
4648
fn schema_name(&self) -> String {
47-
format!("{}", self)
49+
format!("{}.{}", self.namespace, self.name)
4850
}
4951

5052
fn schema_ref(&self) -> ReferenceOr<Schema> {
5153
ReferenceOr::Reference {
52-
reference: format!("#/components/schemas/{}", self),
54+
reference: format!("#/components/schemas/{}.{}", self.namespace, self.name),
5355
}
5456
}
5557
}

compiler-rs/compiler-wasm-lib/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ serde_json = { workspace = true }
1919
anyhow = { workspace = true }
2020

2121
console_error_panic_hook = { workspace = true, optional = true }
22+
tracing-wasm = "0.2.1"
2223

2324
[dev-dependencies]
2425
wasm-bindgen-test = { workspace = true }

compiler-rs/compiler-wasm-lib/pkg/compiler_wasm_lib.js

+57-4
Original file line numberDiff line numberDiff line change
@@ -149,11 +149,68 @@ module.exports.convert_schema_to_openapi = function(json, flavor) {
149149
}
150150
};
151151

152+
function handleError(f, args) {
153+
try {
154+
return f.apply(this, args);
155+
} catch (e) {
156+
wasm.__wbindgen_exn_store(addHeapObject(e));
157+
}
158+
}
159+
152160
module.exports.__wbindgen_string_new = function(arg0, arg1) {
153161
const ret = getStringFromWasm0(arg0, arg1);
154162
return addHeapObject(ret);
155163
};
156164

165+
module.exports.__wbg_log_c9486ca5d8e2cbe8 = function(arg0, arg1) {
166+
let deferred0_0;
167+
let deferred0_1;
168+
try {
169+
deferred0_0 = arg0;
170+
deferred0_1 = arg1;
171+
console.log(getStringFromWasm0(arg0, arg1));
172+
} finally {
173+
wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);
174+
}
175+
};
176+
177+
module.exports.__wbg_log_aba5996d9bde071f = function(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) {
178+
let deferred0_0;
179+
let deferred0_1;
180+
try {
181+
deferred0_0 = arg0;
182+
deferred0_1 = arg1;
183+
console.log(getStringFromWasm0(arg0, arg1), getStringFromWasm0(arg2, arg3), getStringFromWasm0(arg4, arg5), getStringFromWasm0(arg6, arg7));
184+
} finally {
185+
wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);
186+
}
187+
};
188+
189+
module.exports.__wbg_mark_40e050a77cc39fea = function(arg0, arg1) {
190+
performance.mark(getStringFromWasm0(arg0, arg1));
191+
};
192+
193+
module.exports.__wbindgen_object_drop_ref = function(arg0) {
194+
takeObject(arg0);
195+
};
196+
197+
module.exports.__wbg_measure_aa7a73f17813f708 = function() { return handleError(function (arg0, arg1, arg2, arg3) {
198+
let deferred0_0;
199+
let deferred0_1;
200+
let deferred1_0;
201+
let deferred1_1;
202+
try {
203+
deferred0_0 = arg0;
204+
deferred0_1 = arg1;
205+
deferred1_0 = arg2;
206+
deferred1_1 = arg3;
207+
performance.measure(getStringFromWasm0(arg0, arg1), getStringFromWasm0(arg2, arg3));
208+
} finally {
209+
wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);
210+
wasm.__wbindgen_free(deferred1_0, deferred1_1, 1);
211+
}
212+
}, arguments) };
213+
157214
module.exports.__wbg_new_abda76e883ba8a5f = function() {
158215
const ret = new Error();
159216
return addHeapObject(ret);
@@ -179,10 +236,6 @@ module.exports.__wbg_error_f851667af71bcfc6 = function(arg0, arg1) {
179236
}
180237
};
181238

182-
module.exports.__wbindgen_object_drop_ref = function(arg0) {
183-
takeObject(arg0);
184-
};
185-
186239
const path = require('path').join(__dirname, 'compiler_wasm_lib_bg.wasm');
187240
const bytes = require('fs').readFileSync(path);
188241

Binary file not shown.

compiler-rs/compiler-wasm-lib/pkg/compiler_wasm_lib_bg.wasm.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ export function __wbindgen_add_to_stack_pointer(a: number): number;
66
export function __wbindgen_malloc(a: number, b: number): number;
77
export function __wbindgen_realloc(a: number, b: number, c: number, d: number): number;
88
export function __wbindgen_free(a: number, b: number, c: number): void;
9+
export function __wbindgen_exn_store(a: number): void;

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

+8-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use clients_schema::transform::ExpandConfig;
2222

2323
#[wasm_bindgen]
2424
pub fn convert_schema_to_openapi(json: &str, flavor: &str) -> Result<String, String> {
25-
set_panic_hook();
25+
setup_hooks();
2626
convert0(json, flavor).map_err(|err| err.to_string())
2727
}
2828

@@ -50,7 +50,7 @@ fn convert0(json: &str, flavor: &str) -> anyhow::Result<String> {
5050
Ok(result)
5151
}
5252

53-
pub fn set_panic_hook() {
53+
pub fn setup_hooks() {
5454
// When the `console_error_panic_hook` feature is enabled, we can call the
5555
// `set_panic_hook` function at least once during initialization, and then
5656
// we will get better error messages if our code ever panics.
@@ -59,4 +59,10 @@ pub fn set_panic_hook() {
5959
// https://github.com/rustwasm/console_error_panic_hook#readme
6060
#[cfg(feature = "console_error_panic_hook")]
6161
console_error_panic_hook::set_once();
62+
63+
use std::sync::Once;
64+
static SET_TRACING: Once = Once::new();
65+
SET_TRACING.call_once(|| {
66+
tracing_wasm::set_as_global_default();
67+
});
6268
}

0 commit comments

Comments
 (0)