ohayolibs
Version:
Ohayo is a set of essential modules for ohayojp.
141 lines (123 loc) • 4.1 kB
text/typescript
import { Injectable, OnDestroy } from '@angular/core';
import { OhayoConfigService, OhayoMockConfig } from '@ohayo/util';
import { NzSafeAny } from 'ng-zorro-antd/core/types';
import { MockCachedRule, MockRule } from './interface';
import { MOCK_DEFULAT_CONFIG } from './mock.config';
({ providedIn: 'root' })
export class MockService implements OnDestroy {
private cached: MockCachedRule[] = [];
readonly config: OhayoMockConfig;
constructor(cogSrv: OhayoConfigService) {
this.config = cogSrv.merge('mock', MOCK_DEFULAT_CONFIG)!;
this.applyMock();
delete this.config.data;
}
// #region parse rule
private applyMock(): void {
this.cached = [];
try {
this.realApplyMock();
} catch (e) {
this.outputError(e);
}
}
private realApplyMock(): void {
const data = this.config.data;
if (!data) return;
Object.keys(data).forEach((key: string) => {
const rules = data[key];
if (!rules) return;
Object.keys(rules).forEach((ruleKey: string) => {
const value = rules[ruleKey];
if (!(typeof value === 'function' || typeof value === 'object' || typeof value === 'string')) {
throw Error(`mock value of [${key}-${ruleKey}] should be function or object or string, but got ${typeof value}`);
}
const rule = this.genRule(ruleKey, value);
if (['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'PATCH', 'OPTIONS'].indexOf(rule.method) === -1) {
throw Error(`method of ${key}-${ruleKey} is not valid`);
}
const item = this.cached.find(w => w.url === rule.url && w.method === rule.method);
if (item) {
item.callback = rule.callback;
} else {
this.cached.push(rule);
}
});
});
// regular ordering
this.cached.sort((a, b) => (b.martcher || '').toString().length - (a.martcher || '').toString().length);
}
private genRule(key: string, callback: any): MockCachedRule {
let method = 'GET';
let url = key;
if (key.indexOf(' ') > -1) {
const splited = key.split(' ');
method = splited[0].toLowerCase();
url = splited[1];
}
let martcher: RegExp | null = null;
let segments: string[] = [];
if (~url.indexOf(':')) {
segments = url!
.split('/')
.filter(segment => segment.startsWith(':'))
.map(v => v.substring(1));
const reStr = url!
.split('/')
.map(segment => (segment.startsWith(':') ? `([^/]+)` : segment))
.join('/');
martcher = new RegExp(`^${reStr}`, 'i');
} else if (/(\([^)]+\))/i.test(url)) {
martcher = new RegExp(url, 'i');
}
return {
url,
martcher,
segments,
callback,
method: method.toUpperCase(),
};
}
private outputError(error: NzSafeAny): void {
const filePath = error.message.split(': ')[0];
const errors = (error.stack as string)
.split('\n')
.filter(line => line.trim().indexOf('at ') !== 0)
.map(line => line.replace(`${filePath}: `, ''));
errors.splice(1, 0, '');
console.group();
console.warn(`==========Failed to parse mock config.==========`);
console.log(errors.join('\n'));
console.groupEnd();
throw error;
}
// #endregion
getRule(method: string, url: string): MockRule | null {
method = (method || 'GET').toUpperCase();
const params: NzSafeAny = {};
const list = this.cached.filter(w => w.method === method && (w.martcher ? w.martcher.test(url) : w.url === url));
if (list.length === 0) return null;
const ret = list.find(w => w.url === url) || list[0];
if (ret.martcher) {
const execArr = ret.martcher.exec(url);
execArr!.slice(1).map((value: string, index: number) => {
params[ret.segments[index]] = value;
});
}
return {
url,
method: ret.method,
params,
callback: ret.callback,
};
}
clearCache(): void {
this.cached = [];
}
get rules(): MockCachedRule[] {
return this.cached;
}
ngOnDestroy(): void {
this.clearCache();
}
}