Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update and simplify error handling #571

Merged
merged 4 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions src/api/plugins/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import fp from 'fastify-plugin'
import { User } from '@prisma/client'

import apiConfig from '../../config/api'
import { GarboAPIError } from '../../lib/garbo-api-error'
import { prisma } from '../../lib/prisma'

declare module 'fastify' {
Expand All @@ -17,14 +16,18 @@ declare module 'fastify' {
}
}

const unauthorizedError = {
message: 'Unauthorized',
}

async function authPlugin(app: FastifyInstance) {
app.decorateRequest('user')
app.addHook('onRequest', async (request) => {
app.addHook('onRequest', async (request, reply) => {
try {
const token = request.headers['authorization']?.replace('Bearer ', '')

if (!token || !apiConfig.tokens?.includes(token)) {
throw GarboAPIError.unauthorized()
return reply.status(401).send(unauthorizedError)
}

const [username] = token.split(':')
Expand All @@ -38,12 +41,12 @@ async function authPlugin(app: FastifyInstance) {
})

if (!user?.id) {
throw GarboAPIError.unauthorized()
return reply.status(401).send(unauthorizedError)
}

request.user = user
} catch (error) {
throw GarboAPIError.unauthorized()
return reply.status(401).send(unauthorizedError)
}
})
}
Expand Down
42 changes: 27 additions & 15 deletions src/api/plugins/errorhandler.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,37 @@
import { FastifyReply, FastifyRequest } from 'fastify'
import { FastifyReply, FastifyRequest, FastifyError } from 'fastify'
import { Prisma } from '@prisma/client'

import { GarboAPIError } from '../../lib/garbo-api-error'
import apiConfig from '../../config/api'

export const errorHandler = (
export function errorHandler(
error: Error,
request: FastifyRequest,
reply: FastifyReply
) => {
) {
request.log.error(error)

if (error instanceof GarboAPIError) {
request.log.error(error.original)
return reply.code(error.statusCode).send({
error: error.message,
details: error.original,
help: 'Contact support if the problem persists',
if ((error as FastifyError)?.validation) {
const fastifyError = error as FastifyError

reply.status(400).send({
code: 'VALIDATION_ERROR',
message: fastifyError.message,
details: apiConfig.DEV ? fastifyError : fastifyError.validation,
})
} else if (
error instanceof Prisma.PrismaClientKnownRequestError &&
error.code === 'P2025'
) {
reply.status(404).send({
code: 'NOT_FOUND',
message: 'The requested resource could not be found.',
details: apiConfig.DEV ? error : undefined,
})
} else {
reply.status(500).send({
code: 'INTERNAL_SERVER_ERROR',
message: apiConfig.DEV ? error.message : 'An unexpected error occurred.',
details: apiConfig.DEV ? error : undefined,
})
}

reply.code(500).send({
error: 'Internal Server Error',
help: 'Contact support if the problem persists',
})
}
36 changes: 24 additions & 12 deletions src/api/routes/company.delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
wikidataIdParamSchema,
emptyBodySchema,
garboEntityIdSchema,
getErrorSchemas,
} from '../schemas'
import { getTags } from '../../config/openapi'
import { GarboEntityId, WikidataIdParams } from '../types'
Expand All @@ -20,11 +21,12 @@ export async function companyDeleteRoutes(app: FastifyInstance) {
{
schema: {
summary: 'Delete company',
description: 'Deletes a company by Wikidata ID',
description: 'Delete a company by Wikidata ID',
tags: getTags('Companies'),
params: wikidataIdParamSchema,
response: {
204: emptyBodySchema,
...getErrorSchemas(400, 404),
},
},
},
Expand All @@ -43,11 +45,12 @@ export async function companyDeleteRoutes(app: FastifyInstance) {
{
schema: {
summary: 'Delete a goal',
description: 'Deletes a goal by id',
description: 'Delete a goal by id',
tags: getTags('Goals'),
params: garboEntityIdSchema,
response: {
204: emptyBodySchema,
...getErrorSchemas(400, 404),
},
},
},
Expand All @@ -71,6 +74,7 @@ export async function companyDeleteRoutes(app: FastifyInstance) {
params: garboEntityIdSchema,
response: {
204: emptyBodySchema,
...getErrorSchemas(400, 404),
},
},
},
Expand All @@ -89,11 +93,11 @@ export async function companyDeleteRoutes(app: FastifyInstance) {
{
schema: {
summary: 'Delete an initiative',
description: 'Deletes an initiative by id',
description: 'Delete an initiative by id',
tags: getTags('Initiatives'),
params: garboEntityIdSchema,
response: {
204: emptyBodySchema,
...getErrorSchemas(400, 404),
},
},
},
Expand All @@ -112,11 +116,12 @@ export async function companyDeleteRoutes(app: FastifyInstance) {
{
schema: {
summary: 'Delete a reporting period',
description: 'Deletes a reporting period by id',
description: 'Delete a reporting period by id',
tags: getTags('ReportingPeriods'),
params: garboEntityIdSchema,
response: {
204: emptyBodySchema,
...getErrorSchemas(400, 404),
},
},
},
Expand All @@ -135,11 +140,12 @@ export async function companyDeleteRoutes(app: FastifyInstance) {
{
schema: {
summary: 'Delete stated total emissions',
description: 'Deletes stated total emissions by id',
description: 'Delete stated total emissions by id',
tags: getTags('Emissions'),
params: garboEntityIdSchema,
response: {
204: emptyBodySchema,
...getErrorSchemas(400, 404),
},
},
},
Expand All @@ -158,11 +164,12 @@ export async function companyDeleteRoutes(app: FastifyInstance) {
{
schema: {
summary: 'Delete biogenic emissions',
description: 'Deletes biogenic emissions by id',
description: 'Delete biogenic emissions by id',
tags: getTags('Emissions'),
params: garboEntityIdSchema,
response: {
204: emptyBodySchema,
...getErrorSchemas(400, 404),
},
},
},
Expand All @@ -181,11 +188,12 @@ export async function companyDeleteRoutes(app: FastifyInstance) {
{
schema: {
summary: 'Delete Scope1',
description: 'Deletes the Scope1 emissions by id',
description: 'Delete the Scope1 emissions by id',
tags: getTags('Emissions'),
params: garboEntityIdSchema,
response: {
204: emptyBodySchema,
...getErrorSchemas(400, 404),
},
},
},
Expand All @@ -204,11 +212,12 @@ export async function companyDeleteRoutes(app: FastifyInstance) {
{
schema: {
summary: 'Delete scope1and2',
description: 'Deletes a scope1and2 by id',
description: 'Delete a scope1and2 by id',
tags: getTags('Emissions'),
params: garboEntityIdSchema,
response: {
204: emptyBodySchema,
...getErrorSchemas(400, 404),
},
},
},
Expand All @@ -227,11 +236,12 @@ export async function companyDeleteRoutes(app: FastifyInstance) {
{
schema: {
summary: 'Delete scope2',
description: 'Deletes a scope2 by id',
description: 'Delete a scope2 by id',
tags: getTags('Emissions'),
params: garboEntityIdSchema,
response: {
204: emptyBodySchema,
...getErrorSchemas(400, 404),
},
},
},
Expand All @@ -250,11 +260,12 @@ export async function companyDeleteRoutes(app: FastifyInstance) {
{
schema: {
summary: 'Delete scope3',
description: 'Deletes a scope3 by id',
description: 'Delete a scope3 by id',
tags: getTags('Emissions'),
params: garboEntityIdSchema,
response: {
204: emptyBodySchema,
...getErrorSchemas(400, 404),
},
},
},
Expand All @@ -273,11 +284,12 @@ export async function companyDeleteRoutes(app: FastifyInstance) {
{
schema: {
summary: 'Delete a scope3 category',
description: 'Deletes a scope3 category by id',
description: 'Delete a scope3 category by id',
tags: getTags('Emissions'),
params: garboEntityIdSchema,
response: {
204: emptyBodySchema,
...getErrorSchemas(400, 404),
},
},
},
Expand Down
21 changes: 5 additions & 16 deletions src/api/routes/company.goals.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { FastifyInstance, AuthenticatedFastifyRequest } from 'fastify'
import { Prisma } from '@prisma/client'

import { GarboAPIError } from '../../lib/garbo-api-error'
import { goalService } from '../services/goalService'
import {
wikidataIdParamSchema,
postGoalSchema,
postGoalsSchema,
okResponseSchema,
garboEntityIdSchema,
getErrorSchemas,
} from '../schemas'
import {
PostGoalsBody,
Expand All @@ -31,6 +30,7 @@ export async function companyGoalsRoutes(app: FastifyInstance) {
body: postGoalsSchema,
response: {
200: okResponseSchema,
...getErrorSchemas(400, 404),
},
},
},
Expand Down Expand Up @@ -69,6 +69,7 @@ export async function companyGoalsRoutes(app: FastifyInstance) {
body: postGoalSchema,
response: {
200: okResponseSchema,
...getErrorSchemas(400, 404),
},
},
},
Expand All @@ -87,20 +88,8 @@ export async function companyGoalsRoutes(app: FastifyInstance) {
user: request.user,
})

await goalService
.updateGoal(id, { goal }, createdMetadata)
.catch((error) => {
if (
error instanceof Prisma.PrismaClientKnownRequestError &&
error.code === 'P2025'
) {
throw new GarboAPIError('Goal not found', {
statusCode: 404,
original: error,
})
}
throw error
})
await goalService.updateGoal(id, { goal }, createdMetadata)

reply.send({ ok: true })
}
)
Expand Down
32 changes: 13 additions & 19 deletions src/api/routes/company.industry.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { FastifyInstance, AuthenticatedFastifyRequest } from 'fastify'

import { prisma } from '../../lib/prisma'
import { GarboAPIError } from '../../lib/garbo-api-error'
import { industryService } from '../services/industryService'
import { postIndustrySchema } from '../schemas'
import { getErrorSchemas, postIndustrySchema } from '../schemas'
import { metadataService } from '../services/metadataService'
import { getTags } from '../../config/openapi'
import { wikidataIdParamSchema, okResponseSchema } from '../schemas'
Expand All @@ -22,6 +21,7 @@ export async function companyIndustryRoutes(app: FastifyInstance) {
body: postIndustrySchema,
response: {
200: okResponseSchema,
...getErrorSchemas(400, 404),
},
},
},
Expand All @@ -38,7 +38,7 @@ export async function companyIndustryRoutes(app: FastifyInstance) {
} = request.body
const { wikidataId } = request.params

const current = await prisma.industry.findFirst({
const current = await prisma.industry.findFirstOrThrow({
where: { companyWikidataId: wikidataId },
})

Expand All @@ -48,23 +48,17 @@ export async function companyIndustryRoutes(app: FastifyInstance) {
})

if (current) {
await industryService
.updateIndustry(wikidataId, { subIndustryCode }, createdMetadata)
.catch((error) => {
throw new GarboAPIError('Failed to update industry', {
original: error,
statusCode: 500,
})
})
await industryService.updateIndustry(
wikidataId,
{ subIndustryCode },
createdMetadata
)
} else {
await industryService
.createIndustry(wikidataId, { subIndustryCode }, createdMetadata)
.catch((error) => {
throw new GarboAPIError('Failed to create industry', {
original: error,
statusCode: 500,
})
})
await industryService.createIndustry(
wikidataId,
{ subIndustryCode },
createdMetadata
)
}

reply.send({ ok: true })
Expand Down
Loading