Skip to content

impl "multipart/form-data" support#418

Open
drahnr wants to merge 27 commits into
oxidecomputer:mainfrom
drahnr:main
Open

impl "multipart/form-data" support#418
drahnr wants to merge 27 commits into
oxidecomputer:mainfrom
drahnr:main

Conversation

@drahnr

@drahnr drahnr commented Apr 11, 2023

Copy link
Copy Markdown
Contributor

Currently form data handling is not implemented.

This PR has an initial, dirty implementation on which I'll iterate over the next weeks.

Closes #402

@drahnr drahnr force-pushed the main branch 2 times, most recently from 00b610a to 7979fe2 Compare April 11, 2023 16:30
@drahnr drahnr changed the title first take at form data impl "multipart/form-data" support Apr 12, 2023
@drahnr

drahnr commented Apr 12, 2023

Copy link
Copy Markdown
Contributor Author

@ahl if you have a few minutes to spare, it'd be great to get some feedback, in particular on:

  • is Generator the intended place for additional metadata?
  • is the boundary between parsing and generation sufficiently upheld in your opinion?
  • do you expect to panic on failed assumptions - currently the generation uses a filter iterator modifier and hence does not panic.

@ahl ahl left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks very much for submitting this. A great start. I left some scattered comments at varying levels of detail. The highest order bit comment I offer is to please add test cases--at a minimum modifying one of the openapi documents to include a body parameter of this type.


@ahl if you have a few minutes to spare, it'd be great to get some feedback, in particular on:

is Generator the intended place for additional metadata?

I mean... it can, but in the case of forms my inclination is to inline that code in the body method.

is the boundary between parsing and generation sufficiently upheld in your opinion?

I think so modulo the prior comment.

do you expect to panic on failed assumptions - currently the generation uses a filter iterator modifier and hence does not panic.

I mean... sure? It depends? I think the real answer is "yes" except when it would screw up currently working users in unexpected ways... which is hard to say.

Comment thread progenitor-impl/Cargo.toml Outdated
heck = "0.4.1"
getopts = "0.2"
indexmap = "1.9"
itertools = "0.10"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this used?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Woud you mind if I add a deny rule?

@drahnr drahnr Apr 12, 2024

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 4323c11

Comment thread progenitor-impl/src/method.rs Outdated
let typ = self
.type_space
.add_type_with_name(&schema, Some(name))?;
.add_type_with_name(&schema, Some(dbg!(name)))?;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove before merging?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really thought I fixed that..

Comment thread progenitor-impl/src/method.rs Outdated
Comment on lines +878 to +886
let url_renames =
HashMap::from_iter(method.params.iter().filter_map(|param| {
match &param.kind {
OperationParameterKind::Path => {
Some((&param.api_name, &param.name))
}
_ => None,
}
_ => None,
})
.collect();
}));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spurious?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Somewhat yes, collect elides what the elemets are being collected into which I personally find a lot easier to reason about code. Will remove.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.

Comment thread progenitor-impl/src/method.rs Outdated
Comment thread progenitor-impl/src/method.rs Outdated
fn form_from_raw<
S: AsRef<str>,
T: AsRef<[u8]>,
I: Sized + IntoIterator<Item = (S, T)>,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why IntoIterator?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because a form can contain multiple streams. That's the least limiting for an Impl and sufficient for the R..BuilderExt trait. But with the comments below this might become obsolete

Comment thread progenitor-impl/src/lib.rs Outdated
.map(|prop_typ| (prop_name, prop_typ))
})
);
let properties = syn::punctuated::Punctuated::<_, syn::Token![,]>::from_iter(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let properties = syn::punctuated::Punctuated::<_, syn::Token![,]>::from_iter(
let properties =

#(#properties,)* below

@drahnr drahnr Apr 9, 2024

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The above creates a punctuated list, which can both be used with quote to create the correct expansion, while also being iterable, can change to Vec<TokenStream> and use quote!{ #(#propeties),* if desired.

Comment thread progenitor-impl/src/lib.rs Outdated
pub fn as_form<'f>(&'f self) -> impl std::iter::Iterator<Item=(&'static str, &'f [u8])> {
[#properties]
.into_iter()
.filter_map(|(name, val)| val.as_ref().map(|val| (name, val.as_slice())))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean that all val have to be strings?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, not at all. It means all values have to be convertible to a stream of bytes. The representation is limiting and should be improved, i.e. it only allows for basic usage key but not for keys derived from objects where the keys end up as foo[bar].sub

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Are there types we might encounter here other than String and Vec[u8] that satisfy this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any type really, such that we could call as_form for nested types. That was the direction I wanted to go into. Hence avoiding the direct expansion. There are a few pitfalls, i.e. metadata desc such as mime type and filename that are commonly expected but not defined in the openapi spec. I am not yet sure how to handle this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that the PR here is in a state of rough draft/idea/make my immediate needs meet, so I am open to changing direction as you see fit.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gentle ping on this one.

Comment thread progenitor-impl/src/method.rs Outdated
) => {
Some(quote! {
// This uses progenitor_client::RequestBuilderExt which sets up a simple form data based on bytes
.form_from_raw(body.as_form())?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did you think about unrolled the as_form logic here? That seems potentially cleaner than an impl on the generated type.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I try to avoid writing to much code with quote if it can be formalized as a simple trait implementation without many constraints.

@PlamenHristov

PlamenHristov commented Apr 2, 2024

Copy link
Copy Markdown

Hey team,

Do you think the above has the potential to be merged. If not, is there anything I can do to complete the PR so we can merge it ?

@ahl

ahl commented Apr 3, 2024

Copy link
Copy Markdown
Collaborator

@PlamenHristov I think you'd be welcome to make a fork of this work, and resolve conflicts. As I recall this was pretty close. I don't see any tests and that would be another good addition to this work.

@drahnr

drahnr commented Apr 4, 2024

Copy link
Copy Markdown
Contributor Author

Happy to pick this up again, the silence on my last set of comments and the scarcity of time made me question if it's worth continuing. If @ahl believes it's close to merging, I am happy to push it over the finish line. @PlamenHristov if you want to help, adding test cases is probably the most effective way

@PlamenHristov

Copy link
Copy Markdown

@drahnr/ @ahl thank you for both your responses.

@drahnr yeah could do that. Let me know once you believe the implementation is done.

@ahl

ahl commented Apr 4, 2024

Copy link
Copy Markdown
Collaborator

Happy to pick this up again, the silence on my last set of comments and the scarcity of time made me question if it's worth continuing. If @ahl believes it's close to merging, I am happy to push it over the finish line. @PlamenHristov if you want to help, adding test cases is probably the most effective way

@drahnr my apologies for discouraging you. I appreciate the contribution and suffer from the same endemic time scarcity!

@drahnr

drahnr commented Apr 11, 2024

Copy link
Copy Markdown
Contributor Author

@ahl *control is requiring auth. It's not needed unless there are more steps hiding behind it. The PR is still incomplete and requires some more testing.

Comment thread Cargo.toml Outdated
Comment thread progenitor-impl/src/method.rs Outdated
Comment on lines +878 to +886
let url_renames =
HashMap::from_iter(method.params.iter().filter_map(|param| {
match &param.kind {
OperationParameterKind::Path => {
Some((&param.api_name, &param.name))
}
_ => None,
}
_ => None,
})
.collect();
}));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.

Comment thread progenitor-impl/src/method.rs Outdated
Comment thread progenitor-macro/src/lib.rs Outdated

@JMendyk JMendyk left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very much looking forward to this PR being completed!

While testing the changes myself, I encountered two issues.

Comment thread progenitor-impl/src/lib.rs Outdated
Comment thread progenitor-impl/src/method.rs Outdated

@JMendyk JMendyk left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another two observations found while testing the PR and fiddling with progenitor and typify.

Comment thread progenitor-client/src/progenitor_client.rs Outdated
Comment thread progenitor-impl/src/lib.rs Outdated
@drahnr

drahnr commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

@ahl I ran into the same thing again, and if there is commitment to review and merge any further work, I'd be happy to update this (yes, after 2 years hiatus)

Copilot AI review requested due to automatic review settings June 30, 2026 08:27

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

@drahnr

drahnr commented Jun 30, 2026

Copy link
Copy Markdown
Contributor Author

Addressed all review comments
CC @ahl @JMendyk

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

form-data as content type yet to be implemented

5 participants