Skip to content

Commit

Permalink
support default list of enum values for operation params in python se…
Browse files Browse the repository at this point in the history
…rver codegen
  • Loading branch information
wkarwacki committed Nov 29, 2024
1 parent 38501f1 commit dcc67cd
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 12 deletions.
2 changes: 1 addition & 1 deletion src/lib/gen/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ pub(crate) fn go(
Box::new(FmtSrcIfPresent { gen: gen.clone() }.clone()),
);
handlebars.register_helper("fmtType", Box::new(FmtType { gen: gen.clone() }.clone()));
handlebars.register_helper("fmtValue", Box::new(FmtValue { gen: gen.clone() }.clone()));
handlebars.register_helper("fmtValue", Box::new(FmtValue { gen: gen.clone(), context: context.clone() }.clone()));

handlebars.register_helper("filterNonconst", Box::new(FilterNonconst {}.clone()));
handlebars.register_helper(
Expand Down
2 changes: 1 addition & 1 deletion src/lib/gen/lang.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub(crate) trait Lang {
fn fmt_ref(&self, r#ref: &Ref) -> String;
fn fmt_src(&self, src: &str) -> String;
fn fmt_type(&self, def: &Def, name: &Option<&str>) -> String;
fn fmt_value(&self, json_value: &JsonValue) -> String;
fn fmt_value(&self, json_value: &JsonValue, desc: &Option<Desc>, name: &Option<&str>, context: &Context) -> String;
fn stub_impl(&self, def: &Def, context: &Context) -> String;
}

Expand Down
27 changes: 23 additions & 4 deletions src/lib/gen/python/lang_python.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,24 +168,43 @@ impl Lang for LangPython {
}
}

fn fmt_value(&self, json_value: &Value) -> String {
fn fmt_value(&self, json_value: &Value, desc: &Option<Desc>, name: &Option<&str>, context: &Context) -> String {
let def = desc.as_ref().map(|d| match d {
Desc::Def(def) => def.clone(),
Desc::Ref(r#ref) => context.resolve(&r#ref),
_ => unreachable!(),
});
match json_value {
Value::Bool(val) => if val.clone() { "True" } else { "False" }.to_string(),
Value::Number(val) => val.to_string(),
Value::String(val) => val.clone(),
Value::String(val) => def.as_ref().map(|d| match d {
Def::Enum(Enum { vals, null: _ }) => match vals {
EnumVals::Int(_) => self.fmt_type(d, name) + "." + self.fmt_enum(val).as_str(),
EnumVals::Str(_) => self.fmt_type(d, name) + "." + self.fmt_enum(val).as_str()
},
_ => val.to_string(),
}).unwrap_or(val.clone()),
Value::Array(val) => {
let item = def.map(|d| match d {
Def::Seq(seq) => seq.item,
_ => unreachable!()
});
let name = match item {
Some(Desc::Ref(ref r#ref)) => Some(self.fmt_ref(r#ref)),
_ => None
};
"[".to_string()
+ val
.iter()
.map(|json_value| self.fmt_value(json_value))
.map(|json_value| self.fmt_value(json_value, &item, &name.as_deref(), context))
.collect::<Vec<String>>()
.join(", ")
.as_str()
+ "]"
}
Value::Object(val) => val
.iter()
.map(|(key, json_value)| key.clone() + ": " + self.fmt_value(json_value).as_str())
.map(|(key, json_value)| key.clone() + ": " + self.fmt_value(json_value, &None, &None, context).as_str())
.collect::<Vec<String>>()
.join(", "),
Value::Null => "None".to_string(),
Expand Down
2 changes: 1 addition & 1 deletion src/lib/gen/python/server/templates/router.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ from typing import Annotated, Any
"{{#if ../../useNamespace}}/{{to_kebab_case ../../feature}}{{/if}}{{@../key}}"{{#unless this.res}}, status_code=204, response_class=Response{{/unless}}{{#if this.res.meta}},
responses={200: {"headers": { {{#each this.res.meta}}"{{@key}}": {"schema": {"type": "string"{{#if (eq this.type "const")}}, "const": "{{#each this.val}}{{this}}{{#unless @last}}, {{/unless}}{{/each}}"{{/if}} }}{{/each}} }}}{{/if}}
)
def {{to_snake_case this.name}}({{#if this.req}}{{#if this.req.path}}{{fmtName this.req.path}}: {{>dtoName val=(fmtClass this.req.path)}}{{else}}{{#if (eq this.req.form "multipart/form-data")}}{{fmtName (add this.name "Req")}}: {{>dtoName val=(fmtClass (add this.name "Req"))}} = Depends({{>dtoName val=(fmtClass (add this.name "Req"))}}.of_form){{else if (or (eq this.req.type "obj") (eq this.req.type "seq") (eq this.req.type "map"))}}{{fmtName (add this.name "Req")}}: {{>dtoName val=(fmtClass (add this.name "Req"))}}{{else}}request: {{fmtType this.req}}{{/if}}{{/if}}, {{/if}}{{#each (sortOptionalsLast this.params)}}{{to_snake_case this.name}}: Annotated[{{#if (and (hasKey this "default") (eq this.default null))}}{{fmtOpt (fmtType this)}}{{else}}{{fmtType this}}{{/if}}, {{fmtClass this.loc}}{{#if (ne (to_snake_case this.name) this.name)}}(alias = {{json this.name}}){{/if}}]{{#if (hasKey this "default")}} = {{fmtValue this.default}}{{/if}}, {{/each}}service: {{to_pascal_case ../../feature}}Service = Depends({{to_pascal_case ../../feature}}Service)) -> {{#with this.res}}{{#if (eq this.carrier "stream")}}Streaming{{else}}JSON{{/if}}Response{{else}}None{{/with}}:
def {{to_snake_case this.name}}({{#if this.req}}{{#if this.req.path}}{{fmtName this.req.path}}: {{>dtoName val=(fmtClass this.req.path)}}{{else}}{{#if (eq this.req.form "multipart/form-data")}}{{fmtName (add this.name "Req")}}: {{>dtoName val=(fmtClass (add this.name "Req"))}} = Depends({{>dtoName val=(fmtClass (add this.name "Req"))}}.of_form){{else if (or (eq this.req.type "obj") (eq this.req.type "seq") (eq this.req.type "map"))}}{{fmtName (add this.name "Req")}}: {{>dtoName val=(fmtClass (add this.name "Req"))}}{{else}}request: {{fmtType this.req}}{{/if}}{{/if}}, {{/if}}{{#each (sortOptionalsLast this.params)}}{{to_snake_case this.name}}: Annotated[{{#if (and (hasKey this "default") (eq this.default null))}}{{fmtOpt (fmtType this)}}{{else}}{{fmtType this}}{{/if}}, {{fmtClass this.loc}}{{#if (ne (to_snake_case this.name) this.name)}}(alias = {{json this.name}}){{/if}}]{{#if (hasKey this "default")}} = {{fmtValue this.default this this.name}}{{/if}}, {{/each}}service: {{to_pascal_case ../../feature}}Service = Depends({{to_pascal_case ../../feature}}Service)) -> {{#with this.res}}{{#if (eq this.carrier "stream")}}Streaming{{else}}JSON{{/if}}Response{{else}}None{{/with}}:
{{#if this.res}}content{{#if (filterNonconst this.res.meta)}}, headers{{/if}} = {{/if}}service.{{to_snake_case this.name}}({{#if this.req}}{{#if this.req.path}}{{#with (resolveIfMappedType (fmtClass this.req.path))}}{{fmtName ../req.path}}_to_{{fmtName this}}({{fmtName ../req.path}}){{else}}{{fmtName this.req.path}}{{/with}}{{else}}{{#if (or (eq this.req.form "multipart/form-data") (eq this.req.type "obj") (eq this.req.type "seq") (eq this.req.type "map"))}}{{fmtName (add this.name "Req")}}{{else}}request{{/if}}{{/if}}{{#if this.params}}, {{/if}}{{/if}}{{#each this.params}}{{to_snake_case this.name}}{{#unless @last}}, {{/unless}}{{/each}}){{#if this.res}}
{{#with this.res}}{{#with (resolveIfMappedType (fmtClass this.path))}}content = {{fmtName this}}_to_{{fmtName ../path}}(content){{/with}}{{/with}}
{{#unless (eq this.res.carrier "stream")}}content = jsonable_encoder(content){{/unless}}
Expand Down
6 changes: 5 additions & 1 deletion src/lib/gen/template/fmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use handlebars::{
Output, RenderContext, RenderError, ScopedJson,
};
use serde_json::Value;
use crate::lib::context::Context;

#[derive(Clone)]
pub(crate) struct FmtClass {
Expand Down Expand Up @@ -161,6 +162,7 @@ impl HelperDef for FmtType {
#[derive(Clone)]
pub(crate) struct FmtValue {
pub gen: Box<dyn Gen>,
pub context: Context
}

impl HelperDef for FmtValue {
Expand All @@ -173,8 +175,10 @@ impl HelperDef for FmtValue {
out: &mut dyn Output,
) -> HelperResult {
let json_value: &JsonValue = h.param(0).unwrap().value();
let desc: Result<Desc, _> = serde_json::from_value(h.param(1).unwrap().value().clone());
let name: Result<String, _> = serde_json::from_value(h.param(2).unwrap().value().clone());
Ok(out
.write(self.gen.lang().fmt_value(json_value).as_str())
.write(self.gen.lang().fmt_value(json_value, &desc.ok(), &name.ok().as_deref(), &self.context).as_str())
.unwrap())
}
}
10 changes: 8 additions & 2 deletions src/lib/test/op-params-open-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ paths:
name: queryParamEnumWithDefault
schema:
$ref: '#/components/schemas/SomeEnum'
default:
- second-val
default: second-val
- in: query
name: queryParamSeqEnum
schema:
Expand All @@ -55,6 +54,13 @@ paths:
schema:
$ref: '#/components/schemas/SomeEnum'
default: second-val
- in: query
name: queryParamSeqEnumWithDefault
schema:
type: array
items:
$ref: '#/components/schemas/SomeEnum'
default: [first-val]
operationId: op0Gett
post:
parameters:
Expand Down
9 changes: 7 additions & 2 deletions src/lib/test/op-params-trust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ ops:
- name: queryParamEnumWithDefault
loc: query
path: defs.SomeEnum
default:
- second-val
default: second-val
- name: queryParamSeqEnum
loc: query
type: seq
Expand All @@ -55,6 +54,12 @@ ops:
loc: query
path: defs.SomeEnum
default: second-val
- name: queryParamSeqEnumWithDefault
loc: query
type: seq
item:
path: defs.SomeEnum
default: [first-val]
type: GET
- name: op1Postt
params:
Expand Down

0 comments on commit dcc67cd

Please sign in to comment.