Skip to content

Commit b05b303

Browse files
committed
OpenAPI: update path separator, output examples as json
1 parent dbb89db commit b05b303

File tree

9 files changed

+40639
-24041
lines changed

9 files changed

+40639
-24041
lines changed

compiler-rs/clients_schema_to_openapi/.spectral.yaml

Lines changed: 3 additions & 0 deletions
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

Lines changed: 2277 additions & 864 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 6 additions & 2 deletions
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

Lines changed: 25 additions & 13 deletions
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

@@ -122,28 +121,40 @@ 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+
Some(text) => {
130+
match serde_json::from_str::<serde_json::Value>(&text) {
131+
Ok(json) => Some(json),
132+
// Cannot parse json: assume it's text (e.g. cat requests)
133+
// FIXME: should be validated by looking at the media-type
134+
Err(_) => Some(serde_json::Value::String(text.clone()))
135+
}
136+
}
137+
};
138+
128139
let openapi_example = Example {
129-
value: Some(json!(schema_example.value)),
140+
value: example,
130141
description: schema_example.description.clone(),
131142
summary: schema_example.summary.clone(),
132143
external_value: None,
133144
extensions: Default::default(),
134145
};
135146
openapi_examples.insert(name.clone(), ReferenceOr::Item(openapi_example));
136147
}
137-
return openapi_examples;
148+
openapi_examples
138149
}
139150

140-
141-
let mut request_examples: IndexMap<String, ReferenceOr<Example>> = indexmap! {};
142151
// If this endpoint request has examples in schema.json, convert them to the
143152
// 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-
}
153+
let request_examples = if let Some(examples) = &request.examples {
154+
get_openapi_examples(examples)
155+
} else {
156+
IndexMap::new()
157+
};
147158

148159
let request_body = tac.convert_request(request)?.map(|schema| {
149160
let media = MediaType {
@@ -176,12 +187,13 @@ pub fn add_endpoint(
176187
// FIXME: buggy for responses with no body
177188
// TODO: handle binary responses
178189
let response_def = tac.model.get_response(endpoint.response.as_ref().unwrap())?;
179-
let mut response_examples: IndexMap<String, ReferenceOr<Example>> = indexmap! {};
180190
// If this endpoint response has examples in schema.json, convert them to the
181191
// 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-
}
192+
let response_examples = if let Some(examples) = &response_def.examples {
193+
get_openapi_examples(examples)
194+
} else {
195+
IndexMap::new()
196+
};
185197
let response = Response {
186198
description: "".to_string(),
187199
headers: Default::default(),

compiler-rs/clients_schema_to_openapi/src/utils.rs

Lines changed: 4 additions & 2 deletions
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
}
17.6 KB
Binary file not shown.

0 commit comments

Comments
 (0)