Skip to content

Commit 52d0d3a

Browse files
authored
Reference parsing replace in spec (#38)
* added ref resolving and readded schema field to template context * added ref resolving and readded schema field to template context * clippy glücklich gemacht
1 parent ee4c0d7 commit 52d0d3a

File tree

8 files changed

+103
-5
lines changed

8 files changed

+103
-5
lines changed

example/specs/basic.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ channels:
1414
message:
1515
payload:
1616
type: object
17+
properties:
18+
name:
19+
type: string
1720
publish:
1821
operationId: userSingedUp
1922
summary: send welcome email to user

example/specs/basic_ref.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ servers:
1010
channels:
1111
user/signedup:
1212
subscribe:
13+
operationId: onUserSignup
1314
message:
1415
$ref: "#/components/messages/userSignUp"
1516
components:

src/parser/common.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ use regex::Regex;
55

66
use crate::asyncapi_model::AsyncAPI;
77

8-
pub fn parse_spec_to_model(path: &Path) -> Result<AsyncAPI, serde_yaml::Error> {
8+
pub fn parse_spec_to_model(path: &Path) -> Result<AsyncAPI, serde_json::Error> {
99
let string_content = fs::read_to_string(path).expect("file could not be read");
1010
// check if file is yaml or json
1111
let parsed = match path.extension() {
1212
Some(ext) => match ext.to_str() {
13-
Some("yaml") => serde_yaml::from_str::<AsyncAPI>(&string_content).unwrap(),
14-
Some("yml") => serde_yaml::from_str::<AsyncAPI>(&string_content).unwrap(),
15-
Some("json") => serde_json::from_str::<AsyncAPI>(&string_content).unwrap(),
13+
Some("yaml") => serde_yaml::from_str::<serde_json::Value>(&string_content).unwrap(),
14+
Some("yml") => serde_yaml::from_str::<serde_json::Value>(&string_content).unwrap(),
15+
Some("json") => serde_json::from_str::<serde_json::Value>(&string_content).unwrap(),
1616
_ => {
1717
panic!("file has no extension");
1818
}
@@ -21,7 +21,10 @@ pub fn parse_spec_to_model(path: &Path) -> Result<AsyncAPI, serde_yaml::Error> {
2121
panic!("file has no extension");
2222
}
2323
};
24-
Ok(parsed)
24+
let with_resolved_references =
25+
crate::parser::resolve_refs::resolve_refs(parsed.clone(), parsed);
26+
let spec = serde_json::from_value::<AsyncAPI>(with_resolved_references)?;
27+
Ok(spec)
2528
}
2629

2730
fn capitalize_first_char(s: &str) -> String {

src/parser/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
mod common;
22
mod pubsub;
3+
mod resolve_refs;
34
mod schema_parser;
45
pub use common::parse_spec_to_model;
56
pub use pubsub::spec_to_pubsub_template_type;
7+
pub use resolve_refs::resolve_refs;
68
pub use schema_parser::schema_parser_mapper;

src/parser/pubsub.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ pub fn spec_to_pubsub_template_type<'a>(
108108
server,
109109
subscribe_channels,
110110
publish_channels,
111+
schema: joined_schemas,
111112
};
112113
Ok(pubsub_template)
113114
}

src/parser/resolve_refs.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
pub fn resolve_json_path(json: serde_json::Value, path: &str) -> serde_json::Value {
2+
let parts = path.split('/').collect::<Vec<&str>>();
3+
let mut current_json = json;
4+
for part in parts {
5+
current_json = current_json[part].clone();
6+
}
7+
current_json
8+
}
9+
10+
pub fn resolve_refs(json: serde_json::Value, root_json: serde_json::Value) -> serde_json::Value {
11+
match json {
12+
serde_json::Value::Object(map) => {
13+
let mut new_map = serde_json::Map::new();
14+
for (key, value) in map {
15+
if key == "$ref" {
16+
if let serde_json::Value::String(string_val) = value {
17+
let correct_json = resolve_json_path(
18+
root_json.clone(),
19+
string_val.trim_start_matches("#/"),
20+
);
21+
return resolve_refs(correct_json, root_json);
22+
} else {
23+
panic!("$ref value is not a string");
24+
}
25+
}
26+
let new_value = resolve_refs(value, root_json.clone());
27+
new_map.insert(key, new_value);
28+
}
29+
serde_json::Value::Object(new_map)
30+
}
31+
serde_json::Value::Array(array) => {
32+
let new_array = array
33+
.into_iter()
34+
.map(|value| resolve_refs(value, root_json.clone()))
35+
.collect();
36+
serde_json::Value::Array(new_array)
37+
}
38+
_ => json,
39+
}
40+
}
41+
42+
#[cfg(test)]
43+
mod tests {
44+
use std::{fs, path::Path};
45+
46+
use super::*;
47+
use crate::*;
48+
49+
const SCHEMAS: [&str; 1] = ["./example/specs/basic_ref.yml"];
50+
51+
//parse file to json, allowed files are yaml and json
52+
fn parse_test(path: &Path) -> serde_json::Value {
53+
let string_content = fs::read_to_string(path).expect("file could not be read");
54+
// check if file is yaml or json
55+
let parsed: serde_json::Value = match path.extension() {
56+
Some(ext) => match ext.to_str() {
57+
Some("yaml") => serde_yaml::from_str::<serde_json::Value>(&string_content).unwrap(),
58+
Some("yml") => serde_yaml::from_str::<serde_json::Value>(&string_content).unwrap(),
59+
Some("json") => serde_json::from_str::<serde_json::Value>(&string_content).unwrap(),
60+
_ => {
61+
panic!("file has no extension");
62+
}
63+
},
64+
None => {
65+
panic!("file has no extension");
66+
}
67+
};
68+
parsed
69+
}
70+
71+
#[test]
72+
fn resolves_refs() {
73+
for schema_paths in SCHEMAS {
74+
let definition = parse_test(Path::new(schema_paths));
75+
let resolved: serde_json::Value = resolve_refs(definition.clone(), definition.clone());
76+
let filename_without_extension = Path::new(schema_paths)
77+
.file_stem()
78+
.unwrap()
79+
.to_str()
80+
.unwrap();
81+
let out_dir = Path::new("./test_output/{}.rs").join(filename_without_extension);
82+
utils::write_to_path_create_dir(&resolved.to_string(), &out_dir).unwrap();
83+
}
84+
}
85+
}

src/template_model/pubsub_template.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pub struct PubsubTemplate<'a> {
99
pub server: &'a Server,
1010
pub subscribe_channels: Vec<(&'a String, &'a Operation)>,
1111
pub publish_channels: Vec<(&'a String, &'a Operation)>,
12+
pub schema: String,
1213
}
1314

1415
impl<'a> From<&PubsubTemplate<'a>> for gtmpl::Value {

templates/handler.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ use std::time;
22
use async_nats::{Client, Message};
33
use crate::publish_message;
44

5+
{{ .schema }}
6+
57
{{ range .subscribe_channels }}
68
pub fn handler_{{ (index . 1).operationId }}(message: Message) {
79
println!("Received message {:#?}", message)

0 commit comments

Comments
 (0)