Skip to content

Commit

Permalink
updates todos mutations to new pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
wmalarski committed Oct 29, 2023
1 parent 0ad477d commit 2f8ea59
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 50 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"formdata",
"marko",
"pkce",
"Postgrest",
"supabase",
"tailwindcss",
"todos",
Expand Down
2 changes: 1 addition & 1 deletion src/routes/+middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { initSupabase } from "../server/supabase";

const handler: MarkoRun.Handler = async (context, next) => {
const cookies: string[] = [];

console.log({ url: context.url });
context.supabase = initSupabase({
context,
saveCookie: (cookie) => {
Expand Down
11 changes: 5 additions & 6 deletions src/routes/sign-in/+handler.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { decode } from "decode-formdata";
import {
magicLinkSignIn,
oauthSignIn,
Expand All @@ -7,15 +6,15 @@ import {
import { buildSearchParams } from "../../utils/searchParams";

export const POST: MarkoRun.Handler = async (context) => {
const decoded = decode(await context.request.formData());
const formData = await context.request.formData();

switch (decoded.action) {
switch (formData.get("action")) {
case "password":
return passwordSignIn({ context, decoded });
return passwordSignIn({ context, formData });
case "oauth":
return oauthSignIn({ context, decoded });
return oauthSignIn({ context, formData });
case "magic-link":
return magicLinkSignIn({ context, decoded });
return magicLinkSignIn({ context, formData });
}

const params = buildSearchParams({ message: "Invalid request" });
Expand Down
29 changes: 29 additions & 0 deletions src/routes/todos/(active,finished,)/+handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {
deleteTask,
insertTask,
updateAllTasks,
updateTask,
} from "../../../server/todos";
import { buildSearchParams } from "../../../utils/searchParams";

export const POST: MarkoRun.Handler = async (context) => {
const formData = await context.request.formData();

switch (formData.get("action")) {
case "delete":
return deleteTask({ context, formData });
case "update":
return updateTask({ context, formData });
case "update-all":
return updateAllTasks({ context, formData });
case "insert":
return insertTask({ context, formData });
}

const params = buildSearchParams({ message: "Invalid request" });
const url = new URL(`${context.url.pathname}?${params}`, context.url);
return new Response(null, {
status: 302,
headers: { location: String(url) },
});
};
14 changes: 7 additions & 7 deletions src/server/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ const getCallbackUrl = (context: MarkoRun.Context) => {

type SignInArgs = {
context: MarkoRun.Context;
decoded: ReturnType<typeof decode>;
formData: FormData;
};

export const magicLinkSignIn = async ({ context, decoded }: SignInArgs) => {
export const magicLinkSignIn = async ({ context, formData }: SignInArgs) => {
const parsed = await safeParseAsync(
object({ email: string([email()]) }),
decoded,
decode(formData),
);

if (!parsed.success) {
Expand All @@ -41,10 +41,10 @@ export const magicLinkSignIn = async ({ context, decoded }: SignInArgs) => {
});
};

export const oauthSignIn = async ({ context, decoded }: SignInArgs) => {
export const oauthSignIn = async ({ context, formData }: SignInArgs) => {
const parsed = await safeParseAsync(
object({ provider: literal("google") }),
decoded,
decode(formData),
);

if (!parsed.success) {
Expand All @@ -69,10 +69,10 @@ export const oauthSignIn = async ({ context, decoded }: SignInArgs) => {
});
};

export const passwordSignIn = async ({ context, decoded }: SignInArgs) => {
export const passwordSignIn = async ({ context, formData }: SignInArgs) => {
const parsed = await safeParseAsync(
object({ email: string([email()]), password: string() }),
decoded,
decode(formData),
);

if (!parsed.success) {
Expand Down
19 changes: 19 additions & 0 deletions src/server/errors.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Issues } from "valibot";
import { buildPath, BuildPathArgs, type RoutePath } from "../utils/paths";
import { buildSearchParams } from "../utils/searchParams";

export const unauthorizedError = () => {
return new Response(JSON.stringify({ error: "Unauthorized" }), {
Expand All @@ -14,6 +15,24 @@ export const redirectToPath = <Path extends RoutePath>(
return new Response(null, { status: 302, headers: { location } });
};

type RedirectCurrentWithQueryArgs = {
context: MarkoRun.Context;
query?: Record<string, unknown>;
variant: "success" | "error";
};

export const redirectCurrentWithQuery = ({
context,
query,
variant,
}: RedirectCurrentWithQueryArgs) => {
const location = `${context.url.pathname}?${buildSearchParams({
...query,
variant,
})}`;
return new Response(null, { status: 302, headers: { location } });
};

export const invalidRequestError = (issues: Issues) => {
return new Response(JSON.stringify(issues), { status: 400 });
};
133 changes: 97 additions & 36 deletions src/server/todos.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
import { PostgrestSingleResponse } from "@supabase/supabase-js";
import { decode } from "decode-formdata";
import { boolean, object, safeParseAsync, string } from "valibot";
import {
invalidRequestError,
redirectCurrentWithQuery,
unauthorizedError,
} from "./errors";

type SelectTasksArgs = {
context: MarkoRun.Context;
limit: number;
Expand All @@ -21,57 +30,109 @@ export const selectTasks = async ({
: paginated;
};

type DeleteTaskArgs = {
type BuildResponseArgs = {
context: MarkoRun.Context;
id: string;
result: PostgrestSingleResponse<unknown>;
};

export const deleteTask = async ({ context, id }: DeleteTaskArgs) => {
return context.supabase.from("Task").delete().eq("id", id);
const buildResponse = ({ context, result }: BuildResponseArgs) => {
if (result.error) {
return redirectCurrentWithQuery({
context,
query: { message: result.error.message },
variant: "error",
});
}

return redirectCurrentWithQuery({
context,
query: { message: "Success" },
variant: "success",
});
};

type UpdateTaskArgs = {
type TaskMutationArgs = {
context: MarkoRun.Context;
id: string;
text: string;
finished: boolean;
formData: FormData;
};

export const updateTask = async ({
context,
id,
finished,
text,
}: UpdateTaskArgs) => {
return context.supabase.from("Task").update({ finished, text }).eq("id", id);
export const deleteTask = async ({ context, formData }: TaskMutationArgs) => {
const parsed = await safeParseAsync(
object({ id: string() }),
decode(formData),
);

if (!parsed.success) {
return invalidRequestError(parsed.issues);
}

const result = await context.supabase
.from("Task")
.delete()
.eq("id", parsed.output.id);

return buildResponse({ context, result });
};

type UpdateAllTasksArgs = {
context: MarkoRun.Context;
finished: boolean;
export const updateTask = async ({ context, formData }: TaskMutationArgs) => {
const parsed = await safeParseAsync(
object({ text: string(), finished: boolean(), id: string() }),
decode(formData, { booleans: ["finished"] }),
);

if (!parsed.success) {
return invalidRequestError(parsed.issues);
}

const result = await context.supabase
.from("Task")
.update({ finished: parsed.output.finished, text: parsed.output.text })
.eq("id", parsed.output.id);

return buildResponse({ context, result });
};

export const updateAllTasks = async ({
context,
finished,
}: UpdateAllTasksArgs) => {
return context.supabase.from("Task").update({ finished });
};
formData,
}: TaskMutationArgs) => {
const parsed = await safeParseAsync(
object({ finished: boolean() }),
decode(formData, { booleans: ["finished"] }),
);

type InsertTaskArgs = {
context: MarkoRun.Context;
text: string;
finished: boolean;
userId: string;
};
if (!parsed.success) {
return invalidRequestError(parsed.issues);
}

export const insertTask = async ({
context,
finished,
text,
userId,
}: InsertTaskArgs) => {
return context.supabase
const result = await context.supabase
.from("Task")
.insert({ finished, text, user_id: userId });
.update({ finished: parsed.output.finished });

return buildResponse({ context, result });
};

export const insertTask = async ({ context, formData }: TaskMutationArgs) => {
const userId = context.session?.user.id;

if (!userId) {
return unauthorizedError();
}

const parsed = await safeParseAsync(
object({ text: string(), finished: boolean() }),
decode(formData, { booleans: ["finished"] }),
);

if (!parsed.success) {
return invalidRequestError(parsed.issues);
}

const result = await context.supabase.from("Task").insert({
finished: parsed.output.finished,
text: parsed.output.text,
user_id: userId,
});

return buildResponse({ context, result });
};

0 comments on commit 2f8ea59

Please sign in to comment.