Skip to content

Commit

Permalink
Add builder-like pattern to API instances and models (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
nkzou authored Feb 9, 2024
1 parent a0d1537 commit eecaa4e
Show file tree
Hide file tree
Showing 1,572 changed files with 37,862 additions and 10,272 deletions.
1 change: 1 addition & 0 deletions .generator/src/generator/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def cli(specs, output):
env.filters["untitle_case"] = formatter.untitle_case
env.filters["upperfirst"] = utils.upperfirst
env.filters["variable_name"] = formatter.variable_name
env.filters["has_optional_parameter"] = openapi.has_optional_parameter

env.globals["enumerate"] = enumerate
env.globals["responses_by_types"] = openapi.responses_by_types
Expand Down
13 changes: 8 additions & 5 deletions .generator/src/generator/openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ def option_wrapper(name, option, nullable):

def type_to_rust(schema, alternative_name=None, render_nullable=False, render_option=True, render_box=False, version=None):
"""Return Rust type name for the type."""

# special case for additionalProperties: true
if schema is True or schema == {}:
return "serde_json::Value"
Expand Down Expand Up @@ -90,17 +89,21 @@ def get_type_for_attribute(schema, attribute, current_name=None):
return type_to_rust(child_schema, alternative_name=alternative_name)


def get_type_for_parameter(parameter, version=None):
def get_type_for_parameter(parameter, version=None, render_option=None):
"""Return Rust type name for the parameter."""
render_option = True
if "required" in parameter:
render_option = not parameter["required"]
if render_option is None:
render_option = not parameter.get("required")
if "content" in parameter:
assert "in" not in parameter
for content in parameter["content"].values():
return type_to_rust(content["schema"], version=version, render_option=render_option)
return type_to_rust(parameter.get("schema"), version=version, render_option=render_option)

def has_optional_parameter(operation):
for _, parameter in parameters(operation):
if not parameter.get("required"):
return True
return False

def get_type_for_response(response, version):
"""Return Rust type name for the response."""
Expand Down
58 changes: 38 additions & 20 deletions .generator/src/generator/templates/api.j2
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,48 @@ use serde::{Serialize, Deserialize};
use crate::datadog::*;

{%- set structName = name.replace(" ", "")+"API" %}
{% for path, method, operation in operations|sort(attribute="2.operationId", case_sensitive=True) %}
{% for path, method, operation in operations|sort(attribute="2.operationId", case_sensitive=true) %}
{%- set httpMethod = method.upper() %}
{%- set returnType = operation|return_type(version) %}
{%- set formParameter = operation|form_parameter %}
{%- set optionalBody = False if "required" in operation.requestBody and operation.requestBody.required else True %}
{%- set optionalBody = false if "required" in operation.requestBody and operation.requestBody.required else true %}
{%- set optionalParams = operation|parameters|rejectattr('1.required', 'equalto', true) | list %}

{%- for name, parameter in operation|parameters %}
{%- for name, parameter in optionalParams %}
{%- if loop.first %}
/// {{ operation.operationId }}Params is a struct for passing parameters to the method [`{{ structName }}::{{operation.operationId | snake_case}}`]
#[derive(Clone, Debug)]
pub struct {{operation.operationId}}Params {
/// {{operation.operationId}}OptionalParams is a struct for passing parameters to the method [`{{ structName }}::{{operation.operationId | snake_case}}`]
#[derive(Clone, Default, Debug)]
pub struct {{operation.operationId}}OptionalParams {
{%- endif %}
{%- if parameter.description is defined %}
{{parameter.description | block_comment}}
{%- endif %}
{%- if optionalBody and name == operation.get("x-codegen-request-body-name", "body") %}
pub {{name|variable_name}}: Option<{{ get_type_for_parameter(parameter, version) }}>,
{%- else %}
pub {{name|variable_name}}: {{ get_type_for_parameter(parameter, version) }},
{%- if loop.last %}
}
{% endif %}
{%- endfor %}
{%- for name, parameter in optionalParams %}
{%- if loop.first %}
impl {{operation.operationId}}OptionalParams {
{%- endif %}
{%- if parameter.description is defined %}
{{parameter.description | block_comment}}
{%- endif %}
{%- if get_deprecated(model) %}
#[allow(deprecated)]
{%- endif %}
pub fn {{name|variable_name}}(&mut self, value: {{get_type_for_parameter(parameter, version, render_option=false)}}) -> &mut Self {
self.{{name|variable_name}} = Some(value);
self
}
{%- if loop.last %}
}
{% endif %}
{%- endfor %}
{%- endfor %}

{% for path, method, operation in operations|sort(attribute="2.operationId", case_sensitive=True) %}
{% for path, method, operation in operations|sort(attribute="2.operationId", case_sensitive=true) %}
{%- set httpMethod = method.upper() %}
{%- set returnType = operation|return_type(version) %}
{%- set formParameter = operation|form_parameter %}
Expand Down Expand Up @@ -69,15 +84,16 @@ impl {{ structName }} {
Self { config }
}

{% for path, method, operation in operations|sort(attribute="2.operationId", case_sensitive=True) %}
{% for path, method, operation in operations|sort(attribute="2.operationId", case_sensitive=true) %}
{%- set httpMethod = method.upper() %}
{%- set returnType = operation|return_type(version) %}
{%- set formParameter = operation|form_parameter %}
{%- set requiredParams = operation|parameters|selectattr('1.required', 'equalto', true) | list %}
{% if operation.description is defined %}
{{ operation.description | block_comment }}
{%- endif %}
pub async fn {{operation.operationId | snake_case}}(&self{% for name, parameter in operation|parameters %}{% if loop.first %}, params: {{operation.operationId}}Params{% endif %}{% endfor %}) -> Result<Option<{% if returnType %}{{returnType}}{% else %}(){% endif %}>, Error<{{operation.operationId}}Error>> {
match self.{{operation.operationId | snake_case}}_with_http_info({% for name, parameter in operation|parameters %}{% if loop.first %}params{% endif %}{% endfor %}).await {
pub async fn {{operation.operationId | snake_case}}(&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<Option<{% if returnType %}{{returnType}}{% else %}(){% endif %}>, Error<{{operation.operationId}}Error>> {
match self.{{operation.operationId | snake_case}}_with_http_info({% for name, parameter in requiredParams %}{{name|variable_name}}{% if loop.last %}{% if operation|has_optional_parameter %}, {% endif %}{% else %}, {% endif %}{% endfor %}{% if operation|has_optional_parameter %} params{% endif %}).await {
Ok(response_content) => Ok(response_content.entity),
Err(err) => Err(err),
}
Expand All @@ -86,11 +102,13 @@ impl {{ structName }} {
{% if operation.description is defined %}
{{ operation.description | block_comment }}
{%- endif %}
pub async fn {{operation.operationId | snake_case}}_with_http_info(&self{% for name, parameter in operation|parameters %}{% if loop.first %}, params: {{operation.operationId}}Params{% endif %}{% endfor %}) -> Result<ResponseContent<{% if returnType %}{{returnType}}{% else %}(){% endif %}>, Error<{{operation.operationId}}Error>> {
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;

// unbox and build parameters
{%- for name, parameter in operation|parameters %}
{% for name, parameter in operation|parameters if parameter.required != true %}
{%- if loop.first %}
// unbox and build optional parameters
{%- endif %}
let {{name|variable_name}} = params.{{name|variable_name}};
{%- endfor %}

Expand All @@ -111,16 +129,16 @@ impl {{ structName }} {
{% for name, parameter in operation|parameters if parameter.in == "query" %}
{%- set schema = parameter | parameter_schema %}
{%- if parameter.required and schema.type == "array" %}
local_req_builder = local_req_builder.query(&[("{{name}}", &{{name|variable_name}}.into_iter().map(|p| p.to_string()).collect::<Vec<String>>().join(",").to_string())]);
local_req_builder = local_req_builder.query(&[("{{name}}", &{{name|variable_name}}.iter().map(|p| p.to_string()).collect::<Vec<String>>().join(",").to_string())]);
{%- elif not parameter.required and schema.type == "array" %}
if let Some(ref local) = {{name|variable_name}} {
local_req_builder = local_req_builder.query(&[("{{name}}", &local.into_iter().map(|p| p.to_string()).collect::<Vec<String>>().join(",").to_string())]);
local_req_builder = local_req_builder.query(&[("{{name}}", &local.iter().map(|p| p.to_string()).collect::<Vec<String>>().join(",").to_string())]);
};
{%- elif parameter.required %}
local_req_builder = local_req_builder.query(&[("{{name}}", &{{name|variable_name}}.to_string())]);
{%- else %}
if let Some(ref local_str) = {{name|variable_name}} {
local_req_builder = local_req_builder.query(&[("{{name}}", &local_str.to_string())]);
if let Some(ref local_query_param) = {{name|variable_name}} {
local_req_builder = local_req_builder.query(&[("{{name}}", &local_query_param.to_string())]);
};
{%- endif %}
{%- endfor %}
Expand Down
22 changes: 12 additions & 10 deletions .generator/src/generator/templates/function_mappings.j2
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ pub fn collect_function_calls(world: &mut DatadogWorld) {
{%- for name, operations in apis.items() %}
{%- set apiName = "api_"+name | snake_case %}
{% for _, _, operation in operations %}
{%- set operationParams = operation|parameters|list %}
{%- set requiredParams = operation|parameters|selectattr('1.required', 'equalto', true) | list %}
{%- set optionalParams = operation|parameters|rejectattr('1.required', 'equalto', true) | list %}
fn test_{{version}}_{{ operation['operationId'] | snake_case }}(world: &mut DatadogWorld, _parameters: &HashMap<String, Value>) {
let api = world.api_instances.{{version}}_{{ apiName }}.as_ref().expect("api instance not found");
{%- if operationParams|length > 0 -%}
{%- for parameter in operationParams %}
{%- for parameter in operation|parameters|list %}
{%- set schema = parameter[1] | parameter_schema %}
{%- if parameter[1].required %}
{%- if schema | is_primitive and schema.get("format") == "binary" -%}
Expand All @@ -74,15 +74,17 @@ fn test_{{version}}_{{ operation['operationId'] | snake_case }}(world: &mut Data
};
{%- endif %}
{%- endfor %}
let params = datadog{{ version.upper() }}::api::{{ apiName }}::{{ operation['operationId'] }}Params {
{%- for param in operationParams %}
{{ param[0] | variable_name }},
{%- endfor %}

{%- for parameter in optionalParams %}
{%- if loop.first %}
let params = datadog{{ version.upper() }}::api::{{ apiName }}::{{ operation['operationId'] }}OptionalParams {
{%- endif %}
{{ parameter[0] | variable_name }},
{%- if loop.last %}
};
let response = match block_on(api.{{ operation['operationId'] | snake_case}}_with_http_info(params)) {
{%- else %}
let response = match block_on(api.{{ operation['operationId'] | snake_case}}_with_http_info()) {
{%- endif %}
{%- endfor %}
let response = match block_on(api.{{ operation['operationId'] | snake_case}}_with_http_info({% for name, parameter in requiredParams %}{{name|variable_name}}{% if loop.last %}{% if operation|has_optional_parameter %}, {% endif %}{% else %}, {% endif %}{% endfor %}{% if operation|has_optional_parameter %}params{% endif %})) {
Ok(response) => response,
Err(error) => {
return match error {
Expand Down
2 changes: 1 addition & 1 deletion .generator/src/generator/templates/model_oneof.j2
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
#[serde(untagged)]
pub enum {{name}} {
{%- for oneOf in model.oneOf %}
{%- set dataType = get_type(oneOf, render_nullable=False, render_option=False, version=version) %}
{%- set dataType = get_type(oneOf, render_nullable=false, render_option=false, render_box=false, version=version) %}
{%- set attributeName = (get_name(oneOf) or dataType)|upperfirst %}
{%- if oneOf | is_primitive or oneOf.type == "array" %}
{{attributeName}}({{dataType}}),
Expand Down
22 changes: 16 additions & 6 deletions .generator/src/generator/templates/model_simple.j2
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub struct {{ name }} {
{%- set propertyName = attr|variable_name %}
{%- set required = attr in model.required %}
{%- set nullable = schema.get("nullable", False)%}
{%- set dataType = get_type(schema, alternative_name=name + propertyName, render_nullable=nullable, render_option=not required, render_box=True, version=version) %}
{%- set dataType = get_type(schema, alternative_name=name + propertyName, render_nullable=nullable, render_option=not required, render_box=false, version=version) %}
{%- if schema.description is defined %}
{{ schema.description | block_comment }}
{%- endif %}
Expand All @@ -20,23 +20,22 @@ pub struct {{ name }} {
pub {{propertyName}}: {{dataType}},
{%- endfor %}
{%- if model.additionalProperties is defined and model.additionalProperties != False %}
{%- set dataType = get_type(model.additionalProperties, alternative_name=None, render_nullable=False, render_option=False, render_box=True, version=version) %}
{%- set dataType = get_type(model.additionalProperties, alternative_name=None, render_nullable=False, render_option=False, render_box=false, version=version) %}
#[serde(flatten)]
pub additional_properties: std::collections::BTreeMap<String, {{ dataType }}>,
{%- endif %}
}

impl {{ name }} {
pub fn new({% for attr, schema in model.get("properties", {}).items() if attr in model.required %}{%- set nullable = schema.get("nullable", False)%}{%- set propertyName = attr|variable_name %}{%- set dataType = get_type(schema, alternative_name=name + propertyName, render_nullable=nullable, render_option=False, render_box=True, version=version) %}{{propertyName}}: {{ dataType }}{%- if not loop.last %}, {% endif %}{% endfor %}) -> {{ name }} {
pub fn new({% for attr, schema in model.get("properties", {}).items() if attr in model.required %}{%- set nullable = schema.get("nullable", False)%}{%- set dataType = get_type(schema, alternative_name=name + attr|variable_name, render_nullable=nullable, render_option=False, render_box=false, version=version) %}{{attr|variable_name}}: {{ dataType }}{%- if not loop.last %}, {% endif %}{% endfor %}) -> {{ name }} {
{%- if get_deprecated(model) %}
#[allow(deprecated)]
{%- endif %}
{{ name }} {
{%- for attr, schema in model.get("properties", {}).items() %}
{%- set propertyName = attr|variable_name %}
{%- set required = attr in model.required %}
{%- set nullable = schema.get("nullable", False)%}
{%- set dataType = get_type(schema, alternative_name=name + propertyName, render_nullable=nullable, render_option=not required, render_box=True, version=version) %}
{%- set dataType = get_type(schema, alternative_name=name + attr|variable_name, render_nullable=nullable, render_option=not required, render_box=false, version=version) %}
{%- if attr in model.get("required", []) %}
{{ attr|variable_name }},
{%- else %}
Expand All @@ -48,9 +47,20 @@ impl {{ name }} {
{%- endif %}
}
}
{% for attr, schema in model.get("properties", {}).items() if attr not in model.required %}
{%- set nullable = schema.get("nullable", False)%}
{%- set dataType = get_type(schema, alternative_name=name + attr|variable_name, render_nullable=nullable, render_option=False, render_box=false, version=version) %}
{%- if get_deprecated(model) %}
#[allow(deprecated)]
{%- endif %}
pub fn {{attr|variable_name}}(&mut self, value: {{dataType}}) -> &mut Self {
self.{{attr|variable_name}} = Some(value);
self
}
{% endfor %}
}

{%- if not model.required %}
{% if not model.required %}
impl Default for {{ name }} {
fn default() -> Self {
Self::new()
Expand Down
2 changes: 0 additions & 2 deletions src/datadogV1/api/api_authentication.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,6 @@ impl AuthenticationAPI {
> {
let local_configuration = &self.config;

// unbox and build parameters

let local_client = &local_configuration.client;

let local_uri_str = format!("{}/api/v1/validate", local_configuration.base_path);
Expand Down
Loading

0 comments on commit eecaa4e

Please sign in to comment.