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

✨ added active contacts API #95

Merged
merged 14 commits into from
Jul 9, 2024
138 changes: 138 additions & 0 deletions packages/tom-server/src/active-contacts-api/controllers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/* eslint-disable no-useless-return */
import type { Response, NextFunction } from 'express'
import type { TwakeDB } from '../../db'
import type {
IActiveContactsService,
IActiveContactsApiController
} from '../types'
import type { TwakeLogger } from '@twake/logger'
import ActiveContactsService from '../services'
import type { AuthRequest } from '../../types'

export default class ActiveContactsApiController
implements IActiveContactsApiController
{
ActiveContactsApiService: IActiveContactsService

/**
* the active contacts API controller constructor
*
* @param {TwakeDB} db - the twake database instance
* @param {TwakeLogger} logger - the twake logger instance
* @example
* const controller = new ActiveContactsApiController(db, logger);
*/
constructor(
private readonly db: TwakeDB,
private readonly logger: TwakeLogger
) {
this.ActiveContactsApiService = new ActiveContactsService(db, logger)
}

/**
* Save active contacts
*
* @param {AuthRequest} req - request object
* @param {Response} res - response object
* @param {NextFunction} next - next function
* @returns {Promise<void>} - promise that resolves when the operation is complete
*/
save = async (
req: AuthRequest,
res: Response,
next: NextFunction
): Promise<void> => {
try {
const { contacts } = req.body
const { userId } = req

if (userId === undefined || contacts === undefined) {
res.status(400).json({ message: 'Bad Request' })
return
}

await this.ActiveContactsApiService.save(userId, contacts)

res.status(201).send()
return
} catch (error) {
this.logger.error('An error occured while saving active contacts', {
error
})
next(error)
}
}

/**
* Retrieve active contacts
*
* @param {AuthRequest} req - request object
* @param {Response} res - response object
* @param {NextFunction} next - next function
* @returns {Promise<void>} - promise that resolves when the operation is complete
*/
get = async (
req: AuthRequest,
res: Response,
next: NextFunction
): Promise<void> => {
try {
const { userId } = req

if (userId === undefined) {
throw new Error('Missing data', {
cause: 'userId is missing'
})
}

const contacts = await this.ActiveContactsApiService.get(userId)

if (contacts === null) {
res.status(404).json({ message: 'No active contacts found' })
return
}

res.status(200).json({ contacts })
return
} catch (error) {
this.logger.error('An error occured while retrieving active contacts', {
error
})
next(error)
}
}

/**
* Delete saved active contacts
*
* @param {AuthRequest} req - request object
* @param {Response} res - response object
* @param {NextFunction} next - next function
* @returns {Promise<void>} - promise that resolves when the operation is complete
*/
delete = async (
req: AuthRequest,
res: Response,
next: NextFunction
): Promise<void> => {
try {
const { userId } = req

if (userId === undefined) {
throw new Error('Missing data', {
cause: 'userId is missing'
})
}

await this.ActiveContactsApiService.delete(userId)

res.status(200).send()
return
} catch (error) {
this.logger.error('An error occured while deleting active contacts', {
error
})
next(error)
}
}
}
1 change: 1 addition & 0 deletions packages/tom-server/src/active-contacts-api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './routes'
37 changes: 37 additions & 0 deletions packages/tom-server/src/active-contacts-api/middlewares/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { Response, NextFunction } from 'express'
import type { AuthRequest } from '../../types'
import type { IActiveContactsApiValidationMiddleware } from '../types'

export default class ActiveContactsApiValidationMiddleWare
implements IActiveContactsApiValidationMiddleware
{
/**
* Check the creation requirements of the active contacts API
*
* @param {AuthRequest} req - the request object
* @param {Response} res - the response object
* @param {NextFunction} next - the next function
* @returns {void}
* @example
* router.post('/', checkCreationRequirements, create)
*/
checkCreationRequirements = (
req: AuthRequest,
res: Response,
next: NextFunction
): void => {
try {
const { contacts } = req.body

if (contacts === undefined) {
throw new Error('Missing required fields', {
cause: 'userId or contacts is missing'
})
}

next()
} catch (error) {
res.status(400).json({ message: 'Bad Request' })
}
}
}
46 changes: 46 additions & 0 deletions packages/tom-server/src/active-contacts-api/routes/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/* eslint-disable @typescript-eslint/no-misused-promises */
import {
getLogger,
type TwakeLogger,
type Config as LoggerConfig
} from '@twake/logger'
import type {
AuthenticationFunction,
Config,
IdentityServerDb
} from '../../types'
import { Router } from 'express'
import authMiddleware from '../../utils/middlewares/auth.middleware'
import ActiveContactsApiController from '../controllers'
import ActiveContactsApiValidationMiddleWare from '../middlewares'

export const PATH = '/_twake/v1/activecontacts'

export default (
db: IdentityServerDb,
config: Config,
authenticator: AuthenticationFunction,
defaultLogger?: TwakeLogger
): Router => {
const logger = defaultLogger ?? getLogger(config as unknown as LoggerConfig)
const activeContactsApiController = new ActiveContactsApiController(
db,
logger
)
const authenticate = authMiddleware(authenticator, logger)
const validationMiddleware = new ActiveContactsApiValidationMiddleWare()
const router = Router()

router.get(PATH, authenticate, activeContactsApiController.get)
Fixed Show fixed Hide fixed
Dismissed Show dismissed Hide dismissed

router.post(
PATH,
authenticate,
Fixed Show fixed Hide fixed
Dismissed Show dismissed Hide dismissed
validationMiddleware.checkCreationRequirements,
activeContactsApiController.save
)

router.delete(PATH, authenticate, activeContactsApiController.delete)
Dismissed Show dismissed Hide dismissed

return router
}
95 changes: 95 additions & 0 deletions packages/tom-server/src/active-contacts-api/services/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import type { TwakeLogger } from '@twake/logger'
import type { TwakeDB } from '../../db'
import type { Collections } from '../../types'
import type { ActiveAcountsData, IActiveContactsService } from '../types'

class ActiveContactsService implements IActiveContactsService {
/**
* The active contacts service constructor.
*
* @param {TwakeDB} db - The Twake database instance.
* @param {TwakeLogger} logger - The Twake logger instance.
* @example
* const service = new ActiveContactsService(db, logger);
*/
constructor(
private readonly db: TwakeDB,
private readonly logger: TwakeLogger
) {}

/**
* Fetches the active contacts for a given user ID.
*
* @param {string} userId - The ID of the user whose active contacts need to be fetched.
* @returns {Promise<string | null>} - Active contacts or null if no active contacts found.
* @throws {Error} - If there is an error while fetching the active contacts.
*/
public get = async (userId: string): Promise<string | null> => {
try {
const ActiveContacts = (await this.db.get(
'ActiveContacts' as Collections,
['contacts'],
{ userId }
)) as unknown as ActiveAcountsData[]

if (ActiveContacts.length === 0) {
this.logger.warn('No active contacts found')

return null
}

return ActiveContacts[0].contacts
} catch (error) {
this.logger.error('Failed to get active contacts', { error })
throw new Error('Failed to get active contacts', { cause: error })
}
}

/**
* Saves the active contacts for a given user ID and target ID.
*
* @param {string} userId - The ID of the user whose active contacts need to be saved.
* @param {string} contacts - The active contacts data to be saved.
* @returns {Promise<void>}
* @throws {Error} - If there is an error while saving the active contacts.
*/
save = async (userId: string, contacts: string): Promise<void> => {
try {
await this.db.insert('ActiveContacts' as Collections, {
userId,
contacts
})

this.logger.info('active contacts saved successfully')
} catch (error) {
this.logger.error('Failed to save active contacts', { error })
throw new Error('Failed to save active contacts', { cause: error })
}
}

/**
* Deletes saved active contacts for a given user ID.
*
* @param {string} userId - The ID of the user whose saved active contacts need to be deleted.
* @returns {Promise<void>}
* @throws {Error} - If there is an error while deleting the saved active contacts.
*/
delete = async (userId: string): Promise<void> => {
try {
await this.db.deleteEqual(
'ActiveContacts' as Collections,
'userId',
userId
)

this.logger.info('active contacts deleted successfully')
} catch (error) {
this.logger.error('Failed to delete saved active contacts', { error })
throw new Error('Failed to delete saved active contacts', {
cause: error
})
}
}
}

export default ActiveContactsService
Loading
Loading