Athorcis/athorrent-frontend

View on GitHub
assets/js/core/router.ts

Summary

Maintainability
A
35 mins
Test Coverage
import queryString from 'query-string';
import { HttpClient, Request, newHttpClient } from 'typescript-http-client';

type RequestOptions = ConstructorParameters<typeof Request>[1];

export class Router {

    private queryParams;

    private http: HttpClient;

    constructor(private routes: Routes, private routeParameters: Params, private action: string) {
        this.queryParams = Router.parseQueryParameters();
        this.http = newHttpClient();
    }

    getHttpClient(): HttpClient {
        return this.http;
    }

    sendRequest<R>(name: string , parameters: Params = {}): AbortablePromise<R> {
        const route = this.getRoute(name);
        const request = this.createRequestFromRoute(route, { ...parameters });

        const body$ = this.http.executeForResponse<ApiResponse<R>>(request).then(response => {

            const {body} = response;

            if (body.status === 'success') {
                return body.data;
            }

            throw new Error(body.message);
        }) as AbortablePromise<R>;

        body$.abort = function () {
            request.abort();
        }

        return body$;
    }

    protected getQueryString(params: Params): string {
        if (Object.keys(params).length > 0) {
            return '?' + queryString.stringify(params, { arrayFormat: 'bracket' });
        }

        return '';
    }

    generateUrl(name: string, params: Params = {}): string {
        const route = this.getRoute(name);
        return this.prepareUrl(route, params) + this.getQueryString(params);
    }

    protected prepareUrl(route: Route, params: Params): string {
        return route.pattern.replace(/{(_?[A-Za-z]+)}/g, (match, name) => {
            let result;

            if (params.hasOwnProperty(name)) {
                result = params[name] as string;
                delete params[name];
            } else {
                result = match;
            }

            return result;
        });
    }

    protected createRequest(method: string, url: string, params: Params): Request {
        const options: RequestOptions = {
            method,
            headers: {
                'X-Requested-With': 'XMLHttpRequest'
            }
        };

        if (method === 'GET') {
            url += this.getQueryString(params);
        }
        else {
            options.body = queryString.stringify(params, { arrayFormat: 'bracket' });
            options.contentType = 'application/x-www-form-urlencoded';
        }

        return new Request(url, options);
    }

    protected createRequestFromRoute(route: Route, params: Params): Request {

        if (route.prefixId === this.routeParameters._prefixId) {
            if (route.name === this.action) {
                params = {...this.routeParameters, ...this.queryParams, ...params};
            }
             else {
                params = {...this.routeParameters, ...params};
            }
        }

        const url = this.prepareUrl(route, params);

        for (const key of Object.keys(params)) {
            if (key[0] === '_') {
                delete params[key];
            }
        }

        return this.createRequest(route.method, url, params);
    }

    protected getRoute(name: string): Route {

        let prefixId = this.routeParameters._prefixId as string;

        if (!this.routes.hasOwnProperty(name)) {
            throw new Error(`cannot find route with name: ${name}`);
        }

        const routeGroup = this.routes[name];

        if (!routeGroup.hasOwnProperty(prefixId)) {
            prefixId = Object.keys(routeGroup)[0];
        }

        return routeGroup[prefixId];
    }

    static parseQueryParameters(): Params {
        return queryString.parse(location.search) as Params;
    }
}