Skip to content

Commit

Permalink
Merge pull request #37 from imRohan/develop
Browse files Browse the repository at this point in the history
Adds Account Update and Notification Emails
  • Loading branch information
Rohan Likhite authored Jul 22, 2020
2 parents a92eb31 + 3900c2c commit 5470139
Show file tree
Hide file tree
Showing 17 changed files with 680 additions and 370 deletions.
1 change: 1 addition & 0 deletions env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ CRYPTO_KEY="32 Byte string"
CRYPTO_IV="16 Byte string"
SENDGRID_API_KEY='string'
WELCOME_EMAIL_ID='string
ACCOUNT_ERRORS_EMAIL_ID='string'
AIRTABLE_API_KEY='string'
AIRTABLE_BASE='string'
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions src/app/components/landingRight.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const landingRight = {
},
basket: null,
activeBasket: null,
showErrors: false,
showNameField: false,
copyPantryIdMessage: 'copy',
}
Expand All @@ -47,6 +48,9 @@ const landingRight = {
const _key = key.toString()
return(`${_key.charAt(0).toUpperCase()}${_key.slice(1)}`)
},
trim(text) {
return String(text).trim()
},
},
computed: {
isStatusPositive() {
Expand Down Expand Up @@ -97,6 +101,9 @@ const landingRight = {
this.activeBasket = name
}
},
toggleErrors() {
this.showErrors = !this.showErrors
},
signupValid() {
const _emailRegix = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return _emailRegix.test(String(this.signup.email).toLowerCase());
Expand Down
4 changes: 3 additions & 1 deletion src/app/scss/_banner.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
&--red {
background: $red,
}

&--button {
cursor: pointer
}
&--transparent {
background: none;
color: $secondary-color;
Expand Down
2 changes: 0 additions & 2 deletions src/app/scss/_basket.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
.basket {
&__container {
}
&__payload {
&-container {
padding: 1em;
Expand Down
3 changes: 3 additions & 0 deletions src/app/scss/_text.scss
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ a:any-link {
color: $text-color;
overflow-wrap: anywhere;
}
&--code {
font-family: 'courier', 'sans-serif';
}
&--key {
font-weight: bold;
text-align: center;
Expand Down
32 changes: 32 additions & 0 deletions src/app/templates/landingRight.html
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,22 @@ <h2>
in this Pantry
- {{pantry.data.percentFull}}% Full
</h2>
<template v-if="pantry.data.errors">
<div class="banner__container banner--red banner--button" @click="toggleErrors()">
Click to
{{ showErrors ? 'Hide' : 'Show' }}
Errors
</div>
<template v-if="showErrors">
<ul>
<li v-for="error in pantry.data.errors">
<span class="text--code">
{{ error | trim }}
</span>
</li>
</ul>
</template>
</template>
<div class="basket__container">
<ul>
<template v-for="basketName in pantry.data.baskets">
Expand All @@ -152,6 +168,22 @@ <h2>
</div>
</template>
<template v-else>
<template v-if="pantry.data.errors">
<div class="banner__container banner--red banner--button" @click="toggleErrors()">
Click to
{{ showErrors ? 'Hide' : 'Show' }}
Errors
</div>
<template v-if="showErrors">
<ul>
<li v-for="error in pantry.data.errors">
<span class="text--code">
{{ error | trim }}
</span>
</li>
</ul>
</template>
</template>
<p>
Looks like your pantry is empty and currently has no baskets.
</p>
Expand Down
18 changes: 17 additions & 1 deletion src/controllers/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import logService = require('../services/logger')
import mailer = require('../services/mailer')

// Interfaces
import { IAccountParams, IAccountPublic } from '../interfaces/account'
import { IAccountParams, IAccountPublic, IAccountUpdateParams } from '../interfaces/account'

// Logger setup
const logger = new logService('Account Controller')
Expand All @@ -31,6 +31,22 @@ class AccountController {
}
}

public static async update(uuid: string, data: Partial<IAccountUpdateParams>): Promise<IAccountPublic> {
try {
const _account = await Account.get(uuid)

await _account.update(data)

const _accountDetails = _account.sanitize()

logger.info('Account updated')
return _accountDetails
} catch (error) {
logger.error(`Account update failed: ${error.message}`)
throw error
}
}

public static async get(uuid: string): Promise<IAccountPublic> {
try {
const _account = await Account.get(uuid)
Expand Down
33 changes: 25 additions & 8 deletions src/controllers/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ class BlockController {
// Check if account has reached max number of blocks
const _accountFull = await _account.checkIfFull()
if (_accountFull) {
throw new Error('max number of blocks reached')
const _errorMessage = 'max number of baskets reached'
await _account.saveError(_errorMessage)
throw new Error(_errorMessage)
}

logger.info(`Creating new block in account: ${accountUUID}`)
Expand All @@ -34,12 +36,14 @@ class BlockController {
try {
let _block
let _blockDetails
await Account.get(accountUUID)

const _account = await Account.get(accountUUID)

try {
_block = await Block.get(accountUUID, name)
_blockDetails = _block.sanitize()
} catch (error) {
await _account.saveError(error.message)
throw error
}

Expand All @@ -53,14 +57,20 @@ class BlockController {

public static async update(accountUUID: string, name: string, data: any): Promise<any> {
try {
await Account.get(accountUUID)
let _block
const _account = await Account.get(accountUUID)

const _block = await Block.get(accountUUID, name)
try {
_block = await Block.get(accountUUID, name)
} catch (error) {
await _account.saveError(error.message)
throw error
}

logger.info(`Updating block ${name} in account: ${accountUUID}`)
await _block.update(data)

const _blockDetails = _block.sanitize()

logger.info(`Updating block ${name} in account: ${accountUUID}`)
return _blockDetails
} catch (error) {
logger.error(`Block update failed: ${error.message}, account: ${accountUUID}`)
Expand All @@ -70,9 +80,16 @@ class BlockController {

public static async delete(accountUUID: string, name: string): Promise<string> {
try {
await Account.get(accountUUID)
let _block

const _account = await Account.get(accountUUID)

const _block = await Block.get(accountUUID, name)
try {
_block = await Block.get(accountUUID, name)
} catch (error) {
await _account.saveError(error.message)
throw error
}

logger.info(`Removing block from account: ${accountUUID}`)
await _block.delete()
Expand Down
11 changes: 9 additions & 2 deletions src/interfaces/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,23 @@ export interface IAccountParams {
contactEmail: string,
}

export interface IAccountUpdateParams {
name: string,
description: string,
notifications: boolean,
}

export interface IAccountBase {
name: string,
description: string,
notifications: boolean,
errors: string[],
}

export interface IAccountPrivate extends IAccountBase {
contactEmail: string,
maxNumberOfBlocks: number,
notifications: boolean,
uuid?: string,
uuid: string,
}

export interface IAccountPublic extends IAccountBase {
Expand Down
42 changes: 39 additions & 3 deletions src/models/account.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Extarnal Libs
import {
IsArray,
IsBoolean,
IsEmail,
IsNotEmpty,
Expand All @@ -12,9 +13,10 @@ import uuidv4 = require('uuid/v4')

// External Files
import dataStore = require('../services/dataStore')
import mailer = require('../services/mailer')

// Interfaces
import { IAccountPrivate, IAccountPublic } from '../interfaces/account'
import { IAccountPrivate, IAccountPublic, IAccountUpdateParams } from '../interfaces/account'

class Account {
public static async get(uuid: string): Promise<Account> {
Expand Down Expand Up @@ -58,24 +60,39 @@ class Account {
@IsNotEmpty()
@IsNumber()
private maxNumberOfBlocks: number
@IsNotEmpty()
@IsArray()
private errors: string[]
@IsUUID('4')
private uuid: string

// Constants
private readonly lifeSpanDays = Number(process.env.ACCOUNT_LIFESPAN)
private readonly lifeSpan = Number(86400 * this.lifeSpanDays)
private readonly defaultMaxNumberOfBlocks = 100
private readonly errorsBeforeEmailSent = 5

constructor(params: any) {
const { name, description, contactEmail, notifications, uuid, maxNumberOfBlocks } = params
const { name, description, contactEmail, notifications, uuid, maxNumberOfBlocks, errors } = params
this.name = name
this.description = description
this.contactEmail = contactEmail
this.notifications = notifications ?? false
this.notifications = notifications ?? true
this.maxNumberOfBlocks = maxNumberOfBlocks ?? this.defaultMaxNumberOfBlocks
this.errors = errors ?? []
this.uuid = uuid ?? uuidv4()
}

public async update(newData: Partial<IAccountUpdateParams>): Promise<void> {
const { name, description, notifications } = newData

this.name = name ?? this.name
this.description = description ?? this.description
this.notifications = notifications ?? this.notifications

await this.store()
}

public async store(): Promise<string> {
const _errors = await validate(this)
if (_errors.length > 0) {
Expand All @@ -97,6 +114,8 @@ class Account {
const _sanitizedItems: IAccountPublic = {
name: this.name,
description: this.description,
errors: this.errors,
notifications: this.notifications,
percentFull: _percentFull,
baskets: _baskets,
}
Expand Down Expand Up @@ -130,13 +149,30 @@ class Account {
return _blocksSanitized
}

public async saveError(message: string): Promise<void> {
const _date = new Date().toLocaleDateString()
const _errorString = `${_date} - ${message}`

this.errors = [...this.errors, _errorString]
await this.store()

if (this.errorsThresholdReached() && this.notifications) {
await mailer.sendAccountErrorsEmail(message, this.contactEmail, this.uuid)
}
}

private errorsThresholdReached(): boolean {
return this.errors.length % this.errorsBeforeEmailSent === 0
}

private generateRedisPayload(): string {
const _accountDetails: IAccountPrivate = {
name: this.name,
description: this.description,
contactEmail: this.contactEmail,
notifications: this.notifications,
maxNumberOfBlocks: this.maxNumberOfBlocks,
errors: this.errors,
uuid: this.uuid,
}
return JSON.stringify(_accountDetails)
Expand Down
Loading

0 comments on commit 5470139

Please sign in to comment.