Skip to content

Commit

Permalink
formalise the error structure across the system
Browse files Browse the repository at this point in the history
  • Loading branch information
mahendraHegde committed Sep 14, 2023
1 parent 674278f commit 405a959
Show file tree
Hide file tree
Showing 26 changed files with 143 additions and 46 deletions.
8 changes: 7 additions & 1 deletion app/components/errors/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { isRouteErrorResponse, useRouteError } from "@remix-run/react";
import { NODE_ENV } from "~/utils/env";
import type { ShelfStackError} from "~/utils/error";
import { isShelfStackError } from "~/utils/error";
import { ErrorContent } from "./content";

export interface ErrorContentProps {
Expand All @@ -12,7 +14,11 @@ export const ErrorBoundryComponent = ({
title,
message,
}: ErrorContentProps) => {
const error = useRouteError();
const error: Error = useRouteError() as Error;
if (isShelfStackError(error)) {
title = title || (error as ShelfStackError).title
message = message || error.message
}
/** 404 ERROR */
if (isRouteErrorResponse(error))
switch (error.status) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable no-console */
import type { Role } from "@prisma/client";
import { PrismaClient, Roles } from "@prisma/client";
import { ShelfStackError } from "~/utils/error";

const prisma = new PrismaClient();

Expand Down Expand Up @@ -90,8 +91,7 @@ async function seed() {

console.log(`Database has been seeded. 🌱\n`);
} catch (cause) {
console.error(cause);
throw new Error("Seed failed 🥲");
throw new ShelfStackError({ message: "Seed failed 🥲", cause });
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable no-console */
import { OrganizationType, PrismaClient } from "@prisma/client";
import { ShelfStackError } from "~/utils/error";

const prisma = new PrismaClient();

Expand Down Expand Up @@ -48,8 +49,7 @@ async function seed() {
);
console.log(`Database has been seeded. 🌱\n`);
} catch (cause) {
console.error(cause);
throw new Error("Seed failed 🥲");
throw new ShelfStackError({ message: "Seed failed 🥲", cause });
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable no-console */
import { OrganizationType, PrismaClient } from "@prisma/client";
import { ShelfStackError } from "~/utils/error";

const prisma = new PrismaClient();

Expand Down Expand Up @@ -43,8 +44,7 @@ async function seed() {
);
console.log(`Database has been seeded. 🌱\n`);
} catch (cause) {
console.error(cause);
throw new Error("Seed failed 🥲");
throw new ShelfStackError({ message: "Seed failed 🥲", cause });
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable no-console */
import type { User } from "@prisma/client";
import { PrismaClient } from "@prisma/client";
import { ShelfStackError } from "~/utils/error";

const prisma = new PrismaClient();

Expand Down Expand Up @@ -28,8 +29,7 @@ async function seed() {
});
});
} catch (cause) {
console.error(cause);
throw new Error("Seed failed 🥲");
throw new ShelfStackError({ message: "Seed failed 🥲", cause });
}
}

Expand Down
6 changes: 3 additions & 3 deletions app/database/seed.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { PrismaClient } from "@prisma/client";
import { createClient } from "@supabase/supabase-js";

import { createUser } from "~/modules/user";
import { ShelfStackError } from "~/utils/error";
import {
createAdminRole,
createUserRole,
Expand Down Expand Up @@ -78,16 +79,15 @@ async function seed() {
});

if (!user) {
throw new Error("Unable to create user");
throw new ShelfStackError({ message: "Unable to create user" });
}

console.log(`Database has been seeded. 🌱\n`);
console.log(
`User added to your database 👇 \n🆔: ${user.id}\n📧: ${user.email}\n🔑: supabase`
);
} catch (cause) {
console.error(cause);
throw new Error("Seed failed 🥲");
throw new ShelfStackError({ message: "Seed failed 🥲",cause });
}
}

Expand Down
15 changes: 8 additions & 7 deletions app/integrations/supabase/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,20 @@ import {
SUPABASE_URL,
SUPABASE_ANON_PUBLIC,
} from "~/utils/env";
import { ShelfStackError } from "~/utils/error";
import { isBrowser } from "~/utils/is-browser";

// ⚠️ cloudflare needs you define fetch option : https://github.com/supabase/supabase-js#custom-fetch-implementation
// Use Remix fetch polyfill for node (See https://remix.run/docs/en/v1/other-api/node)
function getSupabaseClient(supabaseKey: string, accessToken?: string) {
const global = accessToken
? {
global: {
headers: {
Authorization: `Bearer ${accessToken}`,
},
global: {
headers: {
Authorization: `Bearer ${accessToken}`,
},
}
},
}
: {};

return createClient(SUPABASE_URL, supabaseKey, {
Expand All @@ -40,8 +41,8 @@ const supabaseClient = getSupabaseClient(SUPABASE_ANON_PUBLIC);
*/
function getSupabaseAdmin() {
if (isBrowser)
throw new Error(
"getSupabaseAdmin is not available in browser and should NOT be used in insecure environments"
throw new ShelfStackError(
{ message: "getSupabaseAdmin is not available in browser and should NOT be used in insecure environments" }
);

return getSupabaseClient(SUPABASE_SERVICE_ROLE);
Expand Down
6 changes: 3 additions & 3 deletions app/modules/asset/service.server.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import {
ErrorCorrection } from "@prisma/client";
import type {
Category,
Location,
Note,
Prisma,
Qr,
Asset,
User,
Tag,
} from "@prisma/client";
import { ErrorCorrection } from "@prisma/client";
Tag } from "@prisma/client";
import type { LoaderArgs } from "@remix-run/node";
import { db } from "~/database";
import {
Expand Down
5 changes: 3 additions & 2 deletions app/modules/auth/mappers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { SupabaseAuthSession } from "~/integrations/supabase";

import { ShelfStackError } from "~/utils/error";
import type { AuthSession } from "./types";

export function mapAuthSession(
Expand All @@ -8,10 +9,10 @@ export function mapAuthSession(
if (!supabaseAuthSession) return null;

if (!supabaseAuthSession.refresh_token)
throw new Error("User should have a refresh token");
throw new ShelfStackError({message:"User should have a refresh token"});

if (!supabaseAuthSession.user?.email)
throw new Error("User should have an email");
throw new ShelfStackError({message:"User should have an email"});

return {
accessToken: supabaseAuthSession.access_token,
Expand Down
3 changes: 2 additions & 1 deletion app/modules/user/service.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
getCurrentSearchParams,
getParamsValues,
} from "~/utils";
import { ShelfStackError } from "~/utils/error";
import {
deleteProfilePicture,
getPublicFileURL,
Expand Down Expand Up @@ -323,7 +324,7 @@ export async function updateProfilePicture({

export async function deleteUser(id: User["id"]) {
if (!id) {
throw new Error("User ID is required");
throw new ShelfStackError({message:"User ID is required"});
}

try {
Expand Down
3 changes: 2 additions & 1 deletion app/routes/_layout+/assets.$assetId.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
import { appendToMetaTitle } from "~/utils/append-to-meta-title";
import { getDateTimeFormat } from "~/utils/client-hints";
import { sendNotification } from "~/utils/emitter/send-notification.server";
import { ShelfStackError } from "~/utils/error";
import { parseMarkdownToReact } from "~/utils/md.server";
import { deleteAssets } from "~/utils/storage.server";

Expand All @@ -51,7 +52,7 @@ export async function loader({ request, params }: LoaderArgs) {

const asset = await getAsset({ userId, id });
if (!asset) {
throw new Response("Not Found", { status: 404 });
throw new ShelfStackError({message:"Asset Not Found", status: 404 });
}
/** We get the first QR code(for now we can only have 1)
* And using the ID of tha qr code, we find the latest scan
Expand Down
3 changes: 2 additions & 1 deletion app/routes/_layout+/assets.$assetId_.edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { buildTagsSet } from "~/modules/tag";
import { assertIsPost, getRequiredParam } from "~/utils";
import { appendToMetaTitle } from "~/utils/append-to-meta-title";
import { sendNotification } from "~/utils/emitter/send-notification.server";
import { ShelfStackError } from "~/utils/error";

export async function loader({ request, params }: LoaderArgs) {
const { userId } = await requireAuthSession(request);
Expand All @@ -33,7 +34,7 @@ export async function loader({ request, params }: LoaderArgs) {

const asset = await getAsset({ userId, id });
if (!asset) {
throw new Response("Not Found", { status: 404 });
throw new ShelfStackError({message:"Not Found", status: 404 });
}

const header: HeaderData = {
Expand Down
3 changes: 2 additions & 1 deletion app/routes/_layout+/assets._index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { requireAuthSession } from "~/modules/auth";
import { getUserByID } from "~/modules/user";
import { notFound, userFriendlyAssetStatus } from "~/utils";
import { appendToMetaTitle } from "~/utils/append-to-meta-title";
import { ShelfStackError } from "~/utils/error";

export interface IndexResponse {
/** Page number. Starts at 1 */
Expand Down Expand Up @@ -90,7 +91,7 @@ export async function loader({ request }: LoaderArgs) {
}

if (!assets) {
throw notFound(`No assets found`);
throw new ShelfStackError({ title: "heyy!", message: `No assets found`, status: 404 });
}

const header: HeaderData = {
Expand Down
3 changes: 2 additions & 1 deletion app/routes/_layout+/locations.$locationId.add-assets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from "~/modules/asset";
import { requireAuthSession } from "~/modules/auth";
import { assertIsPost } from "~/utils";
import { ShelfStackError } from "~/utils/error";

export const loader = async ({ request, params }: LoaderArgs) => {
const { userId } = await requireAuthSession(request);
Expand Down Expand Up @@ -90,7 +91,7 @@ export const action = async ({ request, params }: ActionArgs) => {
});

if (!location) {
throw new Response("Something went wrong", { status: 500 });
throw new ShelfStackError({message:"Something went wrong", status: 500 });
}

if (asset) {
Expand Down
3 changes: 2 additions & 1 deletion app/routes/_layout+/locations.$locationId.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
} from "~/utils";
import { appendToMetaTitle } from "~/utils/append-to-meta-title";
import { sendNotification } from "~/utils/emitter/send-notification.server";
import { ShelfStackError } from "~/utils/error";

export const loader = async ({ request, params }: LoaderArgs) => {
const { userId } = await requireAuthSession(request);
Expand All @@ -55,7 +56,7 @@ export const loader = async ({ request, params }: LoaderArgs) => {
});

if (!location) {
throw new Response("Not Found", { status: 404 });
throw new ShelfStackError({message:"Not Found", status: 404 });
}

const totalItems = totalAssetsWithinLocation;
Expand Down
3 changes: 2 additions & 1 deletion app/routes/_layout+/locations.$locationId_.edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { getLocation, updateLocation } from "~/modules/location";
import { assertIsPost, getRequiredParam } from "~/utils";
import { appendToMetaTitle } from "~/utils/append-to-meta-title";
import { sendNotification } from "~/utils/emitter/send-notification.server";
import { ShelfStackError } from "~/utils/error";
import { MAX_SIZE } from "./locations.new";

export async function loader({ request, params }: LoaderArgs) {
Expand All @@ -26,7 +27,7 @@ export async function loader({ request, params }: LoaderArgs) {

const { location } = await getLocation({ userId, id });
if (!location) {
throw new Response("Not Found", { status: 404 });
throw new ShelfStackError({ message: "Location Not Found", status: 404 });
}

const header: HeaderData = {
Expand Down
3 changes: 2 additions & 1 deletion app/routes/_layout+/settings.workspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ import {
getUserPersonalOrganizationData,
} from "~/modules/organization";
import { appendToMetaTitle } from "~/utils/append-to-meta-title";
import { ShelfStackError } from "~/utils/error";

export const loader = async ({ request }: LoaderArgs) => {
const { userId } = await requireAuthSession(request);
const { organization, totalAssets, totalLocations } =
await getUserPersonalOrganizationData({ userId });
if (!organization) throw new Error("Organization not found");
if (!organization) throw new ShelfStackError({message:"Organization not found"});

const {
page,
Expand Down
14 changes: 9 additions & 5 deletions app/routes/api+/image.$imageId.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import type { LoaderArgs } from "@remix-run/node";
import { db } from "~/database";
import { getAuthSession } from "~/modules/auth";
import { ShelfStackError } from "~/utils/error";

export async function loader({ request, params }: LoaderArgs) {
const session = await getAuthSession(request);

if (!session)
throw new Response(
"Unauthorized. You are not allowed to view this resource",
{ status: 403 }
throw new ShelfStackError(
{
message: "Unauthorized. You are not allowed to view this resource",
status: 403
}
);
const image = await db.image.findUnique({
where: { id: params.imageId },
Expand All @@ -17,12 +20,13 @@ export async function loader({ request, params }: LoaderArgs) {

/** If the image doesnt belong to the user who has the session. Throw an error. */
if (image?.userId !== session.userId) {
throw new Response("Unauthorized. This resource doesn't belong to you.", {
throw new ShelfStackError({
message: "Unauthorized. This resource doesn't belong to you.",
status: 403,
});
}

if (!image) throw new Response("Not found", { status: 404 });
if (!image) throw new ShelfStackError({ message: "Not found", status: 404 });

return new Response(image.blob, {
headers: {
Expand Down
3 changes: 2 additions & 1 deletion app/routes/qr+/$qrId.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { getQr } from "~/modules/qr";
import { belongsToCurrentUser } from "~/modules/qr/utils.server";
import { createScan, updateScan } from "~/modules/scan";
import { assertIsPost, notFound } from "~/utils";
import { ShelfStackError } from "~/utils/error";

export const loader = async ({ request, params }: LoaderArgs) => {
/* Get the ID of the QR from the params */
Expand Down Expand Up @@ -34,7 +35,7 @@ export const loader = async ({ request, params }: LoaderArgs) => {
* that is still there. Will we allow someone to claim it?
*/
if (!qr) {
throw notFound("Not found");
throw new ShelfStackError({message:"Not found"});
}

/**
Expand Down
3 changes: 2 additions & 1 deletion app/utils/client-hints.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* are needed by the server, but are only known by the browser.
*/
import { parseAcceptLanguage } from "intl-parse-accept-language";
import { ShelfStackError } from "./error";
import { useRequestInfo } from "./request-info";

export const clientHints = {
Expand All @@ -18,7 +19,7 @@ type ClientHintNames = keyof typeof clientHints;
function getCookieValue(cookieString: string, name: ClientHintNames) {
const hint = clientHints[name];
if (!hint) {
throw new Error(`Unknown client hint: ${name}`);
throw new ShelfStackError({message:`Unknown client hint: ${name}`});
}
const value = cookieString
.split(";")
Expand Down
Loading

0 comments on commit 405a959

Please sign in to comment.