Add yaml support (#227)
This commit is contained in:
parent
6eaacdfc93
commit
1ef131a244
|
@ -16,6 +16,7 @@
|
|||
https://github.com/oxidecomputer/progenitor/compare/v0.2.0\...HEAD[Full list of commits]
|
||||
|
||||
* Add support for header parameters (#210)
|
||||
* Add support for YAML input (#227)
|
||||
|
||||
== 0.2.0 (released 2022-09-11)
|
||||
|
||||
|
|
|
@ -1110,6 +1110,7 @@ dependencies = [
|
|||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
|
@ -1145,6 +1146,7 @@ dependencies = [
|
|||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"syn",
|
||||
"thiserror",
|
||||
"typify",
|
||||
|
@ -1163,6 +1165,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_json",
|
||||
"serde_tokenstream",
|
||||
"serde_yaml",
|
||||
"syn",
|
||||
]
|
||||
|
||||
|
@ -1539,6 +1542,19 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.9.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d232d893b10de3eb7258ff01974d6ee20663d8e833263c99409d4b13a0209da"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
"unsafe-libyaml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.10.4"
|
||||
|
@ -1998,6 +2014,12 @@ version = "0.1.9"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-libyaml"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1e5fa573d8ac5f1a856f8d7be41d390ee973daf97c806b2c1a465e4e1406e68"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.7.1"
|
||||
|
|
10
README.md
10
README.md
|
@ -48,7 +48,7 @@ field set to `date` or `date-time`, include
|
|||
+chrono = { version = "0.4", features = ["serde"] }
|
||||
```
|
||||
|
||||
Similarly if there is a `format` field set to `uuid`:
|
||||
Similarly, if there is a `format` field set to `uuid`:
|
||||
|
||||
```diff
|
||||
[dependencies]
|
||||
|
@ -76,7 +76,7 @@ generate_api!(
|
|||
);
|
||||
```
|
||||
|
||||
Note that the macro will be re-evaluated when the OpenAPI json document
|
||||
Note that the macro will be re-evaluated when the `spec` OpenAPI document
|
||||
changes (when its mtime is updated).
|
||||
|
||||
### `build.rs`
|
||||
|
@ -111,7 +111,7 @@ code:
|
|||
include!(concat!(env!("OUT_DIR"), "/codegen.rs"));
|
||||
```
|
||||
|
||||
You'll need to add add the following to `Cargo.toml`:
|
||||
You'll need to add the following to `Cargo.toml`:
|
||||
|
||||
```diff
|
||||
[dependencies]
|
||||
|
@ -143,7 +143,7 @@ Usage:
|
|||
progenitor
|
||||
|
||||
Options:
|
||||
-i INPUT OpenAPI definition document (JSON)
|
||||
-i INPUT OpenAPI definition document (JSON or YAML)
|
||||
-o OUTPUT Generated Rust crate directory
|
||||
-n CRATE Target Rust crate name
|
||||
-v VERSION Target Rust crate version
|
||||
|
@ -155,7 +155,7 @@ For example:
|
|||
|
||||
This will produce a package in the specified directory. The output has no
|
||||
persistent dependency on Progenitor including the `progenitor-client` crate.
|
||||
Here's a excerpt from the emitted `Cargo.toml`:
|
||||
Here is an excerpt from the emitted `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
|
|
|
@ -31,3 +31,4 @@ dropshot = { git = "https://github.com/oxidecomputer/dropshot", default-features
|
|||
expectorate = "1.0"
|
||||
http = "0.2.8"
|
||||
hyper = "0.14.23"
|
||||
serde_yaml = "0.9"
|
||||
|
|
|
@ -1,23 +1,42 @@
|
|||
// Copyright 2022 Oxide Computer Company
|
||||
|
||||
use std::{fs::File, path::PathBuf};
|
||||
use std::{
|
||||
fs::File,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use progenitor_impl::{
|
||||
GenerationSettings, Generator, InterfaceStyle, TagStyle, TypePatch,
|
||||
};
|
||||
|
||||
use openapiv3::OpenAPI;
|
||||
|
||||
fn load_api<P>(p: P) -> OpenAPI
|
||||
where
|
||||
P: AsRef<Path> + std::clone::Clone + std::fmt::Debug,
|
||||
{
|
||||
let mut f = File::open(p.clone()).unwrap();
|
||||
match serde_json::from_reader(f) {
|
||||
Ok(json_value) => json_value,
|
||||
_ => {
|
||||
f = File::open(p.clone()).unwrap();
|
||||
serde_yaml::from_reader(f).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn verify_apis(openapi_file: &str) {
|
||||
let mut in_path = PathBuf::from("../sample_openapi");
|
||||
in_path.push(format!("{}.json", openapi_file));
|
||||
in_path.push(openapi_file);
|
||||
let openapi_stem = openapi_file.split('.').next().unwrap();
|
||||
|
||||
let file = File::open(in_path).unwrap();
|
||||
let spec = serde_json::from_reader(file).unwrap();
|
||||
let spec = load_api(in_path);
|
||||
|
||||
let mut generator = Generator::default();
|
||||
let output = generator.generate_text_normalize_comments(&spec).unwrap();
|
||||
expectorate::assert_contents(
|
||||
format!("tests/output/{}-positional.out", openapi_file),
|
||||
format!("tests/output/{}-positional.out", openapi_stem),
|
||||
&output,
|
||||
);
|
||||
|
||||
|
@ -41,7 +60,7 @@ fn verify_apis(openapi_file: &str) {
|
|||
);
|
||||
let output = generator.generate_text_normalize_comments(&spec).unwrap();
|
||||
expectorate::assert_contents(
|
||||
format!("tests/output/{}-builder.out", openapi_file),
|
||||
format!("tests/output/{}-builder.out", openapi_stem),
|
||||
&output,
|
||||
);
|
||||
|
||||
|
@ -53,34 +72,39 @@ fn verify_apis(openapi_file: &str) {
|
|||
let output = generator.generate_text_normalize_comments(&spec).unwrap();
|
||||
println!("{output}");
|
||||
expectorate::assert_contents(
|
||||
format!("tests/output/{}-builder-tagged.out", openapi_file),
|
||||
format!("tests/output/{}-builder-tagged.out", openapi_stem),
|
||||
&output,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_keeper() {
|
||||
verify_apis("keeper");
|
||||
verify_apis("keeper.json");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buildomat() {
|
||||
verify_apis("buildomat");
|
||||
verify_apis("buildomat.json");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nexus() {
|
||||
verify_apis("nexus");
|
||||
verify_apis("nexus.json");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_propolis_server() {
|
||||
verify_apis("propolis-server");
|
||||
verify_apis("propolis-server.json");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_param_override() {
|
||||
verify_apis("param-overrides");
|
||||
verify_apis("param-overrides.json");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_yaml() {
|
||||
verify_apis("param-overrides.yaml");
|
||||
}
|
||||
|
||||
// TODO this file is full of inconsistencies and incorrectly specified types.
|
||||
|
@ -89,5 +113,5 @@ fn test_param_override() {
|
|||
#[ignore]
|
||||
#[test]
|
||||
fn test_github() {
|
||||
verify_apis("api.github.com");
|
||||
verify_apis("api.github.com.json");
|
||||
}
|
||||
|
|
|
@ -18,5 +18,6 @@ quote = "1.0"
|
|||
schemars = "0.8.11"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
serde_yaml = "0.9"
|
||||
serde_tokenstream = "0.1.6"
|
||||
syn = "1.0"
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
// Copyright 2022 Oxide Computer Company
|
||||
|
||||
use std::{collections::HashMap, fmt::Display, path::Path};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fmt::Display,
|
||||
fs::File,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use openapiv3::OpenAPI;
|
||||
use proc_macro::TokenStream;
|
||||
|
@ -33,7 +38,7 @@ use syn::LitStr;
|
|||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// The `spec` key is required; it is the OpenAPI document from which the
|
||||
/// The `spec` key is required; it is the OpenAPI document (JSON or YAML) from which the
|
||||
/// client is derived.
|
||||
///
|
||||
/// The optional `interface` lets you specify either a `Positional` argument or
|
||||
|
@ -158,6 +163,16 @@ impl syn::parse::Parse for ClosureOrPath {
|
|||
}
|
||||
}
|
||||
|
||||
fn open_file(
|
||||
path: PathBuf,
|
||||
span: proc_macro2::Span,
|
||||
) -> Result<File, syn::Error> {
|
||||
File::open(path.clone()).map_err(|e| {
|
||||
let path_str = path.to_string_lossy();
|
||||
syn::Error::new(span, format!("couldn't read file {}: {}", path_str, e))
|
||||
})
|
||||
}
|
||||
|
||||
fn do_generate_api(item: TokenStream) -> Result<TokenStream, syn::Error> {
|
||||
let (spec, settings) = if let Ok(spec) = syn::parse::<LitStr>(item.clone())
|
||||
{
|
||||
|
@ -214,19 +229,19 @@ fn do_generate_api(item: TokenStream) -> Result<TokenStream, syn::Error> {
|
|||
let path = dir.join(spec.value());
|
||||
let path_str = path.to_string_lossy();
|
||||
|
||||
let oapi: OpenAPI =
|
||||
serde_json::from_reader(std::fs::File::open(&path).map_err(|e| {
|
||||
syn::Error::new(
|
||||
spec.span(),
|
||||
format!("couldn't read file {}: {}", path_str, e),
|
||||
)
|
||||
})?)
|
||||
.map_err(|e| {
|
||||
syn::Error::new(
|
||||
spec.span(),
|
||||
format!("failed to parse {}: {}", path_str, e),
|
||||
)
|
||||
})?;
|
||||
let mut f = open_file(path.clone(), spec.span())?;
|
||||
let oapi: OpenAPI = match serde_json::from_reader(f) {
|
||||
Ok(json_value) => json_value,
|
||||
_ => {
|
||||
f = open_file(path.clone(), spec.span())?;
|
||||
serde_yaml::from_reader(f).map_err(|e| {
|
||||
syn::Error::new(
|
||||
spec.span(),
|
||||
format!("failed to parse {}: {}", path_str, e),
|
||||
)
|
||||
})?
|
||||
}
|
||||
};
|
||||
|
||||
let mut builder = Generator::new(&settings);
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ anyhow = "1.0"
|
|||
openapiv3 = "1.0.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
serde_yaml = "0.9"
|
||||
clap = { version = "4.0.32", features = ["derive"] }
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -13,7 +13,7 @@ use progenitor::{GenerationSettings, Generator, InterfaceStyle, TagStyle};
|
|||
|
||||
#[derive(Parser)]
|
||||
struct Args {
|
||||
/// OpenAPI definition document (JSON)
|
||||
/// OpenAPI definition document (JSON or YAML)
|
||||
#[clap(short = 'i', long)]
|
||||
input: String,
|
||||
/// Output directory for Rust crate
|
||||
|
@ -167,11 +167,17 @@ fn main() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_api<P>(p: P) -> Result<OpenAPI>
|
||||
fn load_api<P>(p: P) -> Result<OpenAPI>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
P: AsRef<Path> + std::clone::Clone + std::fmt::Debug,
|
||||
{
|
||||
let f = File::open(p)?;
|
||||
let api = serde_json::from_reader(f)?;
|
||||
let mut f = File::open(p.clone())?;
|
||||
let api = match serde_json::from_reader(f) {
|
||||
Ok(json_value) => json_value,
|
||||
_ => {
|
||||
f = File::open(p)?;
|
||||
serde_yaml::from_reader(f)?
|
||||
}
|
||||
};
|
||||
Ok(api)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
mod load_yaml {
|
||||
progenitor::generate_api!("../sample_openapi/param-overrides.yaml");
|
||||
|
||||
fn _ignore() {
|
||||
let _ = Client::new("").key_get(None, None);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
openapi: 3.0.0
|
||||
info:
|
||||
description: Minimal API for testing parameter overrides
|
||||
title: Parameter override test
|
||||
version: v1
|
||||
components:
|
||||
parameters:
|
||||
key:
|
||||
description: A key parameter that will be overridden by the path spec
|
||||
in: query
|
||||
name: key
|
||||
schema:
|
||||
type: string
|
||||
unique-key:
|
||||
description: A key parameter that will not be overridden by the path spec
|
||||
in: query
|
||||
name: uniqueKey
|
||||
schema:
|
||||
type: string
|
||||
paths:
|
||||
/key:
|
||||
get:
|
||||
description: Gets a key
|
||||
operationId: key.get
|
||||
parameters:
|
||||
- description: The same key parameter that overlaps with the path level parameter
|
||||
in: query
|
||||
name: key
|
||||
schema:
|
||||
type: boolean
|
||||
responses:
|
||||
'200':
|
||||
description: Successful response
|
||||
type: string
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/key'
|
||||
- $ref: '#/components/parameters/unique-key'
|
Loading…
Reference in New Issue