Skip to content

Commit

Permalink
feat(route): ✨ author ShowHelper and DeleteHelper
Browse files Browse the repository at this point in the history
  • Loading branch information
djdembeck committed Aug 20, 2022
1 parent 634feb6 commit f1b72e9
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 69 deletions.
17 changes: 7 additions & 10 deletions src/config/routes/authors/delete.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { FastifyInstance } from 'fastify'

import { RequestGeneric } from '#config/typing/requests'
import PaprAudibleAuthorHelper from '#helpers/database/audible/PaprAudibleAuthorHelper'
import RedisHelper from '#helpers/database/RedisHelper'
import AuthorDeleteHelper from '#helpers/routes/AuthorDeleteHelper'
import SharedHelper from '#helpers/shared'

async function _delete(fastify: FastifyInstance) {
Expand All @@ -15,21 +14,19 @@ async function _delete(fastify: FastifyInstance) {
throw new Error('Bad ASIN')
}

// Setup Helpers
const paprHelper = new PaprAudibleAuthorHelper(request.params.asin, {})
// Setup helper
const { redis } = fastify
const redisHelper = new RedisHelper(redis, 'author', request.params.asin)
const helper = new AuthorDeleteHelper(request.params.asin, redis)

// Get author from database
const existingAuthor = await paprHelper.findOne()
// Call helper handler
const isHandled = await helper.handler()

if (!existingAuthor) {
if (!isHandled) {
reply.code(404)
throw new Error(`${request.params.asin} not found in the database`)
}

await redisHelper.deleteOne()
return paprHelper.delete()
return { message: `${request.params.asin} deleted` }
})
}

Expand Down
64 changes: 5 additions & 59 deletions src/config/routes/authors/show.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import { FastifyInstance } from 'fastify'

import type { AuthorDocument } from '#config/models/Author'
import { AuthorProfile } from '#config/typing/people'
import { RequestGeneric } from '#config/typing/requests'
import ScrapeHelper from '#helpers/authors/audible/ScrapeHelper'
import addTimestamps from '#helpers/database/addTimestamps'
import PaprAudibleAuthorHelper from '#helpers/database/audible/PaprAudibleAuthorHelper'
import RedisHelper from '#helpers/database/RedisHelper'
import AuthorShowHelper from '#helpers/routes/AuthorShowHelper'
import SharedHelper from '#helpers/shared'

async function _show(fastify: FastifyInstance) {
Expand All @@ -24,61 +19,12 @@ async function _show(fastify: FastifyInstance) {
throw new Error('Bad ASIN')
}

// Setup Helpers
const paprHelper = new PaprAudibleAuthorHelper(request.params.asin, options)
// Setup Helper
const { redis } = fastify
const redisHelper = new RedisHelper(redis, 'author', request.params.asin)
const helper = new AuthorShowHelper(request.params.asin, options, redis)

// Get author from database
const existingAuthor = (await paprHelper.findOne()).data
let author: AuthorProfile | undefined = undefined

// Add dates to data if not present
if (existingAuthor && !existingAuthor.createdAt) {
paprHelper.authorData = addTimestamps(existingAuthor) as AuthorDocument
author = (await paprHelper.update()).data
} else {
const checkAuthor = await paprHelper.findOneWithProjection()
if (checkAuthor.data) {
author = checkAuthor.data
}
}

// Check for existing or cached data from redis
if (options.update !== '0') {
const redisAuthor = await redisHelper.findOrCreate(author)
if (redisAuthor) return redisAuthor
}

// Check if the object was updated recently
if (
options.update == '0' &&
existingAuthor &&
commonHelpers.checkIfRecentlyUpdated(existingAuthor)
)
return author

// Proceed to scrape the author since it doesn't exist in db or is outdated
// Set up helper
const scrapeHelper = new ScrapeHelper(request.params.asin)
// Request data to be processed by helper
const authorData = await scrapeHelper.process()
// Pass requested data to the CRUD helper
paprHelper.authorData = authorData
// Let CRUD helper decide how to handle the data
const authorToReturn = await paprHelper.createOrUpdate()

// Throw error on null return data
if (!authorToReturn.data) {
throw new Error(`No data returned from database for author ${request.params.asin}`)
}

// Update Redis if the item is modified
if (authorToReturn.modified) {
redisHelper.setOne(authorToReturn.data)
}

return authorToReturn.data
// Call helper handler
return helper.handler()
})
}

Expand Down
49 changes: 49 additions & 0 deletions src/helpers/routes/AuthorDeleteHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { FastifyRedis } from '@fastify/redis'

import { AuthorDocument } from '#config/models/Author'
import PaprAudibleAuthorHelper from '#helpers/database/audible/PaprAudibleAuthorHelper'
import RedisHelper from '#helpers/database/RedisHelper'

export default class AuthorDeleteHelper {
asin: string
paprHelper: PaprAudibleAuthorHelper
redisHelper: RedisHelper
originalAuthor: AuthorDocument | null = null
constructor(asin: string, redis: FastifyRedis | null) {
this.asin = asin
this.paprHelper = new PaprAudibleAuthorHelper(this.asin, {
update: undefined
})
this.redisHelper = new RedisHelper(redis, 'author', this.asin)
}

async getAuthorFromPapr(): Promise<AuthorDocument | null> {
return (await this.paprHelper.findOne()).data
}

/**
* Actions to run when a deletion is requested
*/
async deleteActions() {
// 1. Delete the author from cache
await this.redisHelper.deleteOne()

// 2. Delete the author from DB
return (await this.paprHelper.delete()).modified
}

/**
* Main handler for the author delete route
*/
async handler() {
this.originalAuthor = await this.getAuthorFromPapr()

// If the author is already present
if (this.originalAuthor) {
return this.deleteActions()
}

// If the author is not present
return false
}
}
121 changes: 121 additions & 0 deletions src/helpers/routes/AuthorShowHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { FastifyRedis } from '@fastify/redis'

import type { AuthorDocument } from '#config/models/Author'
import { AuthorProfile } from '#config/typing/people'
import { RequestGeneric } from '#config/typing/requests'
import ScrapeHelper from '#helpers/authors/audible/ScrapeHelper'
import addTimestamps from '#helpers/database/addTimestamps'
import PaprAudibleAuthorHelper from '#helpers/database/audible/PaprAudibleAuthorHelper'
import RedisHelper from '#helpers/database/RedisHelper'
import SharedHelper from '#helpers/shared'

export default class AuthorShowHelper {
asin: string
authorInternal: AuthorProfile | undefined = undefined
commonHelpers: SharedHelper
paprHelper: PaprAudibleAuthorHelper
redisHelper: RedisHelper
options: RequestGeneric['Querystring']
scrapeHelper: ScrapeHelper
originalAuthor: AuthorDocument | null = null
constructor(asin: string, options: RequestGeneric['Querystring'], redis: FastifyRedis | null) {
this.asin = asin
this.commonHelpers = new SharedHelper()
this.options = options
this.paprHelper = new PaprAudibleAuthorHelper(this.asin, this.options)
this.redisHelper = new RedisHelper(redis, 'book', this.asin)
this.scrapeHelper = new ScrapeHelper(this.asin)
}

async getAuthorFromPapr(): Promise<AuthorDocument | null> {
return (await this.paprHelper.findOne()).data
}

async getNewAuthorData() {
return this.scrapeHelper.process()
}

async createOrUpdateAuthor() {
// Place the new author data into the papr helper
this.paprHelper.setAuthorData(await this.getNewAuthorData())
// Create or update the author
const authorToReturn = await this.paprHelper.createOrUpdate()
// Update or create the author in cache
await this.redisHelper.findOrCreate(authorToReturn.data)
// Return the author
return authorToReturn
}

/**
* Check if the author is updated recently by comparing the timestamps of updatedAt
*/
isUpdatedRecently() {
if (!this.originalAuthor) {
return false
}
return this.commonHelpers.checkIfRecentlyUpdated(this.originalAuthor)
}

/**
* Update the timestamps of the author when they are missing
*/
async updateAuthorTimestamps(): Promise<AuthorProfile> {
// Return if not present or already has timestamps
if (!this.originalAuthor || this.originalAuthor.createdAt)
return (await this.paprHelper.findOneWithProjection()).data

// Add timestamps
this.paprHelper.authorData = addTimestamps(this.originalAuthor) as AuthorDocument
// Update author in DB
try {
this.authorInternal = (await this.paprHelper.update()).data
} catch (err) {
throw new Error(`An error occurred while adding timestamps to author ${this.asin} in the DB`)
}
return this.authorInternal
}

/**
* Actions to run when an update is requested
*/
async updateActions(): Promise<AuthorProfile> {
// 1. Check if it is updated recently
if (this.isUpdatedRecently()) return this.originalAuthor as AuthorProfile

// 2. Get the new author and create or update it
const authorToReturn = await this.createOrUpdateAuthor()

// 3. Update author in cache
if (authorToReturn.modified) {
this.redisHelper.setOne(authorToReturn.data)
}

// 4. Return the author
return authorToReturn.data
}

/**
* Main handler for the author show route
*/
async handler() {
this.originalAuthor = await this.getAuthorFromPapr()

// If the author is already present
if (this.originalAuthor) {
// If an update is requested
if (this.options.update === '1') {
return this.updateActions()
}
// 1. Make sure it has timestamps
await this.updateAuthorTimestamps()
// 2. Check it it is cached
const redisAuthor = await this.redisHelper.findOrCreate(this.originalAuthor)
if (redisAuthor) return redisAuthor as AuthorProfile
// 3. Return the author from DB
return this.originalAuthor as AuthorProfile
}

// If the author is not present
return (await this.createOrUpdateAuthor()).data
}
}

0 comments on commit f1b72e9

Please sign in to comment.