Skip to content

Commit a3c85b9

Browse files
doepnernJacob Große
andauthored
Env changes and bugfixes (#104)
* added replace function * added testing readme * changed env to be more performant * In this commit: readme added more limitations and a few infos to types Ability to share rust type between payloads by giving the schema the same name prop Validation using json schemas at runtime Env variable to turn on and off validation Fixed streams never being generated currently * made service_port a port * made basic example use validation * fixed naming being inconsistent between schema and rust types * added comment for how to use test command * fix wrong env names in handler * simplified message names * fmt * added performance hint for validation * brought policy into scope --------- Co-authored-by: Jacob Große <jacobgrosse@Jacobs-Spectre.fritz.box>
1 parent c451ad9 commit a3c85b9

24 files changed

Lines changed: 233 additions & 73 deletions

File tree

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,16 @@ just service-doc {project-id} # Alternatively, you can use 'cd output/{project-i
5454

5555
Remember to replace `{project-id}` with the name of your generated microservice (`title` field from the provided spec).
5656

57+
## Types
58+
Rust types will be generated in the models folder according to the given payload json schema definitions. Names will be generated according to channels etc, if you want to share a payload type between two messages, make sure to use the same "name" property in the payload. Warning: This will not check if the types of those payloads are actually the same, so make sure to use the same schema or better even, simply a ref to the schema with the name. By default, all defined properties are required and no additional properties are allowed, if you want to use optional types, please modify the generated types after generation or use oneOf/anyOf/allOf to represent optional types.
59+
5760
## Limitations
5861

5962
- Only json payloads are currently supported for automatic deserialization
6063
- Only one server is currently supported and only nats protocol is supported
6164
- Only one message is currently supported per channel, payloads can be choosen freely including anyOf/oneOf/allOf
65+
- The generated rust types are required by default, if you want to use optional types, please modify the generated types after generation or use oneOf/anyOf/allOf to represent optional types
66+
- references in the specification are only suppported inside the same file, external references are not supported
6267

6368
## Contribute
6469

example/specs/basic.yaml

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,35 @@ channels:
1414
summary: User signup notification
1515
message:
1616
payload:
17+
name: userSignUpPayload
1718
type: object
1819
properties:
19-
userSingnedUp:
20+
userName:
2021
type: string
22+
minLength: 3
23+
password:
24+
type: string
25+
minLength: 8
26+
age:
27+
type: number
28+
minimum: 18
2129
publish:
2230
operationId: userSignedUp
2331
summary: send welcome email to user
2432
message:
25-
payload:
33+
payload:
34+
name: userSignUpPayload
2635
type: object
2736
properties:
28-
username:
37+
userName:
2938
type: string
39+
minLength: 3
3040
password:
3141
type: string
42+
minLength: 8
3243
age:
3344
type: number
45+
minimum: 18
3446
user/buy:
3547
subscribe:
3648
summary: User bought something

src/asyncapi_model/message.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,9 @@ use super::{
169169
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
170170
#[serde(rename_all = "camelCase")]
171171
pub struct Message {
172+
#[serde(skip_serializing_if = "Option::is_none", rename = "schema")]
173+
pub payload_schema: Option<String>,
174+
172175
/// Schema definition of the application headers.
173176
/// Schema MUST be of type "object". It **MUST NOT** define the protocol headers.
174177
#[serde(skip_serializing_if = "Option::is_none")]

src/generator/common.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::io::{self, Error};
88
use std::path::Path;
99
use walkdir::WalkDir;
1010

11-
use super::generate_models_folder;
11+
use super::{generate_models_folder, generate_schemas_folder};
1212

1313
/// runs cargo command with options
1414
/// Example: ` cargo_command!("init","--bin","path"); `
@@ -167,6 +167,10 @@ fn separate_files(
167167
generate_models_folder(template_str, context, output_dir);
168168
return Ok(true);
169169
}
170+
if template_path.contains("$$schemas$$") {
171+
generate_schemas_folder(template_str, context, output_dir);
172+
return Ok(true);
173+
}
170174
Ok(false)
171175
}
172176

src/generator/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,7 @@ pub use common::{
66
mod model;
77
pub use model::generate_models_folder;
88

9+
mod schemas;
10+
pub use schemas::generate_schemas_folder;
11+
912
mod template_functions;

src/generator/schemas.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
use super::common::render_write_template;
2+
use crate::parser::common::validate_identifier_string;
3+
use crate::template_context::TemplateContext;
4+
use std::path::Path;
5+
6+
pub fn generate_schemas_folder(
7+
template: impl Into<String> + Clone,
8+
async_config: &TemplateContext,
9+
output_dir: &Path,
10+
) {
11+
async_config
12+
.publish_channels
13+
.iter()
14+
.chain(async_config.subscribe_channels.iter())
15+
.for_each(|(_key, operation)| {
16+
let message = operation.messages.first();
17+
if message.is_none() {
18+
return;
19+
}
20+
let message = message.unwrap();
21+
if message.payload_schema.is_none() {
22+
return;
23+
}
24+
render_write_template(
25+
template.clone(),
26+
&message.clone(),
27+
&output_dir.join(format!(
28+
"{}_payload_schema.json",
29+
validate_identifier_string(&message.unique_id, false)
30+
)),
31+
);
32+
});
33+
}

src/main.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ fn main() {
5151
}
5252
};
5353
check_for_overwrite(output_path, title);
54-
5554
// make output a compilable project in output_path
5655
cargo_command!("init", "--bin", output_path);
5756

src/parser/asyncapi_model_parser/preprocessor.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@ use crate::parser::common::{self, validate_identifier_string};
66
pub fn preprocess_schema(spec: serde_json::Value) -> serde_json::Value {
77
let with_message_names = fill_message_and_payload_names(spec.clone(), spec, false, false, None);
88
let resolved_refs = resolve_refs(with_message_names.clone(), with_message_names);
9+
let with_payload_schemas = duplicate_payload_schemas(resolved_refs.clone(), resolved_refs);
910
let mut seen = HashSet::new();
10-
sanitize_operation_ids_and_check_duplicate(resolved_refs.clone(), resolved_refs, &mut seen)
11+
sanitize_operation_ids_and_check_duplicate(
12+
with_payload_schemas.clone(),
13+
with_payload_schemas,
14+
&mut seen,
15+
)
1116
}
1217

1318
pub fn sanitize_operation_ids_and_check_duplicate(
@@ -93,6 +98,41 @@ pub fn resolve_refs(json: serde_json::Value, root_json: serde_json::Value) -> se
9398
}
9499
}
95100

101+
pub fn duplicate_payload_schemas(
102+
json: serde_json::Value,
103+
root_json: serde_json::Value,
104+
) -> serde_json::Value {
105+
match json {
106+
serde_json::Value::Object(map) => {
107+
let mut new_map = serde_json::Map::new();
108+
for (key, value) in map {
109+
if key == "payload" {
110+
if let serde_json::Value::Object(schema) = value {
111+
// insert schema as json string
112+
new_map.insert(
113+
"schema".into(),
114+
serde_json::Value::String(serde_json::to_string(&schema).unwrap()),
115+
);
116+
new_map.insert("payload".into(), serde_json::Value::Object(schema.clone()));
117+
}
118+
} else {
119+
let new_value = duplicate_payload_schemas(value, root_json.clone());
120+
new_map.insert(key, new_value);
121+
}
122+
}
123+
serde_json::Value::Object(new_map)
124+
}
125+
serde_json::Value::Array(array) => {
126+
let new_array = array
127+
.into_iter()
128+
.map(|value| duplicate_payload_schemas(value, root_json.clone()))
129+
.collect();
130+
serde_json::Value::Array(new_array)
131+
}
132+
_ => json,
133+
}
134+
}
135+
96136
pub fn fill_message_and_payload_names(
97137
json: serde_json::Value,
98138
root_json: serde_json::Value,

src/parser/json_schema_parser/array_schema.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ pub fn parse_array_schema(
3535
// ))
3636
Ok(RustSchemaRepresentation {
3737
unique_id: identifyer,
38+
original_key: property_name.to_string(),
3839
struct_reference: format!("Vec<{}>", item_type),
3940
model_definition: "".to_string(),
4041
related_models: vec![],

src/parser/json_schema_parser/enum_schema.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ pub fn parse_enum_schema(
3333
string_builder.push_str("}\n");
3434
Ok(RustSchemaRepresentation {
3535
unique_id: identifyer.clone(),
36+
original_key: property_name.to_string(),
3637
struct_reference: identifyer,
3738
model_definition: string_builder,
3839
model_type: "enum".to_string(),

0 commit comments

Comments
 (0)