From b48089ffe45ad53e6bdaad059d4b4d587d94775e Mon Sep 17 00:00:00 2001 From: Loi Phan Date: Wed, 27 Sep 2023 15:48:51 +0700 Subject: [PATCH] Add common service and base class --- README.MD | 1 - typescript-practice/src/pages/index.html | 2 +- .../src/ts/constants/config.ts | 28 +++ .../src/ts/constants/message.ts | 31 ++++ typescript-practice/src/ts/helpers/helpers.ts | 69 ++++++++ .../src/ts/services/commonService.ts | 104 +++++++++++ .../src/ts/services/firebaseService.ts | 163 ++++++++++++++++++ typescript-practice/src/ts/services/index.ts | 1 + .../src/ts/services/localStorageService.ts | 25 +++ 9 files changed, 422 insertions(+), 2 deletions(-) delete mode 100644 README.MD create mode 100644 typescript-practice/src/ts/constants/config.ts create mode 100644 typescript-practice/src/ts/constants/message.ts create mode 100644 typescript-practice/src/ts/helpers/helpers.ts create mode 100644 typescript-practice/src/ts/services/commonService.ts create mode 100644 typescript-practice/src/ts/services/firebaseService.ts create mode 100644 typescript-practice/src/ts/services/index.ts create mode 100644 typescript-practice/src/ts/services/localStorageService.ts diff --git a/README.MD b/README.MD deleted file mode 100644 index 12e2d47..0000000 --- a/README.MD +++ /dev/null @@ -1 +0,0 @@ -typescript-training diff --git a/typescript-practice/src/pages/index.html b/typescript-practice/src/pages/index.html index 3ddbe41..1e0e9c1 100644 --- a/typescript-practice/src/pages/index.html +++ b/typescript-practice/src/pages/index.html @@ -1,4 +1,4 @@ - + diff --git a/typescript-practice/src/ts/constants/config.ts b/typescript-practice/src/ts/constants/config.ts new file mode 100644 index 0000000..9a030a0 --- /dev/null +++ b/typescript-practice/src/ts/constants/config.ts @@ -0,0 +1,28 @@ +export const DATABASE_URL = + 'https://javascript-training-81f7a-default-rtdb.asia-southeast1.firebasedatabase.app'; +export const TIME_OUT_SEC = 3; + +export enum BTN_CONTENT { + GOT_IT = 'Got it!', + OK = 'Ok', +} + +export const LOCAL_STORAGE = { + ACCESS_TOKEN: 'accessToken', +}; + +export enum TYPE_TOAST { + success = 'success', + error = 'error', +} +export enum MARK_ICON { + success = 'check', + error = 'error', +} + +export const REGEX = { + PASSWORD: + /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/, + EMAIL: + /^(([^<>()[\]\\.,;:\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,}))$/, +}; diff --git a/typescript-practice/src/ts/constants/message.ts b/typescript-practice/src/ts/constants/message.ts new file mode 100644 index 0000000..6a4bad3 --- /dev/null +++ b/typescript-practice/src/ts/constants/message.ts @@ -0,0 +1,31 @@ +export const PASSWORD_NOT_MATCH = 'Password not match!'; + +export const PASSWORD_NOT_STRONG = + 'Password must at least one uppercase, one lowercase letter, one number and one special character!'; + +export const ERROR_MESSAGE_DEFAULT = ['Something went wrong!']; + +export const TIME_OUT_ERROR = 'Connection time out! Please try again!'; + +export const ERROR_CREDENTIAL = { + title: 'Error Credential', + message: 'Email or password not match! Please try again!', +}; + +export const DEFAULT_TITLE_ERROR_TOAST = 'Error'; + +export const USER_EXIST_ERROR = 'User is exists! Please try another email!'; + +export const ADD_WALLET_SUCCESS = 'Add wallet success!'; + +export const DEFAULT_MESSAGE = 'Press OK to continue!'; + +export const ADD_TRANSACTION_SUCCESS = 'Add success!'; + +export const UPDATE_TRANSACTION_SUCCESS = 'Update success!'; + +export const REGISTER_SUCCESS = 'Register Success'; + +export const REQUIRED_MESSAGE = (field) => `The ${field} is required!`; + +export const INVALID_EMAIL_FORMAT = `The email is not correct!`; diff --git a/typescript-practice/src/ts/helpers/helpers.ts b/typescript-practice/src/ts/helpers/helpers.ts new file mode 100644 index 0000000..278e65d --- /dev/null +++ b/typescript-practice/src/ts/helpers/helpers.ts @@ -0,0 +1,69 @@ +import * as MESSAGE from '../constants/message'; +import { TIME_OUT_SEC, REGEX } from '../constants/config'; +import FirebaseService from '../services/firebaseService'; + +/** + * Validate password + * @param {string} password Password input + * @returns {boolean} Return true if validate password success, otherwise return false + */ +export const isValidPassword = (password: string): boolean => { + return REGEX.PASSWORD.test(password); +}; + +export const isValidateEmail = (email: string): RegExpMatchArray | null => { + return String(email).toLowerCase().match(REGEX.EMAIL); +}; + +export const compare2Password = ( + password: string, + passwordConfirm: string, +): boolean => { + return password === passwordConfirm; +}; + +/** + * A waiting function with s second + * @param {number} s The time will be waiting + * @returns {Promise} A Promise will be only reject after s second + */ +export const timeout = (s: number): Promise => { + return new Promise((_, reject) => { + setTimeout(() => { + FirebaseService.disconnect(); + reject(MESSAGE.TIME_OUT_ERROR); + }, s * 1000); + }); +}; + +export const createIdUser = (): number => { + return new Date().getTime(); +}; + +/** + * A function waiting the action need to be perform and throw error after s second. + * @param {Function} action The action need to be perform. + * @returns { Object || Error } Return the any object from Firebase or Error + */ +export const timeOutConnect = async ( + action: Promise, +): Promise => { + const result: object | string = await Promise.race([ + action, + timeout(TIME_OUT_SEC), + ]); + + return result; +}; + +export const renderRequiredText = (field: string, element: Element) => { + const markup: string = ` +

${MESSAGE.REQUIRED_MESSAGE(field)}

+ `; + + element.insertAdjacentHTML('afterend', markup); +}; + +export const redirectToLoginPage = () => { + window.location.replace('/login'); +}; diff --git a/typescript-practice/src/ts/services/commonService.ts b/typescript-practice/src/ts/services/commonService.ts new file mode 100644 index 0000000..eb3d611 --- /dev/null +++ b/typescript-practice/src/ts/services/commonService.ts @@ -0,0 +1,104 @@ +import { + convertDataObjectToModel, + convertModelToDataObject, + timeOutConnect, +} from '../helpers/helpers'; +import FirebaseService from './firebaseService'; + +export default class CommonService { + constructor() { + this.defaultPath = '/'; + this.firebaseService = FirebaseService; + } + + /** + * Connect to Firebase Databse + */ + connectToDb() { + this.firebaseService.reconnect(); + } + + async getDataFromProp(property, value, path = this.defaultPath) { + this.connectToDb(); + const data = this.firebaseService.getDataFromProp(path, property, value); + const result = await timeOutConnect(data); + + if (result.id && result.data) { + return convertDataObjectToModel(result); + } + + return null; + } + + /** + * Save data on database + * @param {*} data The data wants to save on database + * @param {string} path The path of database + */ + async save(model) { + this.connectToDb(); + const results = convertModelToDataObject(model); + + const saveData = this.firebaseService.save( + results.data, + this.defaultPath + results.id, + ); + + await timeOutConnect(saveData); + } + + /** + * + * @param {string} id The string of data object + * @param {string} path The path of database + * @returns {Object || null} Return the object if has, otherwise return null + */ + async getDataFromId(id, path = this.defaultPath) { + this.connectToDb(); + const result = await this.firebaseService.getDataFromId(id, path); + const data = await timeOutConnect(result); + + if (data) return data; + + return null; + } + + async getAllDataFromPath(path = this.defaultPath) { + this.connectToDb(); + + const results = await timeOutConnect( + this.firebaseService.getAllDataFromPath(path), + ); + + if (results) { + // Convert format object + return results.map((data) => { + return convertDataObjectToModel(data); + }); + } + + return null; + } + + async getListDataFromProp(property, value, path = this.defaultPath) { + this.connectToDb(); + + const results = await timeOutConnect( + this.firebaseService.getListDataFromProp(path, property, value), + ); + if (results) { + // Convert format object + + return results.map((data) => { + return convertDataObjectToModel(data); + }); + } + + return null; + } + + async deleteData(id, path = this.defaultPath) { + this.connectToDb(); + await timeOutConnect(this.firebaseService.delete(id, path)); + } +} diff --git a/typescript-practice/src/ts/services/firebaseService.ts b/typescript-practice/src/ts/services/firebaseService.ts new file mode 100644 index 0000000..d0c9b09 --- /dev/null +++ b/typescript-practice/src/ts/services/firebaseService.ts @@ -0,0 +1,163 @@ +import { FirebaseApp, initializeApp } from 'firebase/app'; +import { + getDatabase, + ref, + set, + goOffline, + goOnline, + onValue, + remove, + Database, +} from 'firebase/database'; +import { DATABASE_URL } from '../constants/config'; + +class FirebaseService { + private app: FirebaseApp; + private db: Database; + + constructor() { + const firebaseConfig = { + databaseURL: DATABASE_URL, + }; + this.app = initializeApp(firebaseConfig); + this.db = getDatabase(this.app); + } + + /** + * Save data in database + * @param {Object} data The object need to save into database + * @param {string} path The path of database need to be save + * @returns {Promise} Return the resolves when write to database completed + */ + save(data: object, path: string): Promise { + return set(ref(this.db, path), data); + } + + delete(id: string, path: string) { + return remove(ref(this.db, path + id)); + } + + /** + * Disconnect to database + */ + disconnect() { + goOffline(this.db); + } + + /** + * Reconnect to database + */ + reconnect() { + goOnline(this.db); + } + + /** + * Find id of value by property in database + * @param {string} path The path of database to be found + * @param {string} property The property of the value need to be found + * @param {value} value The value to compare in database + * @returns {Promise} Return the resolve when find completed + */ + getDataFromProp(path: string, property: string, value: object): Promise { + return new Promise((resolve) => { + onValue( + ref(this.db, path), + (snapshot) => { + let id: string | null = null; + let data: object | null = null; + + // snapshot is a type of data by Firebase define + snapshot.forEach((childSnapshot) => { + const dataTemp = childSnapshot.val(); + + if (dataTemp[property] === value) { + id = childSnapshot.key; + data = dataTemp; + } + }); + resolve({ id, data }); + }, + { + onlyOnce: true, + }, + ); + }); + } + + /** + * Get data object from Id + * @param {string} id The id of data object + * @param {string} path The path of data save in database + * @returns {Promise} Return new Promise + */ + getDataFromId(id: string, path: string): Promise { + return new Promise((resolve) => { + onValue( + ref(this.db, path + id), + (snapshot) => { + resolve(snapshot.val()); + }, + { + onlyOnce: true, + }, + ); + }); + } + + getAllDataFromPath(path) { + return new Promise((resolve) => { + onValue( + ref(this.db, path), + (snapshot) => { + const listData: object[] = []; + + // snapshot is a type of data by Firebase define + snapshot.forEach((childSnapshot) => { + let id: string | null = null; + let data: object | null = null; + + id = childSnapshot.key; + data = childSnapshot.val(); + + listData.push({ id, data }); + }); + resolve(listData); + }, + { + onlyOnce: true, + }, + ); + }); + } + + getListDataFromProp(path: string, property: string, value: object) { + return new Promise((resolve) => { + onValue( + ref(this.db, path), + (snapshot) => { + let id: string; + let data: object; + const listData: object[] = []; + + // snapshot is a type of data by Firebase define + snapshot.forEach((childSnapshot) => { + const dataTemp = childSnapshot.val(); + + if (dataTemp[property] === value) { + id = childSnapshot.key; + data = dataTemp; + + listData.push({ id, data }); + } + }); + resolve(listData); + }, + { + onlyOnce: true, + }, + ); + }); + } +} + +export default new FirebaseService(); diff --git a/typescript-practice/src/ts/services/index.ts b/typescript-practice/src/ts/services/index.ts new file mode 100644 index 0000000..a5d1d4c --- /dev/null +++ b/typescript-practice/src/ts/services/index.ts @@ -0,0 +1 @@ +export default class Service {} diff --git a/typescript-practice/src/ts/services/localStorageService.ts b/typescript-practice/src/ts/services/localStorageService.ts new file mode 100644 index 0000000..93ca214 --- /dev/null +++ b/typescript-practice/src/ts/services/localStorageService.ts @@ -0,0 +1,25 @@ +class LocalStorageService { + private localStorage: Storage; + + constructor() { + this.localStorage = localStorage; + } + + add(key: string, value: string) { + this.localStorage.setItem(key, value); + } + + get(key: string) { + return this.localStorage.getItem(key); + } + + remove(key: string) { + this.localStorage.removeItem(key); + } + + clear() { + this.localStorage.clear(); + } +} + +export default new LocalStorageService();