Skip to content

Commit

Permalink
implement servers both globally and per-operation
Browse files Browse the repository at this point in the history
  • Loading branch information
nkzou committed Feb 20, 2024
1 parent 88180c7 commit a68124d
Show file tree
Hide file tree
Showing 83 changed files with 3,641 additions and 2,807 deletions.
11 changes: 4 additions & 7 deletions .generator/src/generator/templates/api.j2
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,10 @@ impl {{ structName }} {
{{ operation.description | block_comment }}
{%- endif %}
pub async fn {{operation.operationId | snake_case}}_with_http_info(&self{% for name, parameter in requiredParams %}, {{name|variable_name}}: {{ get_type_for_parameter(parameter, version) }}{% endfor %}{% if operation|has_optional_parameter %}, params: {{operation.operationId}}OptionalParams{% endif %}) -> Result<ResponseContent<{% if returnType %}{{returnType}}{% else %}(){% endif %}>, Error<{{operation.operationId}}Error>> {
let local_configuration = &self.config;
{%- if "x-unstable" in operation %}
let operation_id = "{{ version }}.{{ operation.operationId | snake_case }}".to_string();
if self.config.is_unstable_operation_enabled(&operation_id) {
if local_configuration.is_unstable_operation_enabled(&operation_id) {
warn!("Using unstable operation {}", operation_id);
} else {
let local_error = UnstableOperationDisabledError {
Expand All @@ -121,8 +122,6 @@ impl {{ structName }} {
}
{%- endif %}

let local_configuration = &self.config;

{% for name, parameter in operation|parameters if parameter.required != true %}
{%- if loop.first %}
// unbox and build optional parameters
Expand All @@ -134,7 +133,7 @@ impl {{ structName }} {

let local_uri_str = format!(
"{}{{path}}",
local_configuration.base_path
local_configuration.get_operation_host("{{ version }}.{{ operation.operationId | snake_case }}")
{%- for name, parameter in operation|parameters if parameter.in == "path" %}, {{ name|variable_name }}=
{%- if parameter.schema.type == "string" %}
urlencode({{ name|variable_name }}{% if not parameter.required %}.unwrap(){% elif parameter.schema.nullable %}.unwrap(){% endif %}{% if parameter.schema.type == "array" %}.join(",").as_ref(){% endif %})
Expand Down Expand Up @@ -172,9 +171,7 @@ impl {{ structName }} {
{%- endfor %}

// build user agent
if let Some(ref local_user_agent) = local_configuration.user_agent {
local_req_builder = local_req_builder.header(reqwest::header::USER_AGENT, local_user_agent.clone());
}
local_req_builder = local_req_builder.header(reqwest::header::USER_AGENT, local_configuration.user_agent.clone());

// build auth
{%- set authMethods = operation.security if "security" in operation else openapi.security %}
Expand Down
163 changes: 140 additions & 23 deletions .generator/src/generator/templates/configuration.j2
Original file line number Diff line number Diff line change
@@ -1,13 +1,46 @@
{% include "partial_header.j2" %}
use lazy_static::lazy_static;
use std::env;
use std::collections::HashMap;
use log::warn;

#[derive(Debug, Clone)]
pub struct ServerVariable {
pub description: String,
pub default_value: String,
pub enum_values: Vec<String>,
}

#[derive(Debug, Clone)]
pub struct ServerConfiguration {
pub url: String,
pub description: String,
pub variables: HashMap<String, ServerVariable>,
}

impl ServerConfiguration {
pub fn get_url(&self, variables: &HashMap<String, String>) -> String {
let mut url = self.url.clone();
for (name, variable) in &self.variables {
let value = variables.get(name).unwrap_or(&variable.default_value);
if !variable.enum_values.contains(value) && !variable.enum_values.is_empty() {
panic!(
"Value {} for variable {} is not in the enum values",
value, name
);
}
url = url.replace(&format!("{{ '{{{}}}' }}", name), &value);
}
url
}
}

#[non_exhaustive]
#[derive(Debug, Clone)]
pub struct Configuration {
pub base_path: String,
pub user_agent: Option<String>,
pub client: reqwest_middleware::ClientWithMiddleware,
pub(crate) user_agent: String,
pub(crate) client: reqwest_middleware::ClientWithMiddleware,
pub unstable_operations: HashMap<String, bool>,
{%- set authMethods = openapi.security %}
{%- if authMethods %}
{%- for authMethod in authMethods %}
Expand All @@ -19,25 +52,53 @@ pub struct Configuration {
{%- endfor %}
{%- endfor %}
{%- endif %}
unstable_operations: HashMap<String, bool>,
pub server_index: usize,
pub server_variables: HashMap<String, String>,
pub server_operation_index: HashMap<String, usize>,
pub server_operation_variables: HashMap<String, HashMap<String, String>>,

}

impl Configuration {
pub fn new() -> Configuration {
Configuration::default()
}

pub fn client(&mut self, client: reqwest_middleware::ClientWithMiddleware) {
self.client = client;
}

pub fn get_operation_host(&self, operation_str: &str) -> String {
let operation = operation_str.to_string();
if let Some(servers) = OPERATION_SERVERS.get(&operation) {
let server_index = self
.server_operation_index
.get(&operation)
.cloned()
.unwrap_or(0);
return servers
.get(server_index)
.expect(&format!("Server index for operation {operation} not found"))
.get_url(
&self
.server_operation_variables
.get(&operation)
.unwrap_or(&HashMap::new()),
);
}
SERVERS
.get(self.server_index)
.expect("Server index not found.")
.get_url(&self.server_variables)
}

pub fn set_unstable_operation_enabled(&mut self, operation: &str, enabled: bool) -> bool {
if self.unstable_operations.contains_key(operation) {
self.unstable_operations.insert(operation.to_string(), enabled);
return true;
}

warn!(
"Operation {} is not an unstable operation, can't enable/disable",
operation
);

warn!("Operation {operation} is not an unstable operation, can't enable/disable");
false
}

Expand All @@ -46,11 +107,7 @@ impl Configuration {
return self.unstable_operations.get(operation).unwrap().clone();
}

warn!(
"Operation {} is not an unstable operation, is always enabled",
operation
);

warn!("Operation {operation} is not an unstable operation, is always enabled");
false
}

Expand All @@ -66,6 +123,13 @@ impl Configuration {
impl Default for Configuration {
fn default() -> Self {
let http_client = reqwest_middleware::ClientBuilder::new(reqwest::Client::new());
let user_agent = format!(
"datadog-api-client-rust/{} (rust {}; os {}; arch {})",
option_env!("CARGO_PKG_VERSION").unwrap_or("?"),
option_env!("DD_RUSTC_VERSION").unwrap_or("?"),
env::consts::OS,
env::consts::ARCH,
);
let unstable_operations = HashMap::from([
{%- for version, api in apis.items() %}
{%- for operations in api.values() %}
Expand All @@ -79,15 +143,9 @@ impl Default for Configuration {
]);

Configuration {
base_path: "https://api.datadoghq.com".to_owned(),
user_agent: Some(format!(
"datadog-api-client-rust/{} (rust {}; os {}; arch {})",
option_env!("CARGO_PKG_VERSION").unwrap_or("?"),
option_env!("DD_RUSTC_VERSION").unwrap_or("?"),
env::consts::OS,
env::consts::ARCH,
)),
user_agent,
client: http_client.build(),
unstable_operations,
{%- set authMethods = openapi.security %}
{%- if authMethods %}
{%- for authMethod in authMethods %}
Expand All @@ -99,7 +157,66 @@ impl Default for Configuration {
{%- endfor %}
{%- endfor %}
{%- endif %}
unstable_operations,
server_index: 0,
server_variables: HashMap::new(),
server_operation_index: HashMap::new(),
server_operation_variables: HashMap::new(),
}
}
}

{%- macro server_configuration(server) -%}
ServerConfiguration {
url: "{{ server.url }}".into(),
description: "{{ server.description|default("No description provided") }}".into(),
variables: HashMap::from([
{%- for name, variable in server.get("variables", {}).items() %}
(
"{{ name }}".into(),
ServerVariable {
description: "{{ variable.description|default("No description provided") }}".into(),
default_value: "{{ variable.default }}".into(),
enum_values: vec![
{%- for value in variable.enum %}
"{{ value }}".into(),
{%- endfor %}
],
},
),
{%- endfor %}
]),
},
{%- endmacro %}


lazy_static! {
static ref SERVERS: Vec<ServerConfiguration> = {
vec![
{%- for server in openapi.servers %}
{{ server_configuration(server) }}
{%- endfor %}
]
};
static ref OPERATION_SERVERS: HashMap<String, Vec<ServerConfiguration>> = {
HashMap::from([
{%- for version, spec in all_specs.items() %}
{%- for path in spec.paths.values() %}
{%- for operation in path.values() %}
{%- for server in operation.servers %}
{% if loop.first %}
(
"{{ version }}.{{ operation.operationId | snake_case }}".into(),
vec![
{%- endif %}
{{ server_configuration(server) }}
{%- if loop.last %}
],
),
{%- endif %}
{%- endfor %}
{%- endfor %}
{%- endfor %}
{%- endfor %}
])
};
}
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ license = "Apache-2.0"
edition = "2021"

[dependencies]
lazy_static = "1.4.0"
log = "0.4.20"
reqwest = { version = "^0.11", features = ["multipart"] }
reqwest-middleware = "0.1.6"
Expand All @@ -30,7 +31,6 @@ rvcr = { git = "https://github.com/nkzou/rvcr.git", rev = "b8f84bc0dfacd539fdc6f
vcr-cassette = "^2.0"
sha256 = "1.4.0"
tokio = { version = "1.10", features = ["macros", "rt-multi-thread", "time"] }
lazy_static = "1.4.0"
minijinja = "1.0.10"
convert_case = "0.6.0"

Expand Down
Loading

0 comments on commit a68124d

Please sign in to comment.