-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Abstract from Observable in APIClient #48
Comments
An abstract |
import { HKT, Kind, Kind2, URIS, URIS2 } from 'fp-ts/lib/HKT';
import { MonadThrow, MonadThrow1, MonadThrow2 } from 'fp-ts/lib/MonadThrow';
export interface Request {
readonly method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';
readonly url: string;
readonly query?: object;
readonly body?: unknown;
}
export interface HTTPClient<F> extends MonadThrow<F> {
readonly request: (request: Request) => HKT<F, unknown>;
}
export interface HTTPClient1<F extends URIS> extends MonadThrow1<F> {
readonly request: (request: Request) => Kind<F, unknown>;
}
export interface HTTPClient2<F extends URIS2> extends MonadThrow2<F> {
readonly request: (request: Request) => Kind2<F, unknown, unknown>;
}
import { HKT, Kind, Kind2, URIS, URIS2 } from 'fp-ts/lib/HKT';
import { HTTPClient, HTTPClient1, HTTPClient2 } from './client';
import { pipe } from 'fp-ts/lib/pipeable';
import { number } from 'io-ts';
import { either } from 'fp-ts';
import { task } from 'fp-ts/lib/Task';
import { Observable, of } from 'rxjs';
import { failure, pending, RemoteData, success } from '@devexperts/remote-data-ts';
import { catchError, map, startWith } from 'rxjs/operators';
import { mapRD } from '@devexperts/rx-utils/dist/rd/operators/mapRD';
import { switchMapRD } from '@devexperts/rx-utils/dist/rd/operators/switchMapRD';
import { combineLatestRD } from '@devexperts/rx-utils/dist/rd/operators/combineLatestRD';
import { ajax } from 'rxjs/ajax';
interface Controller<F> {
readonly getTime: () => HKT<F, number>;
}
interface Controller1<F extends URIS> {
readonly getTime: () => Kind<F, number>;
}
interface Controller2<F extends URIS2> {
readonly getTime: () => Kind2<F, Error, number>;
}
interface Context<F> {
readonly httpClient: HTTPClient<F>;
}
interface Context1<F extends URIS> {
readonly httpClient: HTTPClient1<F>;
}
interface Context2<F extends URIS2> {
readonly httpClient: HTTPClient2<F>;
}
export function controller<F extends URIS2>(e: Context2<F>): Controller2<F>;
export function controller<F extends URIS>(e: Context1<F>): Controller1<F>;
export function controller<F>(e: Context<F>): Controller<F> {
return {
getTime: () =>
pipe(
e.httpClient.request({
url: '/time',
method: 'GET',
}),
r =>
e.httpClient.chain(r, a =>
pipe(
number.decode(a),
either.fold(err => e.httpClient.throwError(err), a => e.httpClient.of(a)),
),
),
),
};
}
const taskClient: HTTPClient1<'Task'> = {
...task,
request: request => () => fetch(request.url, { method: request.method }),
throwError: e => () => Promise.reject(e),
};
declare module 'fp-ts/lib/HKT' {
interface URItoKind2<E, A> {
LiveData: Observable<RemoteData<E, A>>;
}
}
const liveDataClient: HTTPClient2<'LiveData'> = {
URI: 'LiveData',
map: (fa, f) =>
pipe(
fa,
mapRD(f),
),
of: a => of(success(a)),
chain: (fa, f) =>
pipe(
fa,
switchMapRD(f),
),
ap: (fab, fa) =>
pipe(
combineLatestRD(fab, fa),
mapRD(([ab, a]) => ab(a)),
),
request: request =>
pipe(
ajax(request),
map(r => success(r.response)),
catchError(liveDataClient.throwError),
startWith(pending),
),
throwError: e => of(failure(e)),
};
//
const r1 = controller({ httpClient: taskClient }).getTime(); // Task<number>
r1()
.then()
.catch(); // all Promise methods available
const r2 = controller({ httpClient: liveDataClient }).getTime(); // LiveData<Error, number>
r2.pipe().subscribe(); //all LiveData methods available |
This is working solution (hooray) however this will break a lot. |
Wow, that's beautiful. I'm for it :) basically, this will break only the layer that is autogenerated right know, right? |
Currently an idiomatic way to work with generated controller is to add it to |
BREAKING CHANGE: apiClient dependency was renamed to httpClient BREAKING CHANGE: APIClient was renamed to HTTPClient<F> and now extends MonadThrow<F> BREAKING CHANGE: FullAPIRequest/APIRequest were joined and renamed to Request closes #48
BREAKING CHANGE: apiClient dependency was renamed to httpClient BREAKING CHANGE: APIClient was renamed to HTTPClient<F> and now extends MonadThrow<F> BREAKING CHANGE: FullAPIRequest/APIRequest were joined and renamed to Request closes #48
BREAKING CHANGE: apiClient dependency was renamed to httpClient BREAKING CHANGE: APIClient was renamed to HTTPClient<F> and now extends MonadThrow<F> BREAKING CHANGE: FullAPIRequest/APIRequest were joined and renamed to Request closes #48
Observable
out to some genericMonad
final tagless
The text was updated successfully, but these errors were encountered: