Skip to content

Commit

Permalink
feat: add global errors (#150)
Browse files Browse the repository at this point in the history
feat: add global errors

test: fix login test

test: fix login test
  • Loading branch information
tomicvladan committed Jun 7, 2023
1 parent 08cda1d commit 4568cbc
Show file tree
Hide file tree
Showing 15 changed files with 196 additions and 20 deletions.
3 changes: 2 additions & 1 deletion assets/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,6 @@
"ACCESS": "Access",
"DISCLAIMER": "Disclaimer: Account integrity persistence and security are not assured. Expect that funds used for account might be lost, as well as any data the account uses.",
"DAPP_SIGN_MESSAGE_MESSAGE": "Dapp with ID {dappId} wants to sign the following message: {message}. Do you want to sign the message?",
"EXTENSION_SIGN_MESSAGE_MESSAGE": "Extension with ID {dappId} wants to sign the following message: {message}. Do you want to sign the message?"
"EXTENSION_SIGN_MESSAGE_MESSAGE": "Extension with ID {dappId} wants to sign the following message: {message}. Do you want to sign the message?",
"ERROR_USER_NOT_LOGGED_IN": "User is not logged in"
}
1 change: 1 addition & 0 deletions src/constants/background-actions.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ enum BackgroundAction {
GET_ALL_DAPP_IDS = 'get-all-dapp-ids',
GET_DAPP_SETTINGS = 'get-dapp-settings',
UPDATE_DAPP_SETTINGS = 'update-dapp-settings',
GET_ERROR = 'get-error',
ECHO = 'echo',
}

Expand Down
13 changes: 13 additions & 0 deletions src/constants/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ErrorObject } from '../model/error.model'

export enum ErrorCode {
USER_NOT_LOGGED_IN,
}

export const errors: Record<ErrorCode, ErrorObject> = {
[ErrorCode.USER_NOT_LOGGED_IN]: {
code: ErrorCode.USER_NOT_LOGGED_IN,
priority: 2,
message: 'ERROR_USER_NOT_LOGGED_IN',
},
}
20 changes: 20 additions & 0 deletions src/listeners/message-listeners/general.listener.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import BackgroundAction from '../../constants/background-actions.enum'
import { Errors } from '../../services/error.service'
import { createMessageHandler } from './message-handler'

const errors = new Errors()

export async function getLastError(): Promise<string> {
const errorMessage = await errors.getMostImportantMessage()

return errorMessage
}

const messageHandler = createMessageHandler([
{
action: BackgroundAction.GET_ERROR,
handler: getLastError,
},
])

export default messageHandler
3 changes: 2 additions & 1 deletion src/listeners/message-listeners/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import locales from './locales.listener'
import account from './account.listener'
import settings from './settings.listener'
import signer from './signer.listener'
import general from './general.listener'
import test from './test.listener'
import { isInternalMessage, isOtherExtension } from '../../utils/extension'
import { DAPP_ACTIONS, E2E_ACTIONS } from '../../constants/dapp-actions.enum'
import BackgroundAction from '../../constants/background-actions.enum'

const listenrs = [auth, fdpStorage, locales, account, settings, signer, test]
const listenrs = [auth, fdpStorage, locales, account, settings, signer, general, test]

export function messageHandler(
message: Message<unknown>,
Expand Down
9 changes: 5 additions & 4 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import './listeners'
import { SessionService } from './services/session.service'
import './services/update.service'
import './live-reload'
import { Errors } from './services/error.service'

//eslint-disable-next-line @typescript-eslint/no-unused-vars
const session = new SessionService()
const errors = new Errors()

try {
session.load()
// eslint-disable-next-line no-empty
} catch (error) {}
;(async () => {
await Promise.all([session.load(), errors.refreshGlobalError()])
})()
4 changes: 4 additions & 0 deletions src/messaging/content-api.messaging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ export function updateDappSettings(dapp: Dapp): Promise<void> {
return sendMessage<Dapp, void>(BackgroundAction.UPDATE_DAPP_SETTINGS, dapp)
}

export function getGlobalError(): Promise<string> {
return sendMessage<void, string>(BackgroundAction.GET_ERROR)
}

export function echo<Data>(data: Data): Promise<Data> {
return sendMessage<Data, Data>(BackgroundAction.ECHO, data)
}
7 changes: 7 additions & 0 deletions src/model/error.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ErrorCode } from '../constants/errors'

export interface ErrorObject {
code: ErrorCode
priority: number
message: string
}
8 changes: 8 additions & 0 deletions src/model/storage/general.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { ErrorCode } from '../../constants/errors'
import { ErrorObject } from '../error.model'

export type Errors = Partial<Record<ErrorCode, ErrorObject>>

export interface General {
errors: Errors
}
69 changes: 69 additions & 0 deletions src/services/error.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { ErrorCode, errors as globalErrors } from '../constants/errors'
import { ErrorObject } from '../model/error.model'
import { removeWarningBadge, setWarningBadge } from '../utils/extension'
import { Storage } from './storage/storage.service'
import { Errors as ErrorsModel } from '../model/storage/general.model'
import { Locales } from './locales.service'

Check warning on line 6 in src/services/error.service.ts

View workflow job for this annotation

GitHub Actions / test (16.x)

'Locales' is defined but never used

export class Errors {
private storage: Storage = new Storage()

public async refreshGlobalError() {
const errors = await this.getErrors()

;(this.hasErrors(errors) ? setWarningBadge : removeWarningBadge)()
}

public async getMostImportantMessage(): Promise<string | null> {
const errors = await this.getErrors()

const mostImportantError = Object.values(errors).reduce(
(mostImportantError, currentError) => {
return mostImportantError.priority < currentError.priority ? mostImportantError : currentError
},
{ priority: 100 } as ErrorObject,
)

return mostImportantError ? mostImportantError.message : null
}

public async addGlobalError(errorCode: ErrorCode): Promise<ErrorObject> {
const errors = await this.getErrors()

const error = { ...globalErrors[errorCode] }

errors[errorCode] = error

await this.updateErrors(errors)

setWarningBadge()

return error
}

public async removeGlobalError(errorCode: ErrorCode) {
const errors = await this.getErrors()

delete errors[errorCode]

await this.updateErrors(errors)

if (this.hasErrors(errors)) {
removeWarningBadge()
}
}

private hasErrors(errors: ErrorsModel): boolean {
return errors && Object.keys(errors).length === 0
}

private async getErrors(): Promise<ErrorsModel> {
const { errors } = await this.storage.getGeneral()

return errors
}

private updateErrors(errors: ErrorsModel): Promise<void> {
return this.storage.updateGeneral({ errors })
}
}
28 changes: 20 additions & 8 deletions src/services/session.service.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ErrorCode } from '../constants/errors'
import { isStorageSession } from '../messaging/message.asserts'
import { Address } from '../model/general.types'
import { Network } from '../model/storage/network.model'
Expand All @@ -10,12 +11,13 @@ import {
wordArrayToBytes,
wordArrayToHex,
} from '../utils/encryption'
import { removeWarningBadge, setWarningBadge } from '../utils/extension'
import { generateRandomString } from '../utils/random'
import { Errors } from './error.service'
import { Storage } from './storage/storage.service'

export class SessionService {
private storage: Storage = new Storage()
private errors: Errors = new Errors()

public async open(
ensUserName: string,
Expand All @@ -26,7 +28,7 @@ export class SessionService {
): Promise<void> {
const key = await this.encryptSeed(seed)

removeWarningBadge()
await this.removeGlobalError()

return this.storage.setSession({
ensUserName,
Expand All @@ -43,14 +45,16 @@ export class SessionService {
const session = await this.processSession(rawSession)

if (!session) {
await this.setGlobalError()

throw new Error('User is not logged in')
}

return session
}

public close(): Promise<void> {
setWarningBadge()
public async close(): Promise<void> {
await this.setGlobalError()

return this.storage.deleteSession()
}
Expand All @@ -63,14 +67,14 @@ export class SessionService {

private async processSession(session: StorageSession): Promise<MemorySession> {
if (!session) {
setWarningBadge()
await this.setGlobalError()

return null
}

if (!isStorageSession(session)) {
this.storage.deleteSession()
setWarningBadge()
await this.setGlobalError()

return null
}
Expand All @@ -81,12 +85,12 @@ export class SessionService {

if (!session.key.seed) {
this.storage.deleteSession()
setWarningBadge()
await this.setGlobalError()

return null
}

removeWarningBadge()
await this.removeGlobalError()

return memorySession
}
Expand Down Expand Up @@ -134,6 +138,14 @@ export class SessionService {
return wordArrayToBytes(decrypt(sessionKey, hexToWordArray(encryptedSeed)))
}

private setGlobalError() {
return this.errors.addGlobalError(ErrorCode.USER_NOT_LOGGED_IN)
}

private removeGlobalError() {
return this.errors.removeGlobalError(ErrorCode.USER_NOT_LOGGED_IN)
}

private generateRandomUrl(): string {
return `https://www.${generateRandomString(50)}.com`
}
Expand Down
7 changes: 7 additions & 0 deletions src/services/storage/storage-factories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { StorageSession } from '../../model/storage/session.model'
import { AccountDapps, Dapp, Dapps } from '../../model/storage/dapps.model'
import { Accounts } from '../../model/storage/account.model'
import { DappId } from '../../model/general.types'
import { General } from '../../model/storage/general.model'

export function networkFactory(): Network {
return { ...networks[0] }
Expand Down Expand Up @@ -46,3 +47,9 @@ export function dappFactory(dappId: DappId): Dapp {
export function accountsFactory(): Accounts {
return {}
}

export function generalFactory(): General {
return {
errors: {},
}
}
11 changes: 11 additions & 0 deletions src/services/storage/storage.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import {
accountsFactory,
dappFactory,
accountDappsFactory,
generalFactory,
} from './storage-factories'
import { DappId } from '../../model/general.types'
import { AccountDapps, Dapp, Dapps, PodPermission } from '../../model/storage/dapps.model'
import { StorageAccount, Accounts } from '../../model/storage/account.model'
import { General } from '../../model/storage/general.model'

/**
* Sets any value to the extension storage
Expand Down Expand Up @@ -118,6 +120,7 @@ export class Storage {
static readonly dappsKey = 'dapps'
static readonly accountsKey = 'accounts'
static readonly storageVersion = 'storage-version'
static readonly generalKey = 'general'

constructor() {
chrome.storage.onChanged.addListener(this.onChangeListener.bind(this))
Expand Down Expand Up @@ -300,6 +303,14 @@ export class Storage {
return deleteEntry(Storage.accountsKey)
}

public updateGeneral(data: Partial<General>): Promise<void> {
return updateObject(Storage.generalKey, data)
}

public getGeneral(): Promise<General> {
return getObject(Storage.generalKey, generalFactory)
}

public onNetworkChange(listener: (network: Network) => void) {
this.setListener(Storage.networkKey, listener)
}
Expand Down
27 changes: 21 additions & 6 deletions src/ui/settings/pages/main/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,41 +8,56 @@ import Security from '@mui/icons-material/Security'
import Section from './section.component'
import { useNavigate } from 'react-router-dom'
import RouteCodes from '../../routes/route-codes'
import { getCurrentUser, logout, openAuthPage } from '../../../../messaging/content-api.messaging'
import {
getCurrentUser,
getGlobalError,
logout,
openAuthPage,
} from '../../../../messaging/content-api.messaging'
import { UserResponse } from '../../../../model/internal-messages.model'
import UserInfo from './user-info.component'
import ErrorMessage from '../../../common/components/error-message/error-message.component'

const Main = () => {
const navigate = useNavigate()
const [user, setUser] = useState<UserResponse>(null)
const [globalError, setGlobalError] = useState<string | undefined>(undefined)

const loadData = async () => {
const [user, globalError] = await Promise.all([getCurrentUser(), getGlobalError()])

const loadUser = async () => {
const user = await getCurrentUser()
setUser(user)
setGlobalError(globalError)
}

const onLoginOrRegister = async () => {
await openAuthPage()
loadData()
}

const onLogout = async () => {
await logout()
setUser(null)
loadData()
}

useEffect(() => {
loadUser()
loadData()
}, [])

return (
<>
<Typography variant="h5" align="center">
Blossom
</Typography>
{globalError && <ErrorMessage>{intl.get(globalError)}</ErrorMessage>}
<Stack spacing={3} sx={{ paddingTop: '20px' }}>
{user ? (
<UserInfo user={user} onLogout={onLogout} />
) : (
<Section
description={intl.get('LOGIN_REGISTER_DESCRIPTION')}
image={<VpnKey />}
onClick={openAuthPage}
onClick={onLoginOrRegister}
dataTestId="settings-registration-login"
>
{intl.get('LOGIN_OR_REGISTER')}
Expand Down

0 comments on commit 4568cbc

Please sign in to comment.