UNPKG

@ionic/cli

Version:

A tool for creating and developing Ionic Framework mobile apps.

197 lines (196 loc) 7.92 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.formatResponseError = exports.formatSuperAgentError = exports.createFatalAPIFormat = exports.transformAPIResponse = exports.ResourceClient = exports.TokenPaginator = exports.Paginator = exports.Client = exports.ERROR_UNKNOWN_RESPONSE_FORMAT = exports.ERROR_UNKNOWN_CONTENT_TYPE = void 0; const tslib_1 = require("tslib"); const chalk_1 = tslib_1.__importDefault(require("chalk")); const lodash = tslib_1.__importStar(require("lodash")); const util = tslib_1.__importStar(require("util")); const guards_1 = require("../guards"); const color_1 = require("./color"); const errors_1 = require("./errors"); const http_1 = require("./utils/http"); const FORMAT_ERROR_BODY_MAX_LENGTH = 1000; exports.ERROR_UNKNOWN_CONTENT_TYPE = 'UNKNOWN_CONTENT_TYPE'; exports.ERROR_UNKNOWN_RESPONSE_FORMAT = 'UNKNOWN_RESPONSE_FORMAT'; class Client { constructor(config) { this.config = config; } async make(method, path, contentType = "application/json" /* ContentType.JSON */) { const url = path.startsWith('http://') || path.startsWith('https://') ? path : `${this.config.getAPIUrl()}${path}`; const { req } = await (0, http_1.createRequest)(method, url, this.config.getHTTPConfig()); req .set('Content-Type', contentType) .set('Accept', "application/json" /* ContentType.JSON */); return { req }; } async do(req) { const res = await req; const r = transformAPIResponse(res); if ((0, guards_1.isAPIResponseError)(r)) { throw new errors_1.FatalException('API request was successful, but the response output format was that of an error.\n' + formatAPIResponse(req, r)); } return r; } paginate(args) { return new Paginator({ client: this, ...args }); } } exports.Client = Client; class Paginator { constructor({ client, reqgen, guard, state, max }) { const defaultState = { page: 1, done: false, loaded: 0 }; this.client = client; this.reqgen = reqgen; this.guard = guard; this.max = max; if (!state) { state = { page_size: 100, ...defaultState }; } this.state = lodash.assign({}, state, defaultState); } next() { if (this.state.done) { return { done: true }; // TODO: why can't I exclude value? } return { done: false, value: (async () => { const { req } = await this.reqgen(); req.query(lodash.pick(this.state, ['page', 'page_size'])); const res = await this.client.do(req); if (!this.guard(res)) { throw createFatalAPIFormat(req, res); } this.state.loaded += res.data.length; if (res.data.length === 0 || // no resources in this page, we're done (typeof this.max === 'number' && this.state.loaded >= this.max) || // met or exceeded maximum requested (typeof this.state.page_size === 'number' && res.data.length < this.state.page_size) // number of resources less than page size, so nothing on next page ) { this.state.done = true; } this.state.page++; return res; })(), }; } [Symbol.iterator]() { return this; } } exports.Paginator = Paginator; class TokenPaginator { constructor({ client, reqgen, guard, state, max }) { const defaultState = { done: false, loaded: 0 }; this.client = client; this.reqgen = reqgen; this.guard = guard; this.max = max; if (!state) { state = { ...defaultState }; } this.state = lodash.assign({}, state, defaultState); } next() { if (this.state.done) { return { done: true }; // TODO: why can't I exclude value? } return { done: false, value: (async () => { const { req } = await this.reqgen(); if (this.state.page_token) { req.query({ page_token: this.state.page_token }); } const res = await this.client.do(req); if (!this.isPageTokenResponseMeta(res.meta)) { throw createFatalAPIFormat(req, res); } const nextPageToken = res.meta.next_page_token; if (!this.guard(res)) { throw createFatalAPIFormat(req, res); } this.state.loaded += res.data.length; if (res.data.length === 0 || // no resources in this page, we're done (typeof this.max === 'number' && this.state.loaded >= this.max) || // met or exceeded maximum requested !nextPageToken // no next page token, must be done ) { this.state.done = true; } this.state.page_token = nextPageToken; return res; })(), }; } isPageTokenResponseMeta(meta) { return meta && (!meta.prev_page_token || typeof meta.prev_page_token === 'string') && (!meta.next_page_token || typeof meta.next_page_token === 'string'); } [Symbol.iterator]() { return this; } } exports.TokenPaginator = TokenPaginator; class ResourceClient { applyModifiers(req, modifiers) { if (!modifiers) { return; } if (modifiers.fields) { req.query({ fields: modifiers.fields }); } } applyAuthentication(req, token) { req.set('Authorization', `Bearer ${token}`); } } exports.ResourceClient = ResourceClient; function transformAPIResponse(r) { if (r.status === 204) { r.body = { meta: { status: 204, version: '', request_id: '' } }; } if (r.status !== 204 && r.type !== "application/json" /* ContentType.JSON */) { throw exports.ERROR_UNKNOWN_CONTENT_TYPE; } const j = r.body; if (!j.meta) { throw exports.ERROR_UNKNOWN_RESPONSE_FORMAT; } return j; } exports.transformAPIResponse = transformAPIResponse; function createFatalAPIFormat(req, res) { return new errors_1.FatalException('API request was successful, but the response format was unrecognized.\n' + formatAPIResponse(req, res)); } exports.createFatalAPIFormat = createFatalAPIFormat; function formatSuperAgentError(e) { const res = e.response; const req = res.request; // TODO: `req` and `request` exist: https://visionmedia.github.io/superagent/docs/test.html const statusCode = e.response.status; let f = ''; try { const r = transformAPIResponse(res); f += formatAPIResponse(req, r); } catch (e) { f += (`HTTP Error ${statusCode}: ${req.method.toUpperCase()} ${req.url}\n` + '\n' + (res.text ? res.text.substring(0, FORMAT_ERROR_BODY_MAX_LENGTH) : '<no buffered body>')); if (res.text && res.text.length > FORMAT_ERROR_BODY_MAX_LENGTH) { f += ` ...\n\n[ truncated ${res.text.length - FORMAT_ERROR_BODY_MAX_LENGTH} characters ]`; } } return (0, color_1.failure)((0, color_1.strong)(f)); } exports.formatSuperAgentError = formatSuperAgentError; function formatAPIResponse(req, r) { return formatResponseError(req, r.meta.status, (0, guards_1.isAPIResponseSuccess)(r) ? r.data : r.error); } function formatResponseError(req, status, body) { return (0, color_1.failure)(`Request: ${req.method} ${req.url}\n` + (status ? `Response: ${status}\n` : '') + (body ? `Body: \n${util.inspect(body, { colors: chalk_1.default.level > 0 })}` : '')); } exports.formatResponseError = formatResponseError;