Some refactoring
This commit is contained in:
parent
b12b9c766f
commit
0e7484543e
6 changed files with 157 additions and 7 deletions
|
@ -57,6 +57,8 @@ pub const NoteResponse = struct {
|
|||
created_at: DateTime,
|
||||
};
|
||||
|
||||
pub const CommunityQueryArgs = services.communities.QueryArgs;
|
||||
|
||||
// Frees an api struct and its fields allocated from alloc
|
||||
pub fn free(alloc: std.mem.Allocator, val: anytype) void {
|
||||
switch (@typeInfo(@TypeOf(val))) {
|
||||
|
@ -230,7 +232,7 @@ fn ApiConn(comptime DbConn: type) type {
|
|||
};
|
||||
}
|
||||
|
||||
return error.Unauthorized;
|
||||
return error.TokenRequired;
|
||||
}
|
||||
|
||||
pub fn createCommunity(self: *Self, origin: []const u8) !services.communities.Community {
|
||||
|
@ -334,10 +336,12 @@ fn ApiConn(comptime DbConn: type) type {
|
|||
}
|
||||
|
||||
pub fn createNote(self: *Self, content: []const u8) !NoteResponse {
|
||||
// You cannot post on admin accounts
|
||||
if (self.community.kind == .admin) return error.WrongCommunity;
|
||||
const user_id = self.user_id orelse return error.TokenRequired;
|
||||
|
||||
const note_id = try services.notes.create(self.db, user_id, content);
|
||||
// Only authenticated users can post
|
||||
const user_id = self.user_id orelse return error.TokenRequired;
|
||||
const note_id = try services.notes.create(self.db, user_id, content, self.arena.allocator());
|
||||
|
||||
return self.getNote(note_id) catch |err| switch (err) {
|
||||
error.NotFound => error.Unexpected,
|
||||
|
|
|
@ -151,6 +151,8 @@ pub const QueryArgs = struct {
|
|||
name,
|
||||
host,
|
||||
created_at,
|
||||
|
||||
pub const jsonStringify = util.jsonSerializeEnumAsString;
|
||||
};
|
||||
|
||||
// Max items to fetch
|
||||
|
@ -167,6 +169,8 @@ pub const QueryArgs = struct {
|
|||
direction: enum {
|
||||
ascending,
|
||||
descending,
|
||||
|
||||
pub const jsonStringify = util.jsonSerializeEnumAsString;
|
||||
} = .ascending,
|
||||
|
||||
// Page start parameter
|
||||
|
@ -190,6 +194,8 @@ pub const QueryArgs = struct {
|
|||
page_direction: enum {
|
||||
forward,
|
||||
backward,
|
||||
|
||||
pub const jsonStringify = util.jsonSerializeEnumAsString;
|
||||
} = .forward,
|
||||
};
|
||||
|
||||
|
|
|
@ -39,10 +39,15 @@ pub const GetError = error{
|
|||
DatabaseFailure,
|
||||
NotFound,
|
||||
};
|
||||
const selectStarFromNote = std.fmt.comptimePrint(
|
||||
\\SELECT {s}
|
||||
\\FROM note
|
||||
\\
|
||||
, .{util.comptimeJoin(",", std.meta.fieldNames(Note))});
|
||||
pub fn get(db: anytype, id: Uuid, alloc: std.mem.Allocator) GetError!Note {
|
||||
return db.queryRow(
|
||||
Note,
|
||||
sql.selectStar(Note, "note") ++
|
||||
selectStarFromNote ++
|
||||
\\WHERE id = $1
|
||||
\\LIMIT 1
|
||||
,
|
||||
|
|
|
@ -3,7 +3,8 @@ const root = @import("root");
|
|||
const builtin = @import("builtin");
|
||||
const http = @import("http");
|
||||
const api = @import("./api.zig");
|
||||
const Uuid = @import("util").Uuid;
|
||||
const util = @import("util");
|
||||
const Uuid = util.Uuid;
|
||||
|
||||
pub const auth = @import("./controllers/auth.zig");
|
||||
pub const communities = @import("./controllers/communities.zig");
|
||||
|
@ -57,6 +58,85 @@ pub const utils = struct {
|
|||
return parsed;
|
||||
}
|
||||
|
||||
pub fn parseQueryParams(comptime T: type, ctx: *http.server.Context) !T {
|
||||
// TODO: clean up parsing
|
||||
const path = ctx.request.path;
|
||||
const start = (std.mem.indexOfScalar(u8, path, '?') orelse return error.NoQuery) + 1;
|
||||
const rest = path[start..];
|
||||
const query = std.mem.sliceTo(rest, '#');
|
||||
|
||||
const fake_url = util.Url{
|
||||
.scheme = "",
|
||||
.hostport = "",
|
||||
.path = "",
|
||||
.query = query,
|
||||
.fragment = "",
|
||||
};
|
||||
|
||||
var result: T = .{};
|
||||
inline for (std.meta.fields(T)) |f| {
|
||||
if (fake_url.getQuery(f.name)) |param| {
|
||||
const F = if (comptime @typeInfo(f.field_type) == .Optional) std.meta.Child(f.field_type) else f.field_type;
|
||||
std.log.debug("{}: {s}", .{ F, param });
|
||||
|
||||
@field(result, f.name) = switch (F) {
|
||||
[]const u8 => param,
|
||||
|
||||
else => switch (@typeInfo(F)) {
|
||||
.Struct => if (@hasDecl(F, "parse")) try F.parse(param) else @compileError("Invalid type " ++ @typeName(F)),
|
||||
.Enum => std.meta.stringToEnum(F, param) orelse return error.ParseError,
|
||||
.Int => try std.fmt.parseInt(F, param, 10),
|
||||
|
||||
else => {},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
if (false) {
|
||||
inline for (std.meta.fields(T)) |f| {
|
||||
if (fake_url.getQuery(f.name)) |param| {
|
||||
const F = if (comptime @typeInfo(f.field_type) == .Optional) std.meta.Child(f.field_type) else f.field_type;
|
||||
|
||||
switch (F) {
|
||||
[]const u8 => @field(result, f.name) = param,
|
||||
|
||||
else => switch (@typeInfo(F)) {
|
||||
.Struct,
|
||||
.Opaque,
|
||||
//.Union,
|
||||
=> if (@hasDecl(F, "parse")) {
|
||||
@compileLog(F);
|
||||
if (true) @compileError(F);
|
||||
@field(result, f.name) = try F.parse(param);
|
||||
},
|
||||
|
||||
//.Int => @field(result, f.name) = try std.fmt.parseInt(F, param),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn parseTypeFromQueryParams(comptime T: type, comptime name_prefix: []const u8, url: util.Url) !T {
|
||||
var result: T = .{};
|
||||
inline for (std.meta.fields(T)) |field| {
|
||||
const FieldType = switch (@typeInfo(field.field_type)) {
|
||||
.Optional => |info| info.child,
|
||||
else => field.field_type,
|
||||
};
|
||||
_ = FieldType;
|
||||
_ = result;
|
||||
|
||||
const qualified_name = name_prefix ++ field.name;
|
||||
if (url.getQuery(qualified_name)) |param| {
|
||||
_ = param;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn freeRequestBody(value: anytype, alloc: std.mem.Allocator) void {
|
||||
std.json.parseFree(@TypeOf(value), value, .{ .allocator = alloc });
|
||||
}
|
||||
|
|
|
@ -26,8 +26,10 @@ const router = Router{
|
|||
|
||||
prepare(c.users.create),
|
||||
|
||||
//prepare(c.notes.create),
|
||||
//prepare(c.notes.get),
|
||||
prepare(c.notes.create),
|
||||
prepare(c.notes.get),
|
||||
|
||||
//prepare(c.communities.query),
|
||||
|
||||
//Route.new(.GET, "/notes/:id/reacts", &c.notes.reacts.list),
|
||||
//Route.new(.POST, "/notes/:id/reacts", &c.notes.reacts.create),
|
||||
|
|
53
src/main/query.zig
Normal file
53
src/main/query.zig
Normal file
|
@ -0,0 +1,53 @@
|
|||
const std = @import("std");
|
||||
const ParamIter = struct {
|
||||
remaining: []const u8,
|
||||
target: []const u8,
|
||||
|
||||
fn next(self: *ParamIter) ?[]const u8 {
|
||||
//
|
||||
_ = self;
|
||||
unreachable;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn getParam(str: []const u8, param: []const u8) !?[]const u8 {
|
||||
var iter = ParamIter{ .remaining = str, .target = param };
|
||||
const result = iter.next() orelse return null;
|
||||
if (iter.next() != null) return error.TooMany;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn isScalarType(comptime T: type) bool {
|
||||
return switch (T) {
|
||||
[]const u8 => true,
|
||||
|
||||
else => switch (@typeInfo(T)) {
|
||||
.Int, .Float, .Bool => true,
|
||||
|
||||
.Optional => |info| isScalarType(info.child),
|
||||
.Enum => |info| if (info.is_exhaustive)
|
||||
true
|
||||
else
|
||||
@compileError("Unsupported type " ++ @typeName(T)),
|
||||
|
||||
.Struct => false,
|
||||
else => @compileError("Unsupported type " ++ @typeName(T)),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn parseQueryArgs(comptime T: type, str: []const u8) !T {
|
||||
var result = std.mem.zeroInit(T, .{});
|
||||
_ = str;
|
||||
|
||||
for (std.meta.fields(T)) |field| {
|
||||
const ParseType = switch (@typeInfo(field.field_type)) {
|
||||
.Optional => |info| info.child,
|
||||
else => field.field_type,
|
||||
};
|
||||
|
||||
_ = ParseType;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
Loading…
Reference in a new issue