Commit 6894e538 by Vladislav Lagunov

Добавлен алиасы модулей eff http

parent 8cc3cb4a
import * as Rx from 'rxjs'; export * from '../http';
import { Eff } from './eff';
import * as eff from './eff';
import { Decoder, Problem } from '../decoder';
import { success, failure } from '../either';
/** http method */
export type Method = 'GET'|'POST'|'PUT'|'DELETE'|'PATCH';
/** request */
export interface Request {
url: string;
method: Method;
body?: any;
headers?: Record<string, string|number|undefined|null>;
withCredentials?: boolean;
timeout?: number;
}
/** raw error */
export type HttpError =
| { tag: 'BadUrl', desc: string }
| { tag: 'BadPayload', desc: string }
| { tag: 'ValidationProblem', problem: Problem, url: string }
| { tag: 'BadStatus', status: number, desc: string }
| { tag: 'Timeout' }
| { tag: 'NetworkError' }
/** responce */
export interface Response {
url: string;
status: number;
statusText: string;
headers: Record<string, string>;
body: string;
}
/** query params */
export type ParamsPrimitive = number|string|undefined|null;
export type Params = Record<string, ParamsPrimitive|ParamsPrimitive[]>;
/** progress */
export type Progress =
| { tag: 'Computable', total: number, loaded: number }
| { tag: 'Uncomputable' }
/** send a request */
export function send(req: Request): Eff<HttpError, Response> {
return Rx.Observable.create(observer => {
const xhr = new XMLHttpRequest();
xhr.addEventListener('error', () => (observer.next(failure({ tag: 'NetworkError' } as HttpError)), observer.complete()));
xhr.addEventListener('timeout', () => (observer.next(failure({ tag: 'Timeout' } as HttpError)), observer.complete()));
xhr.addEventListener('load', () => {
observer.next(success({
url: xhr.responseURL,
status: xhr.status,
statusText: xhr.statusText,
headers: parseHeaders(xhr.getAllResponseHeaders()),
body: xhr.response || xhr.responseText,
}));
observer.complete();
});
try {
xhr.open(req.method, req.url, true);
} catch (e) {
observer.next(failure({ tag: 'BadUrl', desc: req.url } as HttpError)); observer.complete();
}
//xhr.addEventListener('progress', e => onResult(success(e.lengthComputable ? { tag: 'Computable', loaded: e.loaded, total: e.total } : { tag: 'Uncomputable' })));
if (req.timeout) xhr.timeout = req.timeout;
if (typeof (req.withCredentials) !== 'undefined') xhr.withCredentials = req.withCredentials;
if (typeof (req.headers) !== 'undefined') {
for (let key in req.headers) {
if (!req.headers.hasOwnProperty(key)) continue;
const value = req.headers[key];
if (typeof(value) !== 'undefined' && value !== null)
xhr.setRequestHeader(key, String(value));
}
}
const body = Object.prototype.toString.apply(req.body) === '[object Object]' ? JSON.stringify(req.body) : req.body;
xhr.send(body);
return () => xhr.abort();
});
}
/** shortcut for GET requests */
export function get(url: string, extra?: Partial<Request>): Eff<HttpError, Response> {
return send(Object.assign({}, extra, { method: 'GET', url }) as any);
}
/** shortcut for POST requests */
export function post(url: string, extra?: Partial<Request>): Eff<HttpError, Response> {
return send(Object.assign({}, extra, { method: 'POST', url }) as any);
}
/** parse response as JSON */
export function expectJSON<A>(decoder: Decoder<A>): (resp: Response) => Eff<HttpError, A> {
return resp => {
if (resp.body === '') return eff.failure({ tag: 'BadPayload', desc: 'empty body' } as HttpError);
let val = null;
try {
val = JSON.parse(resp.body);
} catch (e) {
return eff.failure({ tag: 'BadPayload', desc: 'invalid json' } as HttpError);
}
return Rx.of(decoder.validate(val).mapLeft(problem => ({ tag: 'ValidationProblem', problem, url: resp.url } as HttpError)));
};
}
/** parse headers from string to dict */
function parseHeaders(rawHeaders: string): Record<string, string> {
const output = {};
const lines = rawHeaders.split('\r\n');
for (let i in lines) {
const index = lines[i].indexOf(': ');
if (index < 0) continue;
const key = lines[i].substring(0, index);
const value = lines[i].substring(index + 2);
output[key] = value;
}
return output;
}
/** join segments of url */
function joinTwo(a: string, b: string): string {
if (a === '') return b;
if (b === '') return a;
const trailing = a.length && a[a.length - 1] === '/';
const leading = b.length && b[0] === '/';
if (trailing && leading) return a.substring(0, a.length - 1) + b;
if (!trailing && !leading) return a + '/' + b;
return a + b;
}
/** build an url */
export function join(...args: Array<string|Params>): string {
let path = '';
let params = {} as Record<string, string>;
let query = '';
for (let i in args) {
const arg = args[i];
if (typeof (arg) === 'string') path = joinTwo(path, arg);
else Object['assign'](params, arg);
}
for (let key in params) {
if (!params.hasOwnProperty(key) || typeof(params[key]) === 'undefined' || params[key] === null) continue;
if (Array.isArray(params[key])) {
for (const v of params[key]) {
if (typeof(params[key]) === 'undefined' || params[key] === null) continue;
query += (query ? '&' : '') + `${encodeURIComponent(key)}=${encodeURIComponent(v)}`;
}
} else {
query += (query ? '&' : '') + `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`;
}
}
return query ? (path + '?' + query) : path;
}
This diff is collapsed. Click to expand it.
import * as Rx from 'rxjs';
import { Eff } from '../eff';
import * as eff from '../eff';
import { Decoder, Problem } from '../decoder';
import { success, failure } from '../either';
/** http method */
export type Method = 'GET'|'POST'|'PUT'|'DELETE'|'PATCH';
/** request */
export interface Request {
url: string;
method: Method;
body?: any;
headers?: Record<string, string|number|undefined|null>;
withCredentials?: boolean;
timeout?: number;
}
/** raw error */
export type HttpError =
| { tag: 'BadUrl', desc: string }
| { tag: 'BadPayload', desc: string }
| { tag: 'ValidationProblem', problem: Problem, url: string }
| { tag: 'BadStatus', status: number, desc: string }
| { tag: 'Timeout' }
| { tag: 'NetworkError' }
/** responce */
export interface Response {
url: string;
status: number;
statusText: string;
headers: Record<string, string>;
body: string;
}
/** query params */
export type ParamsPrimitive = number|string|undefined|null;
export type Params = Record<string, ParamsPrimitive|ParamsPrimitive[]>;
/** progress */
export type Progress =
| { tag: 'Computable', total: number, loaded: number }
| { tag: 'Uncomputable' }
/** send a request */
export function send(req: Request): Eff<HttpError, Response> {
return Rx.Observable.create(observer => {
const xhr = new XMLHttpRequest();
xhr.addEventListener('error', () => (observer.next(failure({ tag: 'NetworkError' } as HttpError)), observer.complete()));
xhr.addEventListener('timeout', () => (observer.next(failure({ tag: 'Timeout' } as HttpError)), observer.complete()));
xhr.addEventListener('load', () => {
observer.next(success({
url: xhr.responseURL,
status: xhr.status,
statusText: xhr.statusText,
headers: parseHeaders(xhr.getAllResponseHeaders()),
body: xhr.response || xhr.responseText,
}));
observer.complete();
});
try {
xhr.open(req.method, req.url, true);
} catch (e) {
observer.next(failure({ tag: 'BadUrl', desc: req.url } as HttpError)); observer.complete();
}
//xhr.addEventListener('progress', e => onResult(success(e.lengthComputable ? { tag: 'Computable', loaded: e.loaded, total: e.total } : { tag: 'Uncomputable' })));
if (req.timeout) xhr.timeout = req.timeout;
if (typeof (req.withCredentials) !== 'undefined') xhr.withCredentials = req.withCredentials;
if (typeof (req.headers) !== 'undefined') {
for (let key in req.headers) {
if (!req.headers.hasOwnProperty(key)) continue;
const value = req.headers[key];
if (typeof(value) !== 'undefined' && value !== null)
xhr.setRequestHeader(key, String(value));
}
}
const body = Object.prototype.toString.apply(req.body) === '[object Object]' ? JSON.stringify(req.body) : req.body;
xhr.send(body);
return () => xhr.abort();
});
}
/** shortcut for GET requests */
export function get(url: string, extra?: Partial<Request>): Eff<HttpError, Response> {
return send(Object.assign({}, extra, { method: 'GET', url }) as any);
}
/** shortcut for POST requests */
export function post(url: string, extra?: Partial<Request>): Eff<HttpError, Response> {
return send(Object.assign({}, extra, { method: 'POST', url }) as any);
}
/** parse response as JSON */
export function expectJSON<A>(decoder: Decoder<A>): (resp: Response) => Eff<HttpError, A> {
return resp => {
if (resp.body === '') return eff.failure({ tag: 'BadPayload', desc: 'empty body' } as HttpError);
let val = null;
try {
val = JSON.parse(resp.body);
} catch (e) {
return eff.failure({ tag: 'BadPayload', desc: 'invalid json' } as HttpError);
}
return Rx.of(decoder.validate(val).mapLeft(problem => ({ tag: 'ValidationProblem', problem, url: resp.url } as HttpError)));
};
}
/** parse headers from string to dict */
function parseHeaders(rawHeaders: string): Record<string, string> {
const output = {};
const lines = rawHeaders.split('\r\n');
for (let i in lines) {
const index = lines[i].indexOf(': ');
if (index < 0) continue;
const key = lines[i].substring(0, index);
const value = lines[i].substring(index + 2);
output[key] = value;
}
return output;
}
/** join segments of url */
function joinTwo(a: string, b: string): string {
if (a === '') return b;
if (b === '') return a;
const trailing = a.length && a[a.length - 1] === '/';
const leading = b.length && b[0] === '/';
if (trailing && leading) return a.substring(0, a.length - 1) + b;
if (!trailing && !leading) return a + '/' + b;
return a + b;
}
/** build an url */
export function join(...args: Array<string|Params>): string {
let path = '';
let params = {} as Record<string, string>;
let query = '';
for (let i in args) {
const arg = args[i];
if (typeof (arg) === 'string') path = joinTwo(path, arg);
else Object['assign'](params, arg);
}
for (let key in params) {
if (!params.hasOwnProperty(key) || typeof(params[key]) === 'undefined' || params[key] === null) continue;
if (Array.isArray(params[key])) {
for (const v of params[key]) {
if (typeof(params[key]) === 'undefined' || params[key] === null) continue;
query += (query ? '&' : '') + `${encodeURIComponent(key)}=${encodeURIComponent(v)}`;
}
} else {
query += (query ? '&' : '') + `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`;
}
}
return query ? (path + '?' + query) : path;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment