Refactoring controllers

This commit is contained in:
jaina heartles 2022-07-21 23:53:05 -07:00
parent ee0db68c5e
commit dc8877eef3
6 changed files with 145 additions and 97 deletions

View File

@ -6,7 +6,11 @@ const api = @import("./api.zig");
const models = @import("./models.zig");
const Uuid = @import("util").Uuid;
const utils = struct {
pub const auth = @import("./controllers/auth.zig");
pub const notes = @import("./controllers/notes.zig");
pub const actors = @import("./controllers/actors.zig");
pub const utils = struct {
const json_options = if (builtin.mode == .Debug) .{
.whitespace = .{
.indent = .{ .Space = 2 },
@ -20,7 +24,7 @@ const utils = struct {
};
// Responds to a request with a json value
fn respondJson(ctx: *http.server.Context, status: http.Status, value: anytype) !void {
pub fn respondJson(ctx: *http.server.Context, status: http.Status, value: anytype) !void {
var headers = http.Headers.init(ctx.alloc);
defer headers.deinit();
@ -36,11 +40,11 @@ const utils = struct {
try stream.finish();
}
fn respondError(ctx: *http.server.Context, status: http.Status, err: []const u8) !void {
pub fn respondError(ctx: *http.server.Context, status: http.Status, err: []const u8) !void {
return respondJson(ctx, status, .{ .@"error" = err });
}
fn parseRequestBody(comptime T: type, ctx: *http.server.Context) !T {
pub fn parseRequestBody(comptime T: type, ctx: *http.server.Context) !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 = ctx.alloc });
@ -48,11 +52,11 @@ const utils = struct {
return parsed;
}
fn freeRequestBody(value: anytype, alloc: std.mem.Allocator) void {
pub fn freeRequestBody(value: anytype, alloc: std.mem.Allocator) void {
std.json.parseFree(@TypeOf(value), value, .{ .allocator = alloc });
}
fn getApiContext(srv: *RequestServer, ctx: *http.server.Context) !api.ApiContext {
pub fn getApiContext(srv: *RequestServer, ctx: *http.server.Context) !api.ApiContext {
const header = ctx.request.headers.get("authorization") orelse "(null)";
const token = header[("bearer ").len..];
@ -65,89 +69,6 @@ const utils = struct {
const RequestServer = root.RequestServer;
const RouteArgs = http.RouteArgs;
pub fn createNote(srv: *RequestServer, ctx: *http.server.Context, _: RouteArgs) !void {
const user_context = try utils.getApiContext(srv, ctx);
// TODO: defer free ApiContext
const info = try utils.parseRequestBody(api.NoteCreate, ctx);
defer utils.freeRequestBody(info, ctx.alloc);
const note = try srv.api.createNoteUser(info, user_context);
try utils.respondJson(ctx, .created, note);
}
pub fn register(srv: *RequestServer, ctx: *http.server.Context, _: RouteArgs) !void {
const info = try utils.parseRequestBody(api.RegistrationInfo, ctx);
defer utils.freeRequestBody(info, ctx.alloc);
const user = srv.api.register(info) catch |err| switch (err) {
error.UsernameUnavailable => return try utils.respondError(ctx, .bad_request, "Username Unavailable"),
else => return err,
};
try utils.respondJson(ctx, .created, user);
}
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.respondError(ctx, .bad_request, "Invalid UUID");
const note = (try srv.api.getNote(id, ctx.alloc)) orelse return utils.respondError(ctx, .not_found, "Note not found");
defer api.free(ctx.alloc, note);
try utils.respondJson(ctx, .ok, note);
}
pub fn getUser(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.respondError(ctx, .bad_request, "Invalid UUID");
const user = (try srv.api.getActor(id, ctx.alloc)) orelse return utils.respondError(ctx, .not_found, "Note not found");
defer api.free(ctx.alloc, user);
try utils.respondJson(ctx, .ok, user);
}
pub fn react(srv: *RequestServer, ctx: *http.server.Context, args: RouteArgs) !void {
const user_context = try utils.getApiContext(srv, ctx);
// TODO: defer free ApiContext
const note_id = args.get("id") orelse return error.NotFound;
const id = Uuid.parse(note_id) catch return utils.respondError(ctx, .bad_request, "Invalid UUID");
try srv.api.react(id, user_context);
try utils.respondJson(ctx, .created, .{});
}
pub fn listReacts(srv: *RequestServer, ctx: *http.server.Context, args: RouteArgs) !void {
const user_context = try utils.getApiContext(srv, ctx);
// TODO: defer free ApiContext
const note_id = args.get("id") orelse return error.NotFound;
const id = Uuid.parse(note_id) catch return utils.respondError(ctx, .bad_request, "Invalid UUID");
const reacts = try srv.api.listReacts(id, user_context);
try utils.respondJson(ctx, .ok, .{ .items = reacts });
}
pub fn authenticate(srv: *RequestServer, ctx: *http.server.Context, _: RouteArgs) !void {
const user_ctx = try utils.getApiContext(srv, ctx);
// TODO: defer api.free(ctx.alloc, user_ctx);
try utils.respondJson(ctx, .ok, user_ctx.user_context);
}
pub fn login(srv: *RequestServer, ctx: *http.server.Context, _: RouteArgs) !void {
const credentials = try utils.parseRequestBody(struct { username: []const u8, password: []const u8 }, ctx);
defer utils.freeRequestBody(credentials, ctx.alloc);
const actor = srv.api.login(credentials.username, credentials.password, ctx.alloc) catch |err| switch (err) {
error.PasswordVerificationFailed => return utils.respondError(ctx, .bad_request, "Invalid Login"),
else => return err,
};
defer api.free(ctx.alloc, actor);
try utils.respondJson(ctx, .ok, actor);
}
pub fn healthcheck(_: *RequestServer, ctx: *http.server.Context, _: RouteArgs) !void {
try utils.respondJson(ctx, .ok, .{ .status = "ok" });
}

View File

@ -0,0 +1,18 @@
const root = @import("root");
const http = @import("http");
const api = @import("../api.zig");
const Uuid = @import("util").Uuid;
const utils = @import("../controllers.zig").utils;
const RequestServer = root.RequestServer;
const RouteArgs = http.RouteArgs;
pub fn get(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.respondError(ctx, .bad_request, "Invalid UUID");
const user = (try srv.api.getActor(id, ctx.alloc)) orelse return utils.respondError(ctx, .not_found, "Note not found");
defer api.free(ctx.alloc, user);
try utils.respondJson(ctx, .ok, user);
}

View File

@ -0,0 +1,44 @@
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 = @import("../controllers.zig").utils;
const RequestServer = root.RequestServer;
const RouteArgs = http.RouteArgs;
pub fn register(srv: *RequestServer, ctx: *http.server.Context, _: RouteArgs) !void {
const info = try utils.parseRequestBody(api.RegistrationInfo, ctx);
defer utils.freeRequestBody(info, ctx.alloc);
const user = srv.api.register(info) catch |err| switch (err) {
error.UsernameUnavailable => return try utils.respondError(ctx, .bad_request, "Username Unavailable"),
else => return err,
};
try utils.respondJson(ctx, .created, user);
}
pub fn authenticate(srv: *RequestServer, ctx: *http.server.Context, _: RouteArgs) !void {
const user_ctx = try utils.getApiContext(srv, ctx);
// TODO: defer api.free(ctx.alloc, user_ctx);
try utils.respondJson(ctx, .ok, user_ctx.user_context);
}
pub fn login(srv: *RequestServer, ctx: *http.server.Context, _: RouteArgs) !void {
const credentials = try utils.parseRequestBody(struct { username: []const u8, password: []const u8 }, ctx);
defer utils.freeRequestBody(credentials, ctx.alloc);
const actor = srv.api.login(credentials.username, credentials.password, ctx.alloc) catch |err| switch (err) {
error.PasswordVerificationFailed => return utils.respondError(ctx, .bad_request, "Invalid Login"),
else => return err,
};
defer api.free(ctx.alloc, actor);
try utils.respondJson(ctx, .ok, actor);
}

View File

@ -0,0 +1,32 @@
const root = @import("root");
const http = @import("http");
const api = @import("../api.zig");
const models = @import("../models.zig");
const Uuid = @import("util").Uuid;
const utils = @import("../controllers.zig").utils;
const RequestServer = root.RequestServer;
const RouteArgs = http.RouteArgs;
pub const reacts = @import("./notes/reacts.zig");
pub fn create(srv: *RequestServer, ctx: *http.server.Context, _: RouteArgs) !void {
const user_context = try utils.getApiContext(srv, ctx);
// TODO: defer free ApiContext
const info = try utils.parseRequestBody(api.NoteCreate, ctx);
defer utils.freeRequestBody(info, ctx.alloc);
const note = try srv.api.createNoteUser(info, user_context);
try utils.respondJson(ctx, .created, note);
}
pub fn get(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.respondError(ctx, .bad_request, "Invalid UUID");
const note = (try srv.api.getNote(id, ctx.alloc)) orelse return utils.respondError(ctx, .not_found, "Note not found");
defer api.free(ctx.alloc, note);
try utils.respondJson(ctx, .ok, note);
}

View File

@ -0,0 +1,32 @@
const root = @import("root");
const http = @import("http");
const api = @import("../../api.zig");
const models = @import("../../models.zig");
const Uuid = @import("util").Uuid;
const utils = @import("../../controllers.zig").utils;
const RequestServer = root.RequestServer;
const RouteArgs = http.RouteArgs;
pub fn create(srv: *RequestServer, ctx: *http.server.Context, args: RouteArgs) !void {
const user_context = try utils.getApiContext(srv, ctx);
// TODO: defer free ApiContext
const note_id = args.get("id") orelse return error.NotFound;
const id = Uuid.parse(note_id) catch return utils.respondError(ctx, .bad_request, "Invalid UUID");
try srv.api.react(id, user_context);
try utils.respondJson(ctx, .created, .{});
}
pub fn list(srv: *RequestServer, ctx: *http.server.Context, args: RouteArgs) !void {
const user_context = try utils.getApiContext(srv, ctx);
// TODO: defer free ApiContext
const note_id = args.get("id") orelse return error.NotFound;
const id = Uuid.parse(note_id) catch return utils.respondError(ctx, .bad_request, "Invalid UUID");
const reacts = try srv.api.listReacts(id, user_context);
try utils.respondJson(ctx, .ok, .{ .items = reacts });
}

View File

@ -16,16 +16,17 @@ const router = Router{
.routes = &[_]Route{
Route.new(.GET, "/healthcheck", c.healthcheck),
Route.new(.GET, "/auth/authenticate", c.authenticate),
Route.new(.POST, "/auth/login", c.login),
Route.new(.POST, "/auth/register", c.auth.register),
Route.new(.POST, "/auth/login", c.auth.login),
Route.new(.GET, "/auth/authenticate", c.auth.authenticate),
Route.new(.POST, "/notes", c.createNote),
Route.new(.GET, "/notes/:id", c.getNote),
Route.new(.GET, "/notes/:id/reacts", c.listReacts),
Route.new(.POST, "/notes/:id/reacts", c.react),
Route.new(.POST, "/notes", c.notes.create),
Route.new(.GET, "/notes/:id", c.notes.get),
Route.new(.POST, "/auth/register", c.register),
Route.new(.GET, "/users/:id", c.getUser),
Route.new(.GET, "/notes/:id/reacts", c.notes.reacts.list),
Route.new(.POST, "/notes/:id/reacts", c.notes.reacts.create),
Route.new(.GET, "/actors/:id", c.actors.get),
},
};