Skip to content

Commit

Permalink
feat: static type check about getOutput
Browse files Browse the repository at this point in the history
using generic to define type of getOutput result

BREAKING CHANGE: specify Tout. simplest way is that set it as `BareFlowOutNode`.
  • Loading branch information
ElonH committed May 17, 2020
1 parent c8a3f39 commit f0b8bc7
Show file tree
Hide file tree
Showing 10 changed files with 101 additions and 68 deletions.
13 changes: 8 additions & 5 deletions src/app/@dataflow/core/ajax-flow.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import { CacheFlow } from './cache-flow';
import { DataFlowNode, BareFlowInNode } from './bare-flow';
import { DataFlowNode, BareFlowInNode, BareFlowOutNode, CombErr } from './bare-flow';
import { Observable, of } from 'rxjs';
import { ajax, AjaxRequest, AjaxResponse } from 'rxjs/ajax';
import { catchError, map } from 'rxjs/operators';

export type AjaxFlowNode = [AjaxResponse | object, Error[]];

export abstract class AjaxFlow<Tin extends BareFlowInNode> extends CacheFlow<Tin> {
export abstract class AjaxFlow<
Tin extends BareFlowInNode,
Tout extends BareFlowOutNode
> extends CacheFlow<Tin, Tout> {
// protected cacheSupport: boolean;
// protected cachePath: string;

protected abstract requestAjax(pre: DataFlowNode): AjaxRequest;
protected abstract reconstructAjaxResult(x: AjaxFlowNode): DataFlowNode;
protected requestCache(pre: DataFlowNode): Observable<DataFlowNode> {
protected abstract requestAjax(pre: CombErr<Tin>): AjaxRequest;
protected abstract reconstructAjaxResult(x: AjaxFlowNode): CombErr<Tout>;
protected requestCache(pre: CombErr<Tin>): Observable<CombErr<Tout>> {
return ajax(this.requestAjax(pre)).pipe(
map((x) => [x, []] as AjaxFlowNode),
catchError((err): Observable<AjaxFlowNode> => of([{}, [err]])),
Expand Down
23 changes: 13 additions & 10 deletions src/app/@dataflow/core/bare-flow.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BareFlow, DataFlowNode, BareFlowInNode } from './bare-flow';
import { BareFlow, DataFlowNode, BareFlowInNode, BareFlowOutNode, CombErr } from './bare-flow';
import { TestScheduler } from 'rxjs/testing';
import { Observable, of } from 'rxjs';

Expand All @@ -19,7 +19,7 @@ describe('BareFlow', () => {
const pre = cold('a----', values);
const expected = 'a----';

const rst = new (class extends BareFlow<BareFlowInNode> {
const rst = new (class extends BareFlow<BareFlowInNode, BareFlowOutNode> {
public prerequest$ = pre;
protected request(pre: DataFlowNode): Observable<DataFlowNode> {
throw new Error('Method not implemented.');
Expand All @@ -44,7 +44,7 @@ describe('BareFlow', () => {
const pre = cold('a----', values);
const expected = 'b----';

const rst = new (class extends BareFlow<TestPreNode> {
const rst = new (class extends BareFlow<TestPreNode, TestPreNode> {
public prerequest$ = pre;
protected request(pre: DataFlowNode): Observable<DataFlowNode> {
expect(pre).toEqual(values.a);
Expand All @@ -58,8 +58,8 @@ describe('BareFlow', () => {
});
it('prerequest twice(same value), but got once only', () => {
scheduler.run((helpers) => {
const { cold, hot, expectObservable, expectSubscriptions, flush } = helpers;
interface TestPreNode {
const { cold, hot, expectObservable, expectSubscriptions, flush } = helpers;
interface TestPreNode {
a?: number;
b?: number;
}
Expand All @@ -70,7 +70,7 @@ describe('BareFlow', () => {
const pre = cold('a--a-', values);
const expected = 'b----';

const rst = new (class extends BareFlow<TestPreNode> {
const rst = new (class extends BareFlow<TestPreNode, TestPreNode> {
public prerequest$ = pre;
protected request(pre: DataFlowNode): Observable<DataFlowNode> {
return of(values.b);
Expand All @@ -83,8 +83,8 @@ describe('BareFlow', () => {
});
it('prerequest twice(different value), got twice', () => {
scheduler.run((helpers) => {
const { cold, hot, expectObservable, expectSubscriptions, flush } = helpers;
interface TestPreNode {
const { cold, hot, expectObservable, expectSubscriptions, flush } = helpers;
interface TestPreNode {
ab: number;
}
const values: { [id: string]: [TestPreNode, []] } = {
Expand All @@ -96,9 +96,12 @@ describe('BareFlow', () => {
const pre = cold('a--b-', values);
const expected = 'c--d-';

const rst = new (class extends BareFlow<TestPreNode> {
const rst = new (class extends BareFlow<TestPreNode, TestPreNode> {
// protected request(pre: import("./bare-flow").CombErr<TestPreNode>): Observable<import("./bare-flow").CombErr<TestPreNode>> {
// throw new Error("Method not implemented.");
// }
public prerequest$ = pre;
protected request(pre: DataFlowNode): Observable<DataFlowNode> {
protected request(pre: CombErr<TestPreNode>): Observable<CombErr<TestPreNode>> {
return of([{ ab: pre[0]['ab'] + 1 }, []]);
}
})();
Expand Down
20 changes: 11 additions & 9 deletions src/app/@dataflow/core/bare-flow.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,32 @@
import { Observable, of } from 'rxjs';
import { switchMap, take, tap, startWith, distinctUntilChanged, skipWhile } from 'rxjs/operators';

export interface BareFlowInNode {};
export type DataFlowNode = [BareFlowInNode, Error[]];
export interface BareFlowInNode {}
export interface BareFlowOutNode {}
export type DataFlowNode = [BareFlowInNode, Error[]]; // TODO: drop support
export type CombErr<T> = [T, Error[]];

export abstract class BareFlow<Tin extends BareFlowInNode> {
export abstract class BareFlow<Tin extends BareFlowInNode, Tout extends BareFlowOutNode> {
public abstract prerequest$: Observable<CombErr<Tin>>;
protected abstract request(pre: DataFlowNode): Observable<DataFlowNode>;
private bareData$: Observable<DataFlowNode>;
protected abstract request(pre: CombErr<Tin>): Observable<CombErr<Tout>>;
private bareData$: Observable<CombErr<Tout>>;
private deployed = false;
private boostrapData: DataFlowNode;
private boostrapData: CombErr<Tout>;
public deploy() {
this.bareData$ = this.prerequest$.pipe(
switchMap(
(pre): Observable<DataFlowNode> => {
(pre): Observable<CombErr<Tout>> => {
if (pre[1].length === 0) return this.request(pre).pipe(take(1));
return of(pre);
return of((pre as any) as CombErr<Tout>); // force to convert. There are some errors at privious flow.
// Just make sure that checking Error[] at first in subscription
}
),
tap((x) => (this.boostrapData = x))
);
this.bareData$.pipe(take(1)).subscribe();
this.deployed = true;
}
public getOutput(): Observable<DataFlowNode> {
public getOutput(): Observable<CombErr<Tout>> {
if (!this.deployed) throw new Error('run deploy before getOutput');
return this.bareData$.pipe(
startWith(this.boostrapData),
Expand Down
53 changes: 37 additions & 16 deletions src/app/@dataflow/core/cache-flow.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { CacheFlow } from './cache-flow';
import { TestScheduler } from 'rxjs/testing';
import { DataFlowNode, BareFlowInNode } from './bare-flow';
import { DataFlowNode, BareFlowInNode, CombErr, BareFlowOutNode } from './bare-flow';
import { Observable, of } from 'rxjs';

describe('CacheFlow', () => {
Expand All @@ -16,22 +16,29 @@ describe('CacheFlow', () => {
});
it('prerequest twice(different value), request same value, only output onece', () => {
scheduler.run((helpers) => {
const { cold, hot, expectObservable, expectSubscriptions, flush } = helpers;
const values: { [id: string]: DataFlowNode } = {
const { cold, hot, expectObservable, expectSubscriptions, flush } = helpers;
interface TestIn extends BareFlowInNode {
ab: number;
}
interface TestOut extends BareFlowOutNode {
k: number;
}
interface TestSup extends TestOut, TestIn {}
const values: { [id: string]: CombErr<TestIn | TestOut | TestSup> } = {
a: [{ ab: 555 }, []],
b: [{ ab: 123 }, []],
c: [{ ab: 555, k: 1 }, []],
k: [{ k: 1 }, []],
};
const pre = cold(' a--b-', values);
const pre = cold(' a--b-', values) as Observable<CombErr<TestIn>>;
const expectedOutput = 'k----';
const expectedSupers = 'c----';

const rst = new (class extends CacheFlow<BareFlowInNode> {
const rst = new (class extends CacheFlow<TestIn, TestOut> {
protected cacheSupport: boolean = true;
protected cachePath: string = 'foo';
public prerequest$ = pre;
protected requestCache(pre: DataFlowNode): Observable<DataFlowNode> {
protected requestCache(pre: CombErr<TestIn>): Observable<CombErr<TestOut>> {
return of([{ k: 1 }, []]);
}
})();
Expand All @@ -44,23 +51,30 @@ describe('CacheFlow', () => {
it('check cache validation', () => {
scheduler.run((helpers) => {
const { cold, hot, expectObservable, expectSubscriptions, flush } = helpers;
const values: { [id: string]: DataFlowNode } = {
interface TestIn extends BareFlowInNode {
ab: number;
}
interface TestOut extends BareFlowOutNode {
k: number;
}
interface TestSup extends TestOut, TestIn {}
const values: { [id: string]: CombErr<TestIn | TestOut | TestSup> } = {
a: [{ ab: 555 }, []],
b: [{ ab: 123 }, []],
c: [{ ab: 555, k: 555 }, []],
d: [{ ab: 123, k: 555 }, []],
k: [{ k: 555 }, []],
};
const pre = cold(' a--b-', values);
const pre2 = cold(' b--a-', values);
const pre = cold(' a--b-', values) as Observable<CombErr<TestIn>>;
const pre2 = cold(' b--a-', values) as Observable<CombErr<TestIn>>;
const expectedOutput = 'k----';
const expectedSupers = 'c----';
const expectedSuper2 = 'd----';
class TestCache extends CacheFlow<BareFlowInNode> {
class TestCache extends CacheFlow<TestIn, TestOut> {
protected cacheSupport: boolean = true;
protected cachePath: string = 'foo';
public prerequest$ = null;
protected requestCache(pre: DataFlowNode): Observable<DataFlowNode> {
protected requestCache(pre: CombErr<TestIn>): Observable<CombErr<TestOut>> {
return of([{ k: pre[0]['ab'] }, []]);
}
constructor(pre: Observable<DataFlowNode>) {
Expand All @@ -83,24 +97,31 @@ describe('CacheFlow', () => {
it('disable cache', () => {
scheduler.run((helpers) => {
const { cold, hot, expectObservable, expectSubscriptions, flush } = helpers;
const values: { [id: string]: DataFlowNode } = {
interface TestIn extends BareFlowInNode {
ab: number;
}
interface TestOut extends BareFlowOutNode {
cd: number;
}
interface TestSup extends TestOut, TestIn {}
const values: { [id: string]: CombErr<TestIn | TestOut | TestSup> } = {
a: [{ ab: 555 }, []],
b: [{ ab: 123 }, []],
c: [{ cd: 556 }, []],
d: [{ cd: 124 }, []],
e: [{ ab: 555, cd: 556 }, []],
f: [{ ab: 123, cd: 124 }, []],
};
const pre = cold(' a--b-', values);
const pre = cold(' a--b-', values) as Observable<CombErr<TestIn>>;
const expectedOutput = 'c--d-';
const expectedSupers = 'e--f-';

const rst = new (class extends CacheFlow<BareFlowInNode> {
const rst = new (class extends CacheFlow<TestIn, TestOut> {
protected cacheSupport: boolean = false;
protected cachePath: string = 'foo';
public prerequest$ = pre;
protected requestCache(pre: DataFlowNode): Observable<DataFlowNode> {
return of([{ cd: pre[0]['ab'] + 1 }, []]);;
protected requestCache(pre: CombErr<TestIn>): Observable<CombErr<TestOut>> {
return of([{ cd: pre[0]['ab'] + 1 }, []]);
}
})();
rst.deploy();
Expand Down
17 changes: 10 additions & 7 deletions src/app/@dataflow/core/cache-flow.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { DataFlowNode, BareFlowInNode } from './bare-flow';
import { DataFlowNode, BareFlowInNode, BareFlowOutNode, CombErr } from './bare-flow';
import { Observable, iif, of } from 'rxjs';
import { SupersetFlow } from './superset-flow';
import { tap, take, map } from 'rxjs/operators';

export abstract class CacheFlow<Tin extends BareFlowInNode> extends SupersetFlow<Tin> {
protected abstract requestCache(pre: DataFlowNode): Observable<DataFlowNode>;
protected request(pre: DataFlowNode): Observable<DataFlowNode> {
export abstract class CacheFlow<
Tin extends BareFlowInNode,
Tout extends BareFlowOutNode
> extends SupersetFlow<Tin, Tout> {
protected abstract requestCache(pre: CombErr<Tin>): Observable<CombErr<Tout>>;
protected request(pre: CombErr<Tin>): Observable<CombErr<Tout>> {
return iif(
() => this.cacheEnabled() && this.isCached(),
of(this.getCache()),
Expand All @@ -28,10 +31,10 @@ export abstract class CacheFlow<Tin extends BareFlowInNode> extends SupersetFlow
private isCached() {
return CacheFlow.cacheStorage.hasOwnProperty(this.cachePath);
}
private getCache(): DataFlowNode {
return CacheFlow.cacheStorage[this.cachePath];
private getCache(): CombErr<Tout> {
return CacheFlow.cacheStorage[this.cachePath] as CombErr<Tout>;
}
private setCache(x: DataFlowNode) {
private setCache(x: CombErr<Tout>) {
CacheFlow.cacheStorage[this.cachePath] = x;
}
public clearCache() {
Expand Down
4 changes: 2 additions & 2 deletions src/app/@dataflow/core/superset-flow.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { SupersetFlow } from './superset-flow';
import { TestScheduler } from 'rxjs/testing';
import { DataFlowNode, BareFlowInNode } from './bare-flow';
import { DataFlowNode, BareFlowInNode, BareFlowOutNode } from './bare-flow';
import { Observable, of } from 'rxjs';

describe('SupersetFlow', () => {
Expand All @@ -23,7 +23,7 @@ describe('SupersetFlow', () => {
const pre = cold('a--b-', values);
const expected = 'c--d-';

const rst = new (class extends SupersetFlow<BareFlowInNode> {
const rst = new (class extends SupersetFlow<BareFlowInNode, BareFlowOutNode> {
public prerequest$ = pre;
protected request(pre: DataFlowNode): Observable<DataFlowNode> {
return of([{ cd: pre[0]['ab'] + 1 }, []]);
Expand Down
7 changes: 5 additions & 2 deletions src/app/@dataflow/core/superset-flow.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BareFlow, DataFlowNode, BareFlowInNode } from './bare-flow';
import { BareFlow, DataFlowNode, BareFlowInNode, BareFlowOutNode } from './bare-flow';
import { Observable } from 'rxjs';
import {
tap,
Expand All @@ -10,7 +10,10 @@ import {
map,
} from 'rxjs/operators';

export abstract class SupersetFlow<Tin extends BareFlowInNode> extends BareFlow<Tin> {
export abstract class SupersetFlow<
Tin extends BareFlowInNode,
Tout extends BareFlowOutNode
> extends BareFlow<Tin, Tout> {
private boostrapPrerequest$: Observable<DataFlowNode>;
private boostrapPrerequest: DataFlowNode;
public deploy() {
Expand Down
4 changes: 2 additions & 2 deletions src/app/@dataflow/extra/name-validation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ describe('NameValidation', () => {
const { cold, hot, expectObservable, expectSubscriptions, flush } = helpers;
const values: { [id: string]: CombErr<NameValidationPreNode | NameValidationNode> } = {
a: [{ users: [], currentName: '' }, []],
b: [{}, [new Error('You must enter a value')]],
b: [{}, [new Error('You must enter a value')]]as any,
};
const pre = cold('a----', values) as Observable<CombErr<NameValidationPreNode>>;
const expected = 'b----';
Expand All @@ -52,7 +52,7 @@ describe('NameValidation', () => {
const { cold, hot, expectObservable, expectSubscriptions, flush } = helpers;
const values: { [id: string]: CombErr<NameValidationPreNode | NameValidationNode> } = {
a: [{ users: [{ name: '123', url: '' }], currentName: '123' }, []],
b: [{}, [new Error('This name already exists')]],
b: [{}, [new Error('This name already exists')]] as any,
};
const pre = cold('a----', values) as Observable<CombErr<NameValidationPreNode>>;
const expected = 'b----';
Expand Down
12 changes: 6 additions & 6 deletions src/app/@dataflow/extra/name-validation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SupersetFlow, DataFlowNode, BareFlowInNode } from '../core';
import { SupersetFlow, DataFlowNode, BareFlowInNode, CombErr } from '../core';
import { Observable, of } from 'rxjs';
import { IUser, UsersFlowNode } from './users-flow';

Expand All @@ -7,19 +7,19 @@ export interface NameValidationPreNode extends UsersFlowNode {
}

export interface NameValidationNode extends BareFlowInNode {
nameValid?: boolean;
nameValid: boolean;
}

export abstract class NameValidation extends SupersetFlow<NameValidationPreNode> {
protected request(pre: DataFlowNode): Observable<DataFlowNode> {
export abstract class NameValidation extends SupersetFlow<NameValidationPreNode, NameValidationNode> {
protected request(pre: DataFlowNode): Observable<CombErr<NameValidationNode>> {
const curName = pre[0]['currentName'];
if (curName === '') return of([{}, [new Error('You must enter a value')]]);
if (curName === '') return of([{}, [new Error('You must enter a value')]] as any);
const usrs = pre[0]['users'] as IUser[];
let nameExist = false;
usrs.forEach((x) => {
if (x.name === curName) nameExist = true;
});
if (nameExist) return of([{}, [new Error('This name already exists')]]);
if (nameExist) return of([{}, [new Error('This name already exists')]] as any);
return of([{ nameValid: true }, []]);
}
}

0 comments on commit f0b8bc7

Please sign in to comment.