Add documentation to the helpers module.

This commit is contained in:
Spencer Ofwiti
2021-05-11 14:34:23 +03:00
parent c9f73899c3
commit e0c045cdfa
37 changed files with 1403 additions and 585 deletions

View File

@@ -1,7 +1,20 @@
/**
* Returns the sum of all values in an array.
*
* @example
* Prints 6 for the array [1, 2, 3]:
* ```typescript
* console.log(arraySum([1, 2, 3]));
* ```
*
* @param arr - An array of numbers.
* @return The sum of all values in the array.
*/
function arraySum(arr: Array<number>): number {
return arr.reduce((accumulator, current) => accumulator + current, 0);
}
/** @exports */
export {
arraySum
};

View File

@@ -1,3 +1,15 @@
/**
* Copies set text to clipboard.
*
* @example
* copies 'Hello World!' to the clipboard and prints "true":
* ```typescript
* console.log(copyToClipboard('Hello World!'));
* ```
*
* @param text - The text to be copied to the clipboard.
* @returns true - If the copy operation is successful.
*/
function copyToClipboard(text: any): boolean {
// create our hidden div element
const hiddenCopy: HTMLDivElement = document.createElement('div');
@@ -48,6 +60,7 @@ function copyToClipboard(text: any): boolean {
return true;
}
/** @exports */
export {
copyToClipboard
};

View File

@@ -1,3 +1,4 @@
// Application imports
import { CustomErrorStateMatcher } from '@app/_helpers/custom-error-state-matcher';
describe('CustomErrorStateMatcher', () => {

View File

@@ -1,7 +1,20 @@
import {ErrorStateMatcher} from '@angular/material/core';
// Core imports
import {FormControl, FormGroupDirective, NgForm} from '@angular/forms';
import {ErrorStateMatcher} from '@angular/material/core';
/**
* Custom provider that defines how form controls behave with regards to displaying error messages.
*
* @implements ErrorStateMatcher
*/
export class CustomErrorStateMatcher implements ErrorStateMatcher{
/**
* Checks whether an invalid input has been made and an error should be made.
*
* @param control - Tracks the value and validation status of an individual form control.
* @param form - Binding of an existing FormGroup to a DOM element.
* @returns true - If an invalid input has been made to the form control.
*/
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
const isSubmitted: boolean = form && form.submitted;
return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));

View File

@@ -1,3 +1,4 @@
// Application imports
import { CustomValidator } from '@app/_helpers/custom.validator';
describe('Custom.Validator', () => {

View File

@@ -1,6 +1,15 @@
// Core imports
import {AbstractControl, ValidationErrors} from '@angular/forms';
/**
* Provides methods to perform custom validation to form inputs.
*/
export class CustomValidator {
/**
* Sets errors to the confirm password input field if it does not match with the value in the password input field.
*
* @param control - The control object of the form being validated.
*/
static passwordMatchValidator(control: AbstractControl): void {
const password: string = control.get('password').value;
const confirmPassword: string = control.get('confirmPassword').value;
@@ -9,6 +18,13 @@ export class CustomValidator {
}
}
/**
* Sets errors to a form field if it does not match with the regular expression given.
*
* @param regex - The regular expression to match with the form field.
* @param error - Defines the map of errors to return from failed validation checks.
* @returns The map of errors returned from failed validation checks.
*/
static patternValidator(regex: RegExp, error: ValidationErrors): ValidationErrors | null {
return (control: AbstractControl): { [key: string]: any } => {
if (!control.value) {

View File

@@ -1,3 +1,11 @@
/**
* Exports data to a CSV format and provides a download file.
*
* @param arrayData - An array of data to be converted to CSV format.
* @param filename - The name of the file to be downloaded.
* @param delimiter - The delimiter to be used when converting to CSV format.
* Defaults to commas.
*/
function exportCsv(arrayData: Array<any>, filename: string, delimiter: string = ','): void {
if (arrayData === undefined || arrayData.length === 0) {
alert('No data to be exported!');
@@ -26,13 +34,7 @@ function exportCsv(arrayData: Array<any>, filename: string, delimiter: string =
downloadLink.click();
}
function removeSpecialChar(str: string): string {
if (str === null || str === '') {
return '';
}
return str.replace(/[^a-zA-Z0-9 ]/g, '');
}
/** @exports */
export {
exportCsv
};

View File

@@ -1,4 +1,5 @@
import { GlobalErrorHandler } from './global-error-handler';
// Application imports
import { GlobalErrorHandler } from '@app/_helpers/global-error-handler';
describe('GlobalErrorHandler', () => {
it('should create an instance', () => {

View File

@@ -1,11 +1,26 @@
import {ErrorHandler, Injectable} from '@angular/core';
import {LoggingService} from '@app/_services/logging.service';
// Core imports
import {HttpErrorResponse} from '@angular/common/http';
import {ErrorHandler, Injectable} from '@angular/core';
import {Router} from '@angular/router';
// A generalized http response error
// Application imports
import {LoggingService} from '@app/_services/logging.service';
/**
* A generalized http response error.
*
* @extends Error
*/
export class HttpError extends Error {
/** The error's status code. */
public status: number;
/**
* Initialize the HttpError class.
*
* @param message - The message given by the error.
* @param status - The status code given by the error.
*/
constructor(message: string, status: number) {
super(message);
this.status = status;
@@ -13,10 +28,25 @@ export class HttpError extends Error {
}
}
/**
* Provides a hook for centralized exception handling.
*
* @extends ErrorHandler
*/
@Injectable()
export class GlobalErrorHandler extends ErrorHandler {
/**
* An array of sentence sections that denote warnings.
* @private
*/
private sentencesForWarningLogging: Array<string> = [];
/**
* Initialization of the Global Error Handler.
*
* @param loggingService - A service that provides logging capabilities.
* @param router - A service that provides navigation among views and URL manipulation capabilities.
*/
constructor(
private loggingService: LoggingService,
private router: Router
@@ -24,6 +54,11 @@ export class GlobalErrorHandler extends ErrorHandler {
super();
}
/**
* Handles different types of errors.
*
* @param error - An error objects thrown when a runtime errors occurs.
*/
handleError(error: Error): void {
this.logError(error);
const message: string = error.message ? error.message : error.toString();
@@ -44,21 +79,13 @@ export class GlobalErrorHandler extends ErrorHandler {
throw error;
}
logError(error: any): void {
const route: string = this.router.url;
if (error instanceof HttpErrorResponse) {
this.loggingService.sendErrorLevelMessage(
`There was an HTTP error on route ${route}.\n${error.message}.\nStatus code: ${(error as HttpErrorResponse).status}`,
this, {error});
} else if (error instanceof TypeError) {
this.loggingService.sendErrorLevelMessage(`There was a Type error on route ${route}.\n${error.message}`, this, {error});
} else if (error instanceof Error) {
this.loggingService.sendErrorLevelMessage(`There was a general error on route ${route}.\n${error.message}`, this, {error});
} else {
this.loggingService.sendErrorLevelMessage(`Nobody threw an error but something happened on route ${route}!`, this, {error});
}
}
/**
* Checks if an error is of type warning.
*
* @param errorTraceString - A description of the error and it's stack trace.
* @returns true - If the error is of type warning.
* @private
*/
private isWarning(errorTraceString: string): boolean {
let isWarning: boolean = true;
if (errorTraceString.includes('/src/app/')) {
@@ -73,4 +100,24 @@ export class GlobalErrorHandler extends ErrorHandler {
return isWarning;
}
/**
* Write appropriate logs according to the type of error.
*
* @param error - An error objects thrown when a runtime errors occurs.
*/
logError(error: any): void {
const route: string = this.router.url;
if (error instanceof HttpErrorResponse) {
this.loggingService.sendErrorLevelMessage(
`There was an HTTP error on route ${route}.\n${error.message}.\nStatus code: ${(error as HttpErrorResponse).status}`,
this, {error});
} else if (error instanceof TypeError) {
this.loggingService.sendErrorLevelMessage(`There was a Type error on route ${route}.\n${error.message}`, this, {error});
} else if (error instanceof Error) {
this.loggingService.sendErrorLevelMessage(`There was a general error on route ${route}.\n${error.message}`, this, {error});
} else {
this.loggingService.sendErrorLevelMessage(`Nobody threw an error but something happened on route ${route}!`, this, {error});
}
}
}

View File

@@ -1,6 +1,13 @@
/** Provides an avenue of fetching resources via HTTP calls. */
function HttpGetter(): void {}
HttpGetter.prototype.get = filename => new Promise((resolve, reject) => {
/**
* Fetches files using HTTP get requests.
*
* @param filename - The filename to fetch.
* @returns The HTTP response text.
*/
HttpGetter.prototype.get = (filename: string) => new Promise((resolve, reject) => {
const xhr: XMLHttpRequest = new XMLHttpRequest();
xhr.addEventListener('load', (e) => {
if (xhr.status === 200) {
@@ -13,6 +20,7 @@ HttpGetter.prototype.get = filename => new Promise((resolve, reject) => {
xhr.send();
});
/** @exports */
export {
HttpGetter
};

View File

@@ -1,10 +1,10 @@
export * from '@app/_helpers/array-sum';
export * from '@app/_helpers/clipboard-copy';
export * from '@app/_helpers/custom.validator';
export * from '@app/_helpers/custom-error-state-matcher';
export * from '@app/_helpers/mock-backend';
export * from '@app/_helpers/array-sum';
export * from '@app/_helpers/http-getter';
export * from '@app/_helpers/global-error-handler';
export * from '@app/_helpers/export-csv';
export * from '@app/_helpers/global-error-handler';
export * from '@app/_helpers/http-getter';
export * from '@app/_helpers/mock-backend';
export * from '@app/_helpers/read-csv';
export * from '@app/_helpers/clipboard-copy';
export * from '@app/_helpers/schema-validation';

View File

@@ -1,9 +1,18 @@
// Core imports
import {HTTP_INTERCEPTORS, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse} from '@angular/common/http';
import {Injectable} from '@angular/core';
// Third party imports
import {Observable, of, throwError} from 'rxjs';
import {delay, dematerialize, materialize, mergeMap} from 'rxjs/operators';
// Application imports
import {Action, AreaName, AreaType, Category, Token} from '@app/_models';
/** A mock of the curated account types. */
const accountTypes: Array<string> = ['user', 'cashier', 'vendor', 'tokenagent', 'group'];
/** A mock of actions made by the admin staff. */
const actions: Array<Action> = [
{ id: 1, user: 'Tom', role: 'enroller', action: 'Disburse RSV 100', approval: false },
{ id: 2, user: 'Christine', role: 'admin', action: 'Change user phone number', approval: true },
@@ -13,38 +22,93 @@ const actions: Array<Action> = [
{ id: 6, user: 'Patience', role: 'enroller', action: 'Change user information', approval: false }
];
const tokens: Array<Token> = [
/** A mock of curated area names. */
const areaNames: Array<AreaName> = [
{
name: 'Giftable Reserve', symbol: 'GRZ', address: '0xa686005CE37Dce7738436256982C3903f2E4ea8E', supply: '1000000001000000000000000000',
decimals: '18', reserves: {}
name: 'Mukuru Nairobi',
locations: ['kayaba', 'kayba', 'kambi', 'mukuru', 'masai', 'hazina', 'south', 'tetra', 'tetrapak', 'ruben', 'rueben', 'kingston',
'korokocho', 'kingstone', 'kamongo', 'lungalunga', 'sinai', 'sigei', 'lungu', 'lunga lunga', 'owino road', 'seigei']
},
{
name: 'Demo Token', symbol: 'DEMO', address: '0xc80D6aFF8194114c52AEcD84c9f15fd5c8abb187', supply: '99999999999999998976',
decimals: '18', reserves: {'0xa686005CE37Dce7738436256982C3903f2E4ea8E': {weight: '1000000', balance: '99999999999999998976'}},
reserveRatio: '1000000', owner: '0x3Da99AAD2D9CA01D131eFc3B17444b832B31Ff4a'
name: 'Kinango Kwale',
locations: ['amani', 'bofu', 'chibuga', 'chikomani', 'chilongoni', 'chigojoni', 'chinguluni', 'chigato', 'chigale', 'chikole',
'chilongoni', 'chilumani', 'chigojoni', 'chikomani', 'chizini', 'chikomeni', 'chidzuvini', 'chidzivuni', 'chikuyu', 'chizingo',
'doti', 'dzugwe', 'dzivani', 'dzovuni', 'hanje', 'kasemeni', 'katundani', 'kibandaogo', 'kibandaongo', 'kwale', 'kinango',
'kidzuvini', 'kalalani', 'kafuduni', 'kaloleni', 'kilibole', 'lutsangani', 'peku', 'gona', 'guro', 'gandini', 'mkanyeni', 'myenzeni',
'miyenzeni', 'miatsiani', 'mienzeni', 'mnyenzeni', 'minyenzeni', 'miyani', 'mioleni', 'makuluni', 'mariakani', 'makobeni', 'madewani',
'mwangaraba', 'mwashanga', 'miloeni', 'mabesheni', 'mazeras', 'mazera', 'mlola', 'muugano', 'mulunguni', 'mabesheni', 'miatsani',
'miatsiani', 'mwache', 'mwangani', 'mwehavikonje', 'miguneni', 'nzora', 'nzovuni', 'vikinduni', 'vikolani', 'vitangani', 'viogato',
'vyogato', 'vistangani', 'yapha', 'yava', 'yowani', 'ziwani', 'majengo', 'matuga', 'vigungani', 'vidziweni', 'vinyunduni', 'ukunda',
'kokotoni', 'mikindani']
},
{
name: 'Foo Token', symbol: 'FOO', address: '0x9ceD86089f7aBB5A97B40eb0E7521e7aa308d354', supply: '1000000000000000001014',
decimals: '18', reserves: {'0xa686005CE37Dce7738436256982C3903f2E4ea8E': {weight: '1000000', balance: '1000000000000000001014'}},
reserveRatio: '1000000', owner: '0x3Da99AAD2D9CA01D131eFc3B17444b832B31Ff4a'
name: 'Misc Nairobi',
locations: ['nairobi', 'west', 'lindi', 'kibera', 'kibira', 'kibra', 'makina', 'soweto', 'olympic', 'kangemi', 'ruiru', 'congo',
'kawangware', 'kwangware', 'donholm', 'dagoreti', 'dandora', 'kabete', 'sinai', 'donhom', 'donholm', 'huruma', 'kitengela',
'makadara', ',mlolongo', 'kenyatta', 'mlolongo', 'tassia', 'tasia', 'gatina', '56', 'industrial', 'kariobangi', 'kasarani', 'kayole',
'mathare', 'pipe', 'juja', 'uchumi', 'jogoo', 'umoja', 'thika', 'kikuyu', 'stadium', 'buru buru', 'ngong', 'starehe', 'mwiki',
'fuata', 'kware', 'kabiro', 'embakassi', 'embakasi', 'kmoja', 'east', 'githurai', 'landi', 'langata', 'limuru', 'mathere',
'dagoretti', 'kirembe', 'muugano', 'mwiki', 'toi market']
},
{
name: 'testb', symbol: 'tstb', address: '0xC63cFA91A3BFf41cE31Ff436f67D3ACBC977DB95', supply: '99000', decimals: '18',
reserves: {'0xa686005CE37Dce7738436256982C3903f2E4ea8E': {weight: '1000000', balance: '99000'}}, reserveRatio: '1000000',
owner: '0x3Da99AAD2D9CA01D131eFc3B17444b832B31Ff4a'
name: 'Misc Mombasa',
locations: ['mombasa', 'likoni', 'bangla', 'bangladesh', 'kizingo', 'old town', 'makupa', 'mvita', 'ngombeni', 'ngómbeni', 'ombeni',
'magongo', 'miritini', 'changamwe', 'jomvu', 'ohuru', 'tudor', 'diani']
},
{
name: 'testa', symbol: 'tsta', address: '0x8fA4101ef19D0a078239d035659e92b278bD083C', supply: '9981', decimals: '18',
reserves: {'0xa686005CE37Dce7738436256982C3903f2E4ea8E': {weight: '1000000', balance: '9981'}}, reserveRatio: '1000000',
owner: '0x3Da99AAD2D9CA01D131eFc3B17444b832B31Ff4a'
name: 'Kisauni',
locations: ['bamburi', 'kisauni', 'mworoni', 'nyali', 'shanzu', 'bombolulu', 'mtopanga', 'mjambere', 'majaoni', 'manyani', 'magogoni',
'junda', 'mwakirunge', 'mshomoroni']
},
{
name: 'testc', symbol: 'tstc', address: '0x4A6fA6bc3BfE4C9661bC692D9798425350C9e3D4', supply: '100990', decimals: '18',
reserves: {'0xa686005CE37Dce7738436256982C3903f2E4ea8E': {weight: '1000000', balance: '100990'}}, reserveRatio: '1000000',
owner: '0x3Da99AAD2D9CA01D131eFc3B17444b832B31Ff4a'
name: 'Kilifi',
locations: ['kilfi', 'kilifi', 'mtwapa', 'takaungu', 'makongeni', 'mnarani', 'mnarani', 'office', 'g.e', 'ge', 'raibai', 'ribe']
},
{
name: 'Kakuma',
locations: ['kakuma']
},
{
name: 'Kitui',
locations: ['kitui', 'mwingi']
},
{
name: 'Nyanza',
locations: ['busia', 'nyalgunga', 'mbita', 'siaya', 'kisumu', 'nyalenda', 'hawinga', 'rangala', 'uyoma', 'mumias', 'homabay', 'homaboy',
'migori', 'kusumu']
},
{
name: 'Misc Rural Counties',
locations: ['makueni', 'meru', 'kisii', 'bomet', 'machakos', 'bungoma', 'eldoret', 'kakamega', 'kericho', 'kajiado', 'nandi', 'nyeri',
'wote', 'kiambu', 'mwea', 'nakuru', 'narok']
},
{
name: 'other',
locations: ['other', 'none', 'unknown']
}
];
/** A mock of curated area types. */
const areaTypes: Array<AreaType> = [
{
name: 'urban',
area: ['urban', 'nairobi', 'mombasa']
},
{
name: 'rural',
area: ['rural', 'kakuma', 'kwale', 'kinango', 'kitui', 'nyanza']
},
{
name: 'periurban',
area: ['kilifi', 'periurban']
},
{
name: 'other',
area: ['other']
}
];
/** A mock of the user's business categories */
const categories: Array<Category> = [
{
name: 'system',
@@ -147,96 +211,60 @@ const categories: Array<Category> = [
}
];
const areaNames: Array<AreaName> = [
{
name: 'Mukuru Nairobi',
locations: ['kayaba', 'kayba', 'kambi', 'mukuru', 'masai', 'hazina', 'south', 'tetra', 'tetrapak', 'ruben', 'rueben', 'kingston',
'korokocho', 'kingstone', 'kamongo', 'lungalunga', 'sinai', 'sigei', 'lungu', 'lunga lunga', 'owino road', 'seigei']
},
{
name: 'Kinango Kwale',
locations: ['amani', 'bofu', 'chibuga', 'chikomani', 'chilongoni', 'chigojoni', 'chinguluni', 'chigato', 'chigale', 'chikole',
'chilongoni', 'chilumani', 'chigojoni', 'chikomani', 'chizini', 'chikomeni', 'chidzuvini', 'chidzivuni', 'chikuyu', 'chizingo',
'doti', 'dzugwe', 'dzivani', 'dzovuni', 'hanje', 'kasemeni', 'katundani', 'kibandaogo', 'kibandaongo', 'kwale', 'kinango',
'kidzuvini', 'kalalani', 'kafuduni', 'kaloleni', 'kilibole', 'lutsangani', 'peku', 'gona', 'guro', 'gandini', 'mkanyeni', 'myenzeni',
'miyenzeni', 'miatsiani', 'mienzeni', 'mnyenzeni', 'minyenzeni', 'miyani', 'mioleni', 'makuluni', 'mariakani', 'makobeni', 'madewani',
'mwangaraba', 'mwashanga', 'miloeni', 'mabesheni', 'mazeras', 'mazera', 'mlola', 'muugano', 'mulunguni', 'mabesheni', 'miatsani',
'miatsiani', 'mwache', 'mwangani', 'mwehavikonje', 'miguneni', 'nzora', 'nzovuni', 'vikinduni', 'vikolani', 'vitangani', 'viogato',
'vyogato', 'vistangani', 'yapha', 'yava', 'yowani', 'ziwani', 'majengo', 'matuga', 'vigungani', 'vidziweni', 'vinyunduni', 'ukunda',
'kokotoni', 'mikindani']
},
{
name: 'Misc Nairobi',
locations: ['nairobi', 'west', 'lindi', 'kibera', 'kibira', 'kibra', 'makina', 'soweto', 'olympic', 'kangemi', 'ruiru', 'congo',
'kawangware', 'kwangware', 'donholm', 'dagoreti', 'dandora', 'kabete', 'sinai', 'donhom', 'donholm', 'huruma', 'kitengela',
'makadara', ',mlolongo', 'kenyatta', 'mlolongo', 'tassia', 'tasia', 'gatina', '56', 'industrial', 'kariobangi', 'kasarani', 'kayole',
'mathare', 'pipe', 'juja', 'uchumi', 'jogoo', 'umoja', 'thika', 'kikuyu', 'stadium', 'buru buru', 'ngong', 'starehe', 'mwiki',
'fuata', 'kware', 'kabiro', 'embakassi', 'embakasi', 'kmoja', 'east', 'githurai', 'landi', 'langata', 'limuru', 'mathere',
'dagoretti', 'kirembe', 'muugano', 'mwiki', 'toi market']
},
{
name: 'Misc Mombasa',
locations: ['mombasa', 'likoni', 'bangla', 'bangladesh', 'kizingo', 'old town', 'makupa', 'mvita', 'ngombeni', 'ngómbeni', 'ombeni',
'magongo', 'miritini', 'changamwe', 'jomvu', 'ohuru', 'tudor', 'diani']
},
{
name: 'Kisauni',
locations: ['bamburi', 'kisauni', 'mworoni', 'nyali', 'shanzu', 'bombolulu', 'mtopanga', 'mjambere', 'majaoni', 'manyani', 'magogoni',
'junda', 'mwakirunge', 'mshomoroni']
},
{
name: 'Kilifi',
locations: ['kilfi', 'kilifi', 'mtwapa', 'takaungu', 'makongeni', 'mnarani', 'mnarani', 'office', 'g.e', 'ge', 'raibai', 'ribe']
},
{
name: 'Kakuma',
locations: ['kakuma']
},
{
name: 'Kitui',
locations: ['kitui', 'mwingi']
},
{
name: 'Nyanza',
locations: ['busia', 'nyalgunga', 'mbita', 'siaya', 'kisumu', 'nyalenda', 'hawinga', 'rangala', 'uyoma', 'mumias', 'homabay', 'homaboy',
'migori', 'kusumu']
},
{
name: 'Misc Rural Counties',
locations: ['makueni', 'meru', 'kisii', 'bomet', 'machakos', 'bungoma', 'eldoret', 'kakamega', 'kericho', 'kajiado', 'nandi', 'nyeri',
'wote', 'kiambu', 'mwea', 'nakuru', 'narok']
},
{
name: 'other',
locations: ['other', 'none', 'unknown']
}
];
const areaTypes: Array<AreaType> = [
{
name: 'urban',
area: ['urban', 'nairobi', 'mombasa']
},
{
name: 'rural',
area: ['rural', 'kakuma', 'kwale', 'kinango', 'kitui', 'nyanza']
},
{
name: 'periurban',
area: ['kilifi', 'periurban']
},
{
name: 'other',
area: ['other']
}
];
const accountTypes: Array<string> = ['user', 'cashier', 'vendor', 'tokenagent', 'group'];
const transactionTypes: Array<string> = ['transactions', 'conversions', 'disbursements', 'rewards', 'reclamation'];
/** A mock of curated genders */
const genders: Array<string> = ['male', 'female', 'other'];
/** A mock of the tokens in the system. */
const tokens: Array<Token> = [
{
name: 'Giftable Reserve', symbol: 'GRZ', address: '0xa686005CE37Dce7738436256982C3903f2E4ea8E', supply: '1000000001000000000000000000',
decimals: '18', reserves: {}
},
{
name: 'Demo Token', symbol: 'DEMO', address: '0xc80D6aFF8194114c52AEcD84c9f15fd5c8abb187', supply: '99999999999999998976',
decimals: '18', reserves: {'0xa686005CE37Dce7738436256982C3903f2E4ea8E': {weight: '1000000', balance: '99999999999999998976'}},
reserveRatio: '1000000', owner: '0x3Da99AAD2D9CA01D131eFc3B17444b832B31Ff4a'
},
{
name: 'Foo Token', symbol: 'FOO', address: '0x9ceD86089f7aBB5A97B40eb0E7521e7aa308d354', supply: '1000000000000000001014',
decimals: '18', reserves: {'0xa686005CE37Dce7738436256982C3903f2E4ea8E': {weight: '1000000', balance: '1000000000000000001014'}},
reserveRatio: '1000000', owner: '0x3Da99AAD2D9CA01D131eFc3B17444b832B31Ff4a'
},
{
name: 'testb', symbol: 'tstb', address: '0xC63cFA91A3BFf41cE31Ff436f67D3ACBC977DB95', supply: '99000', decimals: '18',
reserves: {'0xa686005CE37Dce7738436256982C3903f2E4ea8E': {weight: '1000000', balance: '99000'}}, reserveRatio: '1000000',
owner: '0x3Da99AAD2D9CA01D131eFc3B17444b832B31Ff4a'
},
{
name: 'testa', symbol: 'tsta', address: '0x8fA4101ef19D0a078239d035659e92b278bD083C', supply: '9981', decimals: '18',
reserves: {'0xa686005CE37Dce7738436256982C3903f2E4ea8E': {weight: '1000000', balance: '9981'}}, reserveRatio: '1000000',
owner: '0x3Da99AAD2D9CA01D131eFc3B17444b832B31Ff4a'
},
{
name: 'testc', symbol: 'tstc', address: '0x4A6fA6bc3BfE4C9661bC692D9798425350C9e3D4', supply: '100990', decimals: '18',
reserves: {'0xa686005CE37Dce7738436256982C3903f2E4ea8E': {weight: '1000000', balance: '100990'}}, reserveRatio: '1000000',
owner: '0x3Da99AAD2D9CA01D131eFc3B17444b832B31Ff4a'
}
];
/** A mock of curated transaction types. */
const transactionTypes: Array<string> = ['transactions', 'conversions', 'disbursements', 'rewards', 'reclamation'];
/**
* Intercepts HTTP requests and handles some specified requests internally.
* Provides a backend that can handle requests for certain data items.
*
* @implements HttpInterceptor
*/
@Injectable()
export class MockBackendInterceptor implements HttpInterceptor {
/**
* Intercepts HTTP requests.
*
* @param request - An outgoing HTTP request with an optional typed body.
* @param next - The next HTTP handler or the outgoing request dispatcher.
* @returns The response from the resolved request.
*/
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const { url, method, headers, body } = request;
@@ -248,22 +276,17 @@ export class MockBackendInterceptor implements HttpInterceptor {
.pipe(delay(500))
.pipe(dematerialize());
/** Forward requests from select routes to their internal handlers. */
function handleRoute(): Observable<any> {
switch (true) {
case url.endsWith('/accounttypes') && method === 'GET':
return getAccountTypes();
case url.endsWith('/actions') && method === 'GET':
return getActions();
case url.match(/\/actions\/\d+$/) && method === 'GET':
return getActionById();
case url.match(/\/actions\/\d+$/) && method === 'POST':
return approveAction();
case url.endsWith('/tokens') && method === 'GET':
return getTokens();
case url.match(/\/tokens\/\w+$/) && method === 'GET':
return getTokenBySymbol();
case url.endsWith('/categories') && method === 'GET':
return getCategories();
case url.match(/\/categories\/\w+$/) && method === 'GET':
return getCategoryByProduct();
case url.endsWith('/areanames') && method === 'GET':
return getAreaNames();
case url.match(/\/areanames\/\w+$/) && method === 'GET':
@@ -272,12 +295,18 @@ export class MockBackendInterceptor implements HttpInterceptor {
return getAreaTypes();
case url.match(/\/areatypes\/\w+$/) && method === 'GET':
return getAreaTypeByArea();
case url.endsWith('/accounttypes') && method === 'GET':
return getAccountTypes();
case url.endsWith('/transactiontypes') && method === 'GET':
return getTransactionTypes();
case url.endsWith('/categories') && method === 'GET':
return getCategories();
case url.match(/\/categories\/\w+$/) && method === 'GET':
return getCategoryByProduct();
case url.endsWith('/genders') && method === 'GET':
return getGenders();
case url.endsWith('/tokens') && method === 'GET':
return getTokens();
case url.match(/\/tokens\/\w+$/) && method === 'GET':
return getTokenBySymbol();
case url.endsWith('/transactiontypes') && method === 'GET':
return getTransactionTypes();
default:
// pass through any requests not handled above
return next.handle(request);
@@ -286,15 +315,6 @@ export class MockBackendInterceptor implements HttpInterceptor {
// route functions
function getActions(): Observable<HttpResponse<any>> {
return ok(actions);
}
function getActionById(): Observable<HttpResponse<any>> {
const queriedAction: Action = actions.find(action => action.id === idFromUrl());
return ok(queriedAction);
}
function approveAction(): Observable<HttpResponse<any>> {
const queriedAction: Action = actions.find(action => action.id === idFromUrl());
queriedAction.approval = body.approval;
@@ -302,23 +322,17 @@ export class MockBackendInterceptor implements HttpInterceptor {
return ok(message);
}
function getTokens(): Observable<HttpResponse<any>> {
return ok(tokens);
function getAccountTypes(): Observable<HttpResponse<any>> {
return ok(accountTypes);
}
function getTokenBySymbol(): Observable<HttpResponse<any>> {
const queriedToken: Token = tokens.find(token => token.symbol === stringFromUrl());
return ok(queriedToken);
function getActions(): Observable<HttpResponse<any>> {
return ok(actions);
}
function getCategories(): Observable<HttpResponse<any>> {
const categoryList: Array<string> = categories.map(category => category.name);
return ok(categoryList);
}
function getCategoryByProduct(): Observable<HttpResponse<any>> {
const queriedCategory: Category = categories.find(category => category.products.includes(stringFromUrl()));
return ok(queriedCategory.name);
function getActionById(): Observable<HttpResponse<any>> {
const queriedAction: Action = actions.find(action => action.id === idFromUrl());
return ok(queriedAction);
}
function getAreaNames(): Observable<HttpResponse<any>> {
@@ -341,24 +355,35 @@ export class MockBackendInterceptor implements HttpInterceptor {
return ok(queriedAreaType.name);
}
function getAccountTypes(): Observable<HttpResponse<any>> {
return ok(accountTypes);
function getCategories(): Observable<HttpResponse<any>> {
const categoryList: Array<string> = categories.map(category => category.name);
return ok(categoryList);
}
function getTransactionTypes(): Observable<HttpResponse<any>> {
return ok(transactionTypes);
function getCategoryByProduct(): Observable<HttpResponse<any>> {
const queriedCategory: Category = categories.find(category => category.products.includes(stringFromUrl()));
return ok(queriedCategory.name);
}
function getGenders(): Observable<HttpResponse<any>> {
return ok(genders);
}
// helper functions
function ok(responseBody: any): Observable<HttpResponse<any>> {
return of(new HttpResponse({ status: 200, body: responseBody }));
function getTokens(): Observable<HttpResponse<any>> {
return ok(tokens);
}
function getTokenBySymbol(): Observable<HttpResponse<any>> {
const queriedToken: Token = tokens.find(token => token.symbol === stringFromUrl());
return ok(queriedToken);
}
function getTransactionTypes(): Observable<HttpResponse<any>> {
return ok(transactionTypes);
}
// helper functions
function error(message): Observable<any> {
return throwError({ status: 400, error: { message } });
}
@@ -368,6 +393,10 @@ export class MockBackendInterceptor implements HttpInterceptor {
return parseInt(urlParts[urlParts.length - 1], 10);
}
function ok(responseBody: any): Observable<HttpResponse<any>> {
return of(new HttpResponse({ status: 200, body: responseBody }));
}
function stringFromUrl(): string {
const urlParts: Array<string> = url.split('/');
return urlParts[urlParts.length - 1];
@@ -375,6 +404,11 @@ export class MockBackendInterceptor implements HttpInterceptor {
}
}
/**
* Exports the MockBackendInterceptor as an Angular provider.
*
* @exports
*/
export const MockBackendProvider = {
provide: HTTP_INTERCEPTORS,
useClass: MockBackendInterceptor,

View File

@@ -1,8 +1,15 @@
/** An object defining the properties of the data read. */
const objCsv: { size: number, dataFile: any } = {
size: 0,
dataFile: []
};
/**
* Reads a csv file and converts it to an array.
*
* @param input - The file to be read.
* @returns An array of the read data.
*/
function readCsv(input: any): Array<any> | void {
if (input.files && input.files[0]) {
const reader: FileReader = new FileReader();
@@ -15,6 +22,12 @@ function readCsv(input: any): Array<any> | void {
}
}
/**
* Parses data to CSV format.
*
* @param data - The data to be parsed.
* @returns An array of the parsed data.
*/
function parseData(data: any): Array<any> {
const csvData: Array<any> = [];
const lineBreak: Array<any> = data.split('\n');
@@ -25,6 +38,7 @@ function parseData(data: any): Array<any> {
return csvData;
}
/** @exports */
export {
readCsv
};

View File

@@ -1,5 +1,11 @@
// Third party imports
import { validatePerson, validateVcard } from 'cic-schemas-data-validator';
/**
* Validates a person object against the defined Person schema.
*
* @param person - A person object to be validated.
*/
async function personValidation(person: any): Promise<void> {
const personValidationErrors: any = await validatePerson(person);
@@ -8,6 +14,11 @@ async function personValidation(person: any): Promise<void> {
}
}
/**
* Validates a vcard object against the defined Vcard schema.
*
* @param vcard - A vcard object to be validated.
*/
async function vcardValidation(vcard: any): Promise<void> {
const vcardValidationErrors: any = await validateVcard(vcard);
@@ -16,6 +27,7 @@ async function vcardValidation(vcard: any): Promise<void> {
}
}
/** @exports */
export {
personValidation,
vcardValidation,