Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add builder-like pattern to API instances and models #9

Merged
merged 6 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 2 additions & 0 deletions .generator/src/generator/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ 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_required"] = openapi.has_required
nkzou marked this conversation as resolved.
Show resolved Hide resolved
env.filters["has_optional"] = openapi.has_optional

env.globals["enumerate"] = enumerate
env.globals["responses_by_types"] = openapi.responses_by_types
Expand Down
22 changes: 17 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,30 @@ 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:
if "required" in parameter:
render_option = not parameter["required"]
else:
render_option = True
nkzou marked this conversation as resolved.
Show resolved Hide resolved
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_required(operation):
for _, parameter in parameters(operation):
if parameter.get("required", False) == True:
nkzou marked this conversation as resolved.
Show resolved Hide resolved
return True
return False

def has_optional(operation):
for _, parameter in parameters(operation):
if parameter.get("required", False) != True:
nkzou marked this conversation as resolved.
Show resolved Hide resolved
return True
return False

def get_type_for_response(response, version):
"""Return Rust type name for the response."""
Expand Down
56 changes: 36 additions & 20 deletions .generator/src/generator/templates/api.j2
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,47 @@ 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 %}

{%- for name, parameter in operation|parameters %}
{%- for name, parameter in operation|parameters if parameter.required != true %}
{%- if loop.first %}
nkzou marked this conversation as resolved.
Show resolved Hide resolved
/// {{ 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 operation|parameters if parameter.required != true %}
nkzou marked this conversation as resolved.
Show resolved Hide resolved
{%- 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 +83,15 @@ 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 %}
{% 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 operation|parameters if parameter.required == true %}, {{name|variable_name}}: {{ get_type_for_parameter(parameter, version) }}{% endfor %}{% if operation|has_optional %}, 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 operation|parameters if parameter.required == true %}{{name|variable_name}}{% if loop.last %}{% if operation|has_optional %}, {% endif %}{% else %}, {% endif %}{% endfor %}{% if operation|has_optional %} params{% endif %}).await {
Ok(response_content) => Ok(response_content.entity),
Err(err) => Err(err),
}
Expand All @@ -86,11 +100,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 operation|parameters if parameter.required == true %}, {{name|variable_name}}: {{ get_type_for_parameter(parameter, version) }}{% endfor %}{% if operation|has_optional %}, 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 +127,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
17 changes: 9 additions & 8 deletions .generator/src/generator/templates/function_mappings.j2
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ pub fn collect_function_calls(world: &mut DatadogWorld) {
{%- set operationParams = operation|parameters|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 %}
{%- set schema = parameter[1] | parameter_schema %}
{%- if parameter[1].required %}
Expand All @@ -74,15 +73,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 operationParams if parameter[1].required != true %}
{%- 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 operation|parameters if parameter.required == true %}{{name|variable_name}}{% if loop.last %}{% if operation|has_optional %}, {% endif %}{% else %}, {% endif %}{% endfor %}{% if operation|has_optional %}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
Loading