Skip to content

Commit a2d5c79

Browse files
committed
Initial commit for schema to openapi converter
1 parent 7094703 commit a2d5c79

File tree

10 files changed

+391
-14
lines changed

10 files changed

+391
-14
lines changed

.editorconfig

Lines changed: 0 additions & 13 deletions
This file was deleted.

openapi-converter/Cargo.lock

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

openapi-converter/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
members = [
33
"clients_schema",
44
"openapi_to_clients_schema",
5+
"clients_schema_to_openapi"
56
]

openapi-converter/clients_schema/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ serde = {version = "1.0", features=["derive"]}
99
serde_json = "1.0"
1010
typed-builder = "0.11"
1111
once_cell = "1.16"
12+
anyhow = "1.0"
1213

1314
[dev-dependencies]
1415
serde_path_to_error = "0.1"

openapi-converter/clients_schema/src/lib.rs

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

18+
use std::collections::HashMap;
19+
use std::fmt::{Display, Formatter};
20+
use anyhow::bail;
1821
// Re-export crates whose types we expose publicly
1922
pub use once_cell;
2023

@@ -30,7 +33,7 @@ const fn is_false(v: &bool) -> bool {
3033
!(*v)
3134
}
3235

33-
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd, Hash)]
36+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Hash)]
3437
pub struct TypeName {
3538
pub namespace: String,
3639
pub name: String
@@ -44,6 +47,14 @@ impl TypeName {
4447
}
4548
}
4649
}
50+
51+
impl Display for TypeName {
52+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
53+
write!(f, "{}:{}", self.namespace, self.name)
54+
}
55+
}
56+
57+
4758
///
4859
/// Type of a value. Used both for property types and nested type definitions.
4960
///
@@ -737,6 +748,10 @@ impl Model {
737748
pub fn from_reader(r: impl std::io::Read) -> Result<Self, serde_json::error::Error> {
738749
serde_json::from_reader(r)
739750
}
751+
752+
pub fn type_registry(&self) -> TypeRegistry {
753+
TypeRegistry::new(self)
754+
}
740755
}
741756

742757
#[cfg(test)]
@@ -772,3 +787,45 @@ mod tests {
772787
};
773788
}
774789
}
790+
791+
pub struct TypeRegistry<'a> {
792+
types: HashMap<&'a TypeName, &'a TypeDefinition>
793+
}
794+
795+
impl<'a> TypeRegistry<'a> {
796+
pub fn new(model: &Model) -> TypeRegistry {
797+
let types = model.types.iter()
798+
.map(|typedef| (typedef.name(), typedef))
799+
.collect::<HashMap<_,_>>();
800+
801+
TypeRegistry{ types }
802+
}
803+
804+
pub fn get(&self, name: &TypeName) -> anyhow::Result<&'a TypeDefinition> {
805+
match self.types.get(name) {
806+
Some(typedef) => Ok(typedef),
807+
None => bail!("No definition for type {}", name),
808+
}
809+
}
810+
811+
// pub fn get_interface(&self, name: &TypeName) -> anyhow::Result<&'a Interface> {
812+
// match self.get(name)? {
813+
// TypeDefinition::Interface(itf) => Ok(itf),
814+
// _ => bail!("Type {} is not an interface", name),
815+
// }
816+
// }
817+
818+
pub fn get_request(&self, name: &TypeName) -> anyhow::Result<&'a Request> {
819+
match self.get(name)? {
820+
TypeDefinition::Request(req) => Ok(req),
821+
_ => bail!("Type {} is not a request", name),
822+
}
823+
}
824+
825+
pub fn get_resppnse(&self, name: &TypeName) -> anyhow::Result<&'a Response> {
826+
match self.get(name)? {
827+
TypeDefinition::Response(resp) => Ok(resp),
828+
_ => bail!("Type {} is not a response", name),
829+
}
830+
}
831+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
[package]
2+
name = "clients_schema_to_openapi"
3+
version = "0.1.0"
4+
edition = "2021"
5+
publish = false
6+
7+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8+
9+
[dependencies]
10+
clients_schema = {path="../clients_schema"}
11+
12+
serde = {version = "1.0", features=["derive"]}
13+
serde_json = "1.0"
14+
serde_path_to_error = "0.1"
15+
serde_ignored = "0.1"
16+
openapiv3 = "1.0"
17+
anyhow = "1.0"
18+
indexmap = "1.9"
19+
convert_case = "0.6"
20+
either_n = "0.2.0"
21+
regex = "1.8"
22+
maplit = "1.0"
23+
24+
tracing = "0.1.37"
25+
tracing-subscriber = "0.3.16"
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
mod paths;
2+
mod schemas;
3+
4+
use std::io::Write;
5+
use std::path::Path;
6+
7+
use clients_schema::{Model, Property, TypeDefinition, TypeName, TypeRegistry, ValueOf};
8+
9+
pub fn convert_schema(path: impl AsRef<Path>, out: impl Write) -> anyhow::Result<()> {
10+
let file = std::fs::File::open(path)?;
11+
12+
let model: Model = serde_json::from_reader(file)?;
13+
let types = model.type_registry();
14+
15+
let mut openapi = openapiv3::OpenAPI::default();
16+
17+
openapi.openapi = "3.1.0".into();
18+
19+
openapi.info = openapiv3::Info {
20+
title: "Elasticsearch API".to_string(),
21+
description: None,
22+
terms_of_service: None,
23+
contact: None,
24+
license: license(&model),
25+
version: "".to_string(),
26+
extensions: Default::default(),
27+
};
28+
29+
// Endpoints
30+
let paths = paths::build_paths(&model.endpoints, &types)?;
31+
32+
openapi.paths = openapiv3::Paths {
33+
paths: paths,
34+
extensions: Default::default(),
35+
};
36+
37+
// Types
38+
let components = openapiv3::Components {
39+
security_schemes: Default::default(),
40+
responses: Default::default(),
41+
parameters: Default::default(),
42+
examples: Default::default(),
43+
request_bodies: Default::default(),
44+
headers: Default::default(),
45+
schemas: Default::default(),
46+
links: Default::default(),
47+
callbacks: Default::default(),
48+
extensions: Default::default(),
49+
};
50+
51+
openapi.components = Some(components);
52+
53+
serde_json::to_writer_pretty(out, &openapi)?;
54+
Ok(())
55+
}
56+
57+
fn license(model: &Model) -> Option<openapiv3::License> {
58+
if let Some(info) = &model.info {
59+
Some(openapiv3::License {
60+
name: info.license.name.clone(),
61+
url: Some(info.license.url.clone()),
62+
extensions: Default::default(),
63+
})
64+
} else {
65+
None
66+
}
67+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
fn main() {
2+
clients_schema_to_openapi::convert_schema(
3+
"../../elasticsearch-specification/output/schema/schema.json",
4+
std::io::stdout()
5+
).unwrap();
6+
7+
}

0 commit comments

Comments
 (0)