Path: blob/main/src/publish/netlify/api/core/request.ts
6464 views
// deno-lint-ignore-file1/* istanbul ignore file */2/* tslint:disable */3/* eslint-disable */4import { ApiError } from "./ApiError.ts";5import type { ApiRequestOptions } from "./ApiRequestOptions.ts";6import type { ApiResult } from "./ApiResult.ts";7import { CancelablePromise } from "./CancelablePromise.ts";8import type { OnCancel } from "./CancelablePromise.ts";9import type { OpenAPIConfig } from "./OpenAPI.ts";1011const isDefined = <T>(12value: T | null | undefined,13): value is Exclude<T, null | undefined> => {14return value !== undefined && value !== null;15};1617const isString = (value: any): value is string => {18return typeof value === "string";19};2021const isStringWithValue = (value: any): value is string => {22return isString(value) && value !== "";23};2425const isBlob = (value: any): value is Blob => {26return (27typeof value === "object" &&28typeof value.type === "string" &&29typeof value.stream === "function" &&30typeof value.arrayBuffer === "function" &&31typeof value.constructor === "function" &&32typeof value.constructor.name === "string" &&33/^(Blob|File)$/.test(value.constructor.name) &&34/^(Blob|File)$/.test(value[Symbol.toStringTag])35);36};3738const isFormData = (value: any): value is FormData => {39return value instanceof FormData;40};4142const base64 = (str: string): string => {43try {44return btoa(str);45} catch (err) {46// @ts-ignore47return Buffer.from(str).toString("base64");48}49};5051const getQueryString = (params: Record<string, any>): string => {52const qs: string[] = [];5354const append = (key: string, value: any) => {55qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);56};5758const process = (key: string, value: any) => {59if (isDefined(value)) {60if (Array.isArray(value)) {61value.forEach((v) => {62process(key, v);63});64} else if (typeof value === "object") {65Object.entries(value).forEach(([k, v]) => {66process(`${key}[${k}]`, v);67});68} else {69append(key, value);70}71}72};7374Object.entries(params).forEach(([key, value]) => {75process(key, value);76});7778if (qs.length > 0) {79return `?${qs.join("&")}`;80}8182return "";83};8485const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => {86const encoder = config.ENCODE_PATH || encodeURI;8788const path = options.url89.replace("{api-version}", config.VERSION)90.replace(/{(.*?)}/g, (substring: string, group: string) => {91if (options.path?.hasOwnProperty(group)) {92return encoder(String(options.path[group]));93}94return substring;95});9697const url = `${config.BASE}${path}`;98if (options.query) {99return `${url}${getQueryString(options.query)}`;100}101return url;102};103104const getFormData = (options: ApiRequestOptions): FormData | undefined => {105if (options.formData) {106const formData = new FormData();107108const process = (key: string, value: any) => {109if (isString(value) || isBlob(value)) {110formData.append(key, value);111} else {112formData.append(key, JSON.stringify(value));113}114};115116Object.entries(options.formData)117.filter(([_, value]) => isDefined(value))118.forEach(([key, value]) => {119if (Array.isArray(value)) {120value.forEach((v) => process(key, v));121} else {122process(key, value);123}124});125126return formData;127}128return undefined;129};130131type Resolver<T> = (options: ApiRequestOptions) => Promise<T>;132133const resolve = async <T>(134options: ApiRequestOptions,135resolver?: T | Resolver<T>,136): Promise<T | undefined> => {137if (typeof resolver === "function") {138return (resolver as Resolver<T>)(options);139}140return resolver;141};142143const getHeaders = async (144config: OpenAPIConfig,145options: ApiRequestOptions,146): Promise<Headers> => {147const token = await resolve(options, config.TOKEN);148const username = await resolve(options, config.USERNAME);149const password = await resolve(options, config.PASSWORD);150const additionalHeaders = await resolve(options, config.HEADERS);151152const headers = Object.entries({153Accept: "application/json",154...additionalHeaders,155...options.headers,156})157.filter(([_, value]) => isDefined(value))158.reduce((headers, [key, value]) => ({159...headers,160[key]: String(value),161}), {} as Record<string, string>);162163if (isStringWithValue(token)) {164headers["Authorization"] = `Bearer ${token}`;165}166167if (isStringWithValue(username) && isStringWithValue(password)) {168const credentials = base64(`${username}:${password}`);169headers["Authorization"] = `Basic ${credentials}`;170}171172if (options.body) {173if (options.mediaType) {174headers["Content-Type"] = options.mediaType;175} else if (isBlob(options.body)) {176headers["Content-Type"] = options.body.type || "application/octet-stream";177} else if (isString(options.body)) {178headers["Content-Type"] = "text/plain";179} else if (!isFormData(options.body)) {180headers["Content-Type"] = "application/json";181}182}183184return new Headers(headers);185};186187const getRequestBody = (options: ApiRequestOptions): any => {188if (options.body) {189if (options.mediaType?.includes("/json")) {190return JSON.stringify(options.body);191} else if (192isString(options.body) || isBlob(options.body) || isFormData(options.body)193) {194return options.body;195} else {196return JSON.stringify(options.body);197}198}199return undefined;200};201202export const sendRequest = async (203config: OpenAPIConfig,204options: ApiRequestOptions,205url: string,206body: any,207formData: FormData | undefined,208headers: Headers,209onCancel: OnCancel,210): Promise<Response> => {211const controller = new AbortController();212213const request: RequestInit = {214headers,215body: body ?? formData,216method: options.method,217signal: controller.signal,218};219220if (config.WITH_CREDENTIALS) {221request.credentials = config.CREDENTIALS;222}223224onCancel(() => controller.abort());225226return await fetch(url, request);227};228229const getResponseHeader = (230response: Response,231responseHeader?: string,232): string | undefined => {233if (responseHeader) {234const content = response.headers.get(responseHeader);235if (isString(content)) {236return content;237}238}239return undefined;240};241242const getResponseBody = async (response: Response): Promise<any> => {243if (response.status !== 204) {244try {245const contentType = response.headers.get("Content-Type");246if (contentType) {247const isJSON = contentType.toLowerCase().startsWith("application/json");248if (isJSON) {249return await response.json();250} else {251return await response.text();252}253}254} catch (error) {255console.error(error);256}257}258return undefined;259};260261const catchErrorCodes = (262options: ApiRequestOptions,263result: ApiResult,264): void => {265const errors: Record<number, string> = {266400: "Bad Request",267401: "Unauthorized",268403: "Forbidden",269404: "Not Found",270500: "Internal Server Error",271502: "Bad Gateway",272503: "Service Unavailable",273...options.errors,274};275276const error = errors[result.status];277if (error) {278throw new ApiError(result, error);279}280281if (!result.ok) {282throw new ApiError(result, "Generic Error");283}284};285286/**287* Request method288* @param config The OpenAPI configuration object289* @param options The request options from the service290* @returns CancelablePromise<T>291* @throws ApiError292*/293export const request = <T>(294config: OpenAPIConfig,295options: ApiRequestOptions,296): CancelablePromise<T> => {297return new CancelablePromise(async (resolve, reject, onCancel) => {298try {299const url = getUrl(config, options);300const formData = getFormData(options);301const body = getRequestBody(options);302const headers = await getHeaders(config, options);303304if (!onCancel.isCancelled) {305const response = await sendRequest(306config,307options,308url,309body,310formData,311headers,312onCancel,313);314const responseBody = await getResponseBody(response);315const responseHeader = getResponseHeader(316response,317options.responseHeader,318);319320const result: ApiResult = {321url,322ok: response.ok,323status: response.status,324statusText: response.statusText,325body: responseHeader ?? responseBody,326};327328catchErrorCodes(options, result);329330resolve(result.body);331}332} catch (error) {333reject(error);334}335});336};337338339