From caa599986dec8e3d62dd441881341c5b5ce1d008 Mon Sep 17 00:00:00 2001 From: jaina heartles Date: Wed, 13 Jul 2022 00:57:21 -0700 Subject: [PATCH] Reorganize controllers --- src/main/controllers.zig | 88 ++++++++++++++++++++++++++++++++++++++++ src/main/main.zig | 86 ++++----------------------------------- 2 files changed, 95 insertions(+), 79 deletions(-) create mode 100644 src/main/controllers.zig diff --git a/src/main/controllers.zig b/src/main/controllers.zig new file mode 100644 index 0000000..fec645c --- /dev/null +++ b/src/main/controllers.zig @@ -0,0 +1,88 @@ +const std = @import("std"); +const root = @import("root"); +const builtin = @import("builtin"); +const http = @import("http"); +const api = @import("./api.zig"); +const models = @import("./models.zig"); +const Uuid = @import("util").Uuid; + +const utils = struct { + const json_options = if (builtin.mode == .Debug) .{ + .whitespace = .{ + .indent = .{ .Space = 2 }, + .separator = true, + }, + } else .{ + .whitespace = .{ + .indent = .None, + .separator = false, + }, + }; + + // Responds to a request with a json value + fn respondJson(ctx: *http.server.Context, status: http.Status, value: anytype, alloc: std.mem.Allocator) !void { + var headers = http.Headers.init(alloc); + defer headers.deinit(); + + // Don't need to free this k/v pair because they aren't dynamically allocated + try headers.put("Content-Type", "application/json"); + + var stream = try ctx.openResponse(&headers, status); + defer stream.close(); + + const writer = stream.writer(); + try std.json.stringify(value, json_options, writer); + + try stream.finish(); + } + + fn parseRequestBody(comptime T: type, ctx: *http.server.Context, alloc: std.mem.Allocator) !T { + const body = ctx.request.body orelse return error.BodyRequired; + var tokens = std.json.TokenStream.init(body); + const parsed = try std.json.parse(T, &tokens, .{ .allocator = alloc }); + + return parsed; + } + + fn freeRequestBody(value: anytype, alloc: std.mem.Allocator) void { + std.json.parseFree(@TypeOf(value), value, .{ .allocator = alloc }); + } +}; + +const RequestServer = root.RequestServer; +const RouteArgs = http.RouteArgs; + +pub fn createNote(srv: *RequestServer, ctx: *http.server.Context, _: RouteArgs) !void { + const info = try utils.parseRequestBody(api.CreateInfo(models.Note), ctx, srv.alloc); + defer utils.freeRequestBody(info, srv.alloc); + + const note = try srv.api.createNote(info); + + try utils.respondJson(ctx, .created, note, srv.alloc); +} + +pub fn createUser(srv: *RequestServer, ctx: *http.server.Context, _: RouteArgs) !void { + const info = try utils.parseRequestBody(api.CreateInfo(models.User), ctx, srv.alloc); + defer utils.freeRequestBody(info, srv.alloc); + + const user = try srv.api.createUser(info); + + try utils.respondJson(ctx, .created, user, srv.alloc); +} + +pub fn getNote(srv: *RequestServer, ctx: *http.server.Context, args: RouteArgs) !void { + const id_str = args.get("id") orelse return error.NotFound; + const id = Uuid.parse(id_str) catch return utils.respondJson(ctx, .bad_request, .{ .@"error" = "Invalid UUID" }, srv.alloc); + const note = (try srv.api.getNote(id, srv.alloc)) orelse return utils.respondJson(ctx, .not_found, .{ .@"error" = "Note not found" }, srv.alloc); + defer api.free(srv.alloc, note); + + try utils.respondJson(ctx, .ok, note, srv.alloc); +} + +pub fn healthcheck(srv: *RequestServer, ctx: *http.server.Context, _: RouteArgs) !void { + try utils.respondJson(ctx, .ok, .{ .status = "ok" }, srv.alloc); +} + +pub fn notFound(srv: *RequestServer, ctx: *http.server.Context) void { + utils.respondJson(ctx, .not_found, .{ .@"error" = "Not Found" }, srv.alloc) catch unreachable; +} diff --git a/src/main/main.zig b/src/main/main.zig index 072fb5a..188630d 100644 --- a/src/main/main.zig +++ b/src/main/main.zig @@ -6,6 +6,7 @@ const util = @import("util"); const api = @import("./api.zig"); const models = @import("./models.zig"); const Uuid = util.Uuid; +const c = @import("./controllers.zig"); // this thing is overcomplicated and weird. stop this const Router = http.Router(*RequestServer); @@ -13,87 +14,14 @@ const Route = Router.Route; const RouteArgs = http.RouteArgs; const router = Router{ .routes = &[_]Route{ - Route.new(.GET, "/healthcheck", healthcheck), - Route.new(.GET, "/notes/:id", getNote), - Route.new(.POST, "/notes", createNote), - Route.new(.POST, "/users", createUser), + Route.new(.GET, "/healthcheck", c.healthcheck), + Route.new(.GET, "/notes/:id", c.getNote), + Route.new(.POST, "/notes", c.createNote), + Route.new(.POST, "/users", c.createUser), }, }; -const json_options = if (builtin.mode == .Debug) -.{ - .whitespace = .{ - .indent = .{ .Space = 2 }, - .separator = true, - }, -} else .{ - .whitespace = .{ - .indent = .None, - .separator = false, - }, -}; - -// Responds to a request with a json value -fn respondJson(ctx: *http.server.Context, status: http.Status, value: anytype, alloc: std.mem.Allocator) !void { - var headers = http.Headers.init(alloc); - defer headers.deinit(); - - // Don't need to free this k/v pair because they aren't dynamically allocated - try headers.put("Content-Type", "application/json"); - - var stream = try ctx.openResponse(&headers, status); - defer stream.close(); - - const writer = stream.writer(); - try std.json.stringify(value, json_options, writer); - - try stream.finish(); -} - -fn parseRequestBody(comptime T: type, ctx: *http.server.Context, alloc: std.mem.Allocator) !T { - const body = ctx.request.body orelse return error.BodyRequired; - var tokens = std.json.TokenStream.init(body); - const parsed = try std.json.parse(T, &tokens, .{ .allocator = alloc }); - - return parsed; -} - -fn freeRequestBody(value: anytype, alloc: std.mem.Allocator) void { - std.json.parseFree(@TypeOf(value), value, .{ .allocator = alloc }); -} - -fn createNote(srv: *RequestServer, ctx: *http.server.Context, _: RouteArgs) !void { - const info = try parseRequestBody(api.CreateInfo(models.Note), ctx, srv.alloc); - defer freeRequestBody(info, srv.alloc); - - const note = try srv.api.createNote(info); - - try respondJson(ctx, .created, note, srv.alloc); -} - -fn createUser(srv: *RequestServer, ctx: *http.server.Context, _: RouteArgs) !void { - const info = try parseRequestBody(api.CreateInfo(models.User), ctx, srv.alloc); - defer freeRequestBody(info, srv.alloc); - - const user = try srv.api.createUser(info); - - try respondJson(ctx, .created, user, srv.alloc); -} - -fn getNote(srv: *RequestServer, ctx: *http.server.Context, args: RouteArgs) !void { - const id_str = args.get("id") orelse return error.NotFound; - const id = Uuid.parse(id_str) catch return respondJson(ctx, .bad_request, .{ .@"error" = "Invalid UUID" }, srv.alloc); - const note = (try srv.api.getNote(id, srv.alloc)) orelse return respondJson(ctx, .not_found, .{ .@"error" = "Note not found" }, srv.alloc); - defer api.free(srv.alloc, note); - - try respondJson(ctx, .ok, note, srv.alloc); -} - -fn healthcheck(srv: *RequestServer, ctx: *http.server.Context, _: RouteArgs) !void { - try respondJson(ctx, .ok, .{ .status = "ok" }, srv.alloc); -} - -const RequestServer = struct { +pub const RequestServer = struct { alloc: std.mem.Allocator, api: api.ApiServer, @@ -117,7 +45,7 @@ const RequestServer = struct { defer ctx.close(); router.dispatch(self, &ctx, ctx.request.method, ctx.request.path) catch |err| switch (err) { - error.NotFound, error.RouteNotApplicable => respondJson(&ctx, .not_found, .{ .@"error" = "Not Found" }, alloc) catch unreachable, + error.NotFound, error.RouteNotApplicable => c.notFound(self, &ctx), else => unreachable, }; }