Skip to content

Commit

Permalink
Merge pull request #159 from gruz0/refactor/handle-application-errors
Browse files Browse the repository at this point in the history
Handle application errors
  • Loading branch information
gruz0 authored Nov 19, 2024
2 parents edd1d40 + 1a24eac commit 32fdb77
Show file tree
Hide file tree
Showing 9 changed files with 93 additions and 9 deletions.
16 changes: 16 additions & 0 deletions src/app/concepts/[id]/not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export default function NotFound() {
return (
<div className="p-8 text-center">
<h1 className="mb-6 text-4xl font-bold">Page Not Found</h1>
<p className="mt-4 text-lg">
Sorry, the concept you&apos;re looking for does not exist.
</p>
<a
href="/"
className="mt-6 inline-block text-lg text-blue-500 hover:underline"
>
Go back to the homepage
</a>
</div>
)
}
12 changes: 9 additions & 3 deletions src/app/concepts/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { notFound } from 'next/navigation'
import React from 'react'
import { App } from '@/concept/service/Service'
import ReportPage from './ReportPage'
Expand Down Expand Up @@ -56,9 +57,14 @@ export default async function Page({ params }: { params: { id: string } }) {

return <ReportPage concept={conceptProps} />
} catch (e) {
// FIXME: Add more fancy errors handling.
// Also don't forget to catch real errors and app layer errors.
if (e instanceof Error) {
if ('isNotFoundError' in e) {
notFound()
}

return <p className="p-6 text-lg">{e.message}</p>
}

return <p>{(e as Error).message}</p>
return <p className="p-6 text-lg">An unexpected error occurred</p>
}
}
16 changes: 16 additions & 0 deletions src/app/ideas/[id]/not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export default function NotFound() {
return (
<div className="p-8 text-center">
<h1 className="mb-6 text-4xl font-bold">Page Not Found</h1>
<p className="mt-4 text-lg">
Sorry, the idea you&apos;re looking for does not exist.
</p>
<a
href="/"
className="mt-6 inline-block text-lg text-blue-500 hover:underline"
>
Go back to the homepage
</a>
</div>
)
}
12 changes: 9 additions & 3 deletions src/app/ideas/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { notFound } from 'next/navigation'
import React from 'react'
import { App } from '@/idea/service/Service'
import { IdeaAnalysisReport } from './IdeaAnalysisReport'
Expand All @@ -12,9 +13,14 @@ export default async function Page({ params }: { params: { id: string } }) {

return <IdeaAnalysisReport data={dto} />
} catch (e) {
// FIXME: Add more fancy errors handling.
// Also don't forget to catch real errors and app layer errors.
if (e instanceof Error) {
if ('isNotFoundError' in e) {
notFound()
}

return <p>{(e as Error).message}</p>
return <p className="p-6 text-lg">{e.message}</p>
}

return <p className="p-6 text-lg">An unexpected error occurred</p>
}
}
11 changes: 11 additions & 0 deletions src/common/errors/ApplicationError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export class ApplicationError extends Error {
public readonly isApplicationError = true

constructor(message: string) {
super(message)

Object.setPrototypeOf(this, new.target.prototype)

this.name = 'ApplicationError'
}
}
11 changes: 11 additions & 0 deletions src/common/errors/NotFoundError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export class NotFoundError extends Error {
public readonly isNotFoundError = true

constructor(message?: string) {
super(message || 'Not Found')

Object.setPrototypeOf(this, new.target.prototype)

this.name = 'NotFoundError'
}
}
15 changes: 15 additions & 0 deletions src/concept/adapters/ConceptRepositorySQLite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,21 @@ export class ConceptRepositorySQLite implements Repository {
if (conceptModel.evaluation) {
const json = JSON.parse(conceptModel.evaluation)

// FIXME: As many changes have applied since 17 Nov, we don't want to support them
// This condition can be removed once we go live on production.
const createdAtDate = new Date(conceptModel.createdAt)
const thresholdDate = new Date('2024-11-17T00:00:00Z')

if (
json.status === 'requires_changes' &&
json.painPoints.length === 0 &&
createdAtDate < thresholdDate
) {
throw new Error(
'The concept was created before November 17, 2024, and is no longer supported. Please create a new one.'
)
}

const evaluation = new Evaluation(
json.status,
json.suggestions,
Expand Down
6 changes: 4 additions & 2 deletions src/concept/app/queries/GetConcept.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import * as Sentry from '@sentry/nextjs'
import { ApplicationError } from '@/common/errors/ApplicationError'
import { NotFoundError } from '@/common/errors/NotFoundError'
import { Concept } from '@/concept/domain/Aggregate'

type Query = {
Expand All @@ -21,11 +23,11 @@ export class GetConceptHandler {
const concept = await this.readModel.getById(query.id)

if (!concept) {
throw new Error(`Concept ${query.id} does not exist`)
throw new NotFoundError(`Concept ${query.id} does not exist`)
}

if (!concept.isAvailable()) {
throw new Error('Concept is not available anymore')
throw new ApplicationError('Concept is not available anymore')
}

return concept
Expand Down
3 changes: 2 additions & 1 deletion src/idea/app/queries/GetIdea.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as Sentry from '@sentry/nextjs'
import { NotFoundError } from '@/common/errors/NotFoundError'
import { Idea } from '@/idea/domain/Aggregate'
import { CompetitorAnalysis } from '@/idea/domain/CompetitorAnalysis'
import { ContentIdeasForMarketing } from '@/idea/domain/ContentIdeasForMarketing'
Expand Down Expand Up @@ -120,7 +121,7 @@ export class GetIdeaHandler {
const idea = await this.readModel.getById(query.id)

if (!idea) {
throw new Error(`Idea ${query.id} does not exist`)
throw new NotFoundError(`Idea ${query.id} does not exist`)
}

const targetAudiences = await this.readModel.getTargetAudiencesByIdeaId(
Expand Down

0 comments on commit 32fdb77

Please sign in to comment.