From 471ca527bb7805cb0a9b348a4fbfbd128926a2fb Mon Sep 17 00:00:00 2001 From: jaina heartles Date: Fri, 16 Dec 2022 02:00:26 -0800 Subject: [PATCH 1/5] Rename mkcol -> mkdir --- src/main/controllers/web.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/controllers/web.zig b/src/main/controllers/web.zig index 835e64e..fe74618 100644 --- a/src/main/controllers/web.zig +++ b/src/main/controllers/web.zig @@ -279,13 +279,13 @@ const drive = struct { }; const Action = enum { - mkcol, + mkdir, }; pub const Body = struct { action: Action, data: union(Action) { - mkcol: struct { + mkdir: struct { name: []const u8, }, }, @@ -294,7 +294,7 @@ const drive = struct { pub fn handler(req: anytype, res: anytype, srv: anytype) !void { if (req.body.action != req.body.data) return error.BadRequest; switch (req.body.data) { - .mkcol => |data| { + .mkdir => |data| { _ = try srv.driveMkdir(req.args.path, data.name); // TODO From e2281f7c14ff89248ac6de1b8734177fe2d96533 Mon Sep 17 00:00:00 2001 From: jaina heartles Date: Fri, 16 Dec 2022 02:01:26 -0800 Subject: [PATCH 2/5] rename parseBodyFromRequest to parseBodyFromReader --- src/http/middleware.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/http/middleware.zig b/src/http/middleware.zig index fb3095a..190a60d 100644 --- a/src/http/middleware.zig +++ b/src/http/middleware.zig @@ -634,7 +634,7 @@ const BaseContentType = enum { other, }; -fn parseBodyFromRequest( +pub fn parseBodyFromReader( comptime T: type, comptime options: ParseBodyOptions, content_type: ?[]const u8, @@ -703,7 +703,7 @@ pub fn ParseBody(comptime Body: type, comptime options: ParseBodyOptions) type { } var stream = req.body orelse return error.NoBody; - const body = try parseBodyFromRequest(Body, options, content_type, stream.reader(), ctx.allocator); + const body = try parseBodyFromReader(Body, options, content_type, stream.reader(), ctx.allocator); defer util.deepFree(ctx.allocator, body); return next.handle( @@ -719,11 +719,11 @@ pub fn parseBody(comptime Body: type) ParseBody(Body) { return .{}; } -test "parseBodyFromRequest" { +test "parseBodyFromReader" { const testCase = struct { fn case(content_type: []const u8, body: []const u8, expected: anytype) !void { var stream = std.io.StreamSource{ .const_buffer = std.io.fixedBufferStream(body) }; - const result = try parseBodyFromRequest(@TypeOf(expected), .{}, content_type, stream.reader(), std.testing.allocator); + const result = try parseBodyFromReader(@TypeOf(expected), .{}, content_type, stream.reader(), std.testing.allocator); defer util.deepFree(std.testing.allocator, result); try util.testing.expectDeepEqual(expected, result); From 57f2bd821ef03c3db53acdcf4702a2aad7d2c519 Mon Sep 17 00:00:00 2001 From: jaina heartles Date: Fri, 16 Dec 2022 02:02:13 -0800 Subject: [PATCH 3/5] 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. --- src/main/controllers.zig | 61 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/src/main/controllers.zig b/src/main/controllers.zig index ae62126..ad971b5 100644 --- a/src/main/controllers.zig +++ b/src/main/controllers.zig @@ -80,6 +80,11 @@ pub fn EndpointRequest(comptime Endpoint: type) type { 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, method: http.Method, @@ -97,9 +102,9 @@ pub fn EndpointRequest(comptime Endpoint: type) type { //else mdw.ParsePathArgs(Endpoint.path, Args){}; - const body_middleware = //if (Body == void) - //mdw.injectContext(.{ .body = {} }) - //else + const body_middleware = if (union_tag_from_query_param) |param| + ParseBodyWithQueryType(Body, param, body_options){} + else mdw.ParseBody(Body, body_options){}; 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 { return struct { pub fn handle(_: @This(), req: anytype, res: anytype, ctx: anytype, _: void) !void { From 6dc84473432791aa0e8728380f014b8a9bc99ed8 Mon Sep 17 00:00:00 2001 From: jaina heartles Date: Fri, 16 Dec 2022 02:05:20 -0800 Subject: [PATCH 4/5] Use body_tag_from_query_param --- src/main/controllers/web.zig | 17 +++++++---------- .../controllers/web/drive/directory.tmpl.html | 5 ++--- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/main/controllers/web.zig b/src/main/controllers/web.zig index fe74618..d1feafd 100644 --- a/src/main/controllers/web.zig +++ b/src/main/controllers/web.zig @@ -282,20 +282,17 @@ const drive = struct { mkdir, }; - pub const Body = struct { - action: Action, - data: union(Action) { - mkdir: struct { - name: []const u8, - }, + pub const body_tag_from_query_param = "action"; + pub const Body = union(Action) { + mkdir: struct { + name: []const u8, }, }; pub fn handler(req: anytype, res: anytype, srv: anytype) !void { - if (req.body.action != req.body.data) return error.BadRequest; - switch (req.body.data) { - .mkdir => |data| { - _ = try srv.driveMkdir(req.args.path, data.name); + switch (req.body) { + .mkdir => |body| { + _ = try srv.driveMkdir(req.args.path, body.name); // TODO try servePage(req, res, srv); diff --git a/src/main/controllers/web/drive/directory.tmpl.html b/src/main/controllers/web/drive/directory.tmpl.html index 5135b6a..277174d 100644 --- a/src/main/controllers/web/drive/directory.tmpl.html +++ b/src/main/controllers/web/drive/directory.tmpl.html @@ -29,12 +29,11 @@ - From d4703a2127517bf9fad4e82f9ead1bd531d64641 Mon Sep 17 00:00:00 2001 From: jaina heartles Date: Fri, 16 Dec 2022 02:37:04 -0800 Subject: [PATCH 5/5] Add delete button to drive page --- src/main/controllers/web.zig | 15 ++++++++ .../controllers/web/drive/directory.tmpl.html | 36 +++++++++++++++++++ src/util/serialize.zig | 6 +++- 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/main/controllers/web.zig b/src/main/controllers/web.zig index d1feafd..5ec6d55 100644 --- a/src/main/controllers/web.zig +++ b/src/main/controllers/web.zig @@ -280,6 +280,7 @@ const drive = struct { const Action = enum { mkdir, + delete, }; pub const body_tag_from_query_param = "action"; @@ -287,6 +288,7 @@ const drive = struct { mkdir: struct { name: []const u8, }, + delete: struct {}, }; pub fn handler(req: anytype, res: anytype, srv: anytype) !void { @@ -297,6 +299,19 @@ const drive = struct { try servePage(req, res, srv); }, + .delete => { + const trimmed_path = std.mem.trim(u8, req.args.path, "/"); + _ = try srv.driveDelete(trimmed_path); + + const dir = trimmed_path[0 .. std.mem.lastIndexOfScalar(u8, trimmed_path, '/') orelse trimmed_path.len]; + const url = try std.fmt.allocPrint(srv.allocator, "{s}/drive/{s}", .{ + req.mount_path, + dir, + }); + defer srv.allocator.free(url); + try res.headers.put("Location", url); + return res.status(.see_other); + }, } } }; diff --git a/src/main/controllers/web/drive/directory.tmpl.html b/src/main/controllers/web/drive/directory.tmpl.html index 277174d..39f4b2d 100644 --- a/src/main/controllers/web/drive/directory.tmpl.html +++ b/src/main/controllers/web/drive/directory.tmpl.html @@ -48,6 +48,25 @@ {$dir.name.?} + + + + + + {#case file |$file|} {#if %user |$u|} @@ -71,6 +90,23 @@ {#if $file.meta.content_type |$t|}{$t}{/if} {$file.meta.size} {$file.meta.created_at} + + + {/switch =} {/for=} diff --git a/src/util/serialize.zig b/src/util/serialize.zig index a7ca8e1..120ca48 100644 --- a/src/util/serialize.zig +++ b/src/util/serialize.zig @@ -266,7 +266,11 @@ pub fn DeserializerContext(comptime Result: type, comptime From: type, comptime } pub fn finish(self: *@This(), allocator: std.mem.Allocator) !Result { - return (try self.deserialize(allocator, Result, self.data, &.{})) orelse error.MissingField; + return (try self.deserialize(allocator, Result, self.data, &.{})) orelse + if (std.meta.fields(Result).len == 0) + return .{} + else + return error.MissingField; } fn getSerializedField(self: *@This(), comptime field_ref: FieldRef) ?From {