Add body_tag_from_query_param option
When you provide a string for this option, and the request body type is a union, the query param provided will be treated as a value of std.meta.Tag(Body). Then the associated value will be parsed from the body during the request.
This commit is contained in:
parent
e2281f7c14
commit
57f2bd821e
1 changed files with 58 additions and 3 deletions
|
@ -80,6 +80,11 @@ pub fn EndpointRequest(comptime Endpoint: type) type {
|
||||||
false,
|
false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const union_tag_from_query_param = if (@hasDecl(Endpoint, "body_tag_from_query_param")) blk: {
|
||||||
|
if (!std.meta.trait.is(.Union)(Body)) @compileError("body_tag_from_query_param only valid if body is a union");
|
||||||
|
break :blk @as(?[]const u8, Endpoint.body_tag_from_query_param);
|
||||||
|
} else null;
|
||||||
|
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
|
|
||||||
method: http.Method,
|
method: http.Method,
|
||||||
|
@ -97,9 +102,9 @@ pub fn EndpointRequest(comptime Endpoint: type) type {
|
||||||
//else
|
//else
|
||||||
mdw.ParsePathArgs(Endpoint.path, Args){};
|
mdw.ParsePathArgs(Endpoint.path, Args){};
|
||||||
|
|
||||||
const body_middleware = //if (Body == void)
|
const body_middleware = if (union_tag_from_query_param) |param|
|
||||||
//mdw.injectContext(.{ .body = {} })
|
ParseBodyWithQueryType(Body, param, body_options){}
|
||||||
//else
|
else
|
||||||
mdw.ParseBody(Body, body_options){};
|
mdw.ParseBody(Body, body_options){};
|
||||||
|
|
||||||
const query_middleware = //if (Query == void)
|
const query_middleware = //if (Query == void)
|
||||||
|
@ -109,6 +114,56 @@ pub fn EndpointRequest(comptime Endpoint: type) type {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets a tag from the query param with the given name, then treats the request body
|
||||||
|
/// as the respective union type
|
||||||
|
fn ParseBodyWithQueryType(comptime Union: type, comptime query_param_name: []const u8, comptime options: anytype) type {
|
||||||
|
return struct {
|
||||||
|
pub fn handle(_: @This(), req: anytype, res: anytype, ctx: anytype, next: anytype) !void {
|
||||||
|
const Tag = std.meta.Tag(Union);
|
||||||
|
const Param = @Type(.{ .Struct = .{
|
||||||
|
.fields = &.{.{
|
||||||
|
.name = query_param_name,
|
||||||
|
.field_type = Tag,
|
||||||
|
.default_value = null,
|
||||||
|
.is_comptime = false,
|
||||||
|
.alignment = if (@sizeOf(Tag) == 0) 0 else @alignOf(Tag),
|
||||||
|
}},
|
||||||
|
.decls = &.{},
|
||||||
|
.layout = .Auto,
|
||||||
|
.is_tuple = false,
|
||||||
|
} });
|
||||||
|
const param = try http.urlencode.parse(ctx.allocator, true, Param, ctx.query_string);
|
||||||
|
|
||||||
|
var result: ?Union = null;
|
||||||
|
const content_type = req.headers.get("Content-Type");
|
||||||
|
inline for (comptime std.meta.tags(Tag)) |tag| {
|
||||||
|
if (@field(param, query_param_name) == tag) {
|
||||||
|
std.debug.assert(result == null);
|
||||||
|
const P = std.meta.TagPayload(Union, tag);
|
||||||
|
|
||||||
|
std.log.debug("Deserializing to type {}", .{P});
|
||||||
|
|
||||||
|
var stream = req.body orelse return error.NoBody;
|
||||||
|
result = @unionInit(Union, @tagName(tag), try mdw.parseBodyFromReader(
|
||||||
|
P,
|
||||||
|
options,
|
||||||
|
content_type,
|
||||||
|
stream.reader(),
|
||||||
|
ctx.allocator,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mdw.injectContextValue("body", result.?).handle(
|
||||||
|
req,
|
||||||
|
res,
|
||||||
|
ctx,
|
||||||
|
next,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
fn CallApiEndpoint(comptime Endpoint: type) type {
|
fn CallApiEndpoint(comptime Endpoint: type) type {
|
||||||
return struct {
|
return struct {
|
||||||
pub fn handle(_: @This(), req: anytype, res: anytype, ctx: anytype, _: void) !void {
|
pub fn handle(_: @This(), req: anytype, res: anytype, ctx: anytype, _: void) !void {
|
||||||
|
|
Loading…
Reference in a new issue