import fetch, {RequestInit, Response} from 'node-fetch'; import * as path from 'path'; import {getActiveCredential, runtimeConfig} from './Config'; import {die} from './die'; export type Method = 'POST' | 'PUT' | 'PATCH' | 'GET' | 'DELETE'; /** * Low level http handler for interacting with the Rivendell api. */ export namespace RivendellApi { interface ApiResponse { response: Response; body: T; } export class ApiError extends Error { constructor(message: string, public responseText: string, public response?: Response) { super(message); } } export async function get(uri: string): Promise> { return request('GET', uri); } export async function post(uri: string, body: any): Promise> { return request('POST', uri, body); } export async function put(uri: string, body: any): Promise> { return request('PUT', uri, body); } export async function delete_(uri: string): Promise> { return request('DELETE', uri); } export async function request(method: Method, uri: string, body?: any): Promise> { const url = `${runtimeConfig().rivendell}/${uri}`; const requestPayload: RequestInit = { method }; requestPayload.headers = { 'x-api-key': loadApiKey(), 'x-cli-version': require(path.join(__dirname, '../oo-cli.manifest.json'))['package']['version'] }; if (body && method !== 'GET') { // @ts-ignore requestPayload.headers['content-type'] = 'application/json'; requestPayload.body = JSON.stringify(body); } const response = await fetch(url, requestPayload); const responseText = await response.text(); if (response.status < 200 || response.status >= 300) { const errorMsg = `Received a ${response.status} from OCP: ${getErrorMessage(response.status)}`; throw new ApiError(errorMsg, responseText, response); } return { response, body: responseText ? JSON.parse(responseText) : '' }; } function loadApiKey(): string { const apiKey = getActiveCredential().apiKey; if (apiKey == null) { die( 'Your API key is not configured in ~/.opti/credentials.json. ' + 'Please assign your API key to the property apiKey in the config file.' ); } return apiKey!; } function getErrorMessage(code: number): string { switch (code) { case 400: return 'Bad request.'; case 403: return 'Access denied.'; case 404: return 'Not found.'; case 500: return 'Internal service error.'; case 502: return 'Bad gateway'; case 503: return 'Service unavailable. Please try again.'; case 504: return 'Request timed out. Please try again.'; default: return 'Unhandled error.'; } } }