/** * MIT License * * Copyright (c) 2020-present, Elastic NV * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ import { PushOptions } from '../common_types'; import { safeNDJSONParse } from '../helpers'; import { MonitorHashID, MonitorSchema } from './monitor'; import { formatFailedMonitors, formatStaleMonitors, handleError, sendReqAndHandleError, sendRequest, APIMonitorError, } from './request'; import { generateURL } from './utils'; // Default batch size for bulk put / delete export const BATCH_SIZE = parseInt(process.env.CHUNK_SIZE) || 250; export const MAX_PAYLOAD_SIZE_KB = 50 * 1000; // 50 MB in KB export type PutResponse = { createdMonitors: string[]; updatedMonitors: string[]; failedMonitors: APIMonitorError[]; }; export async function bulkPutMonitors( options: PushOptions, schemas: MonitorSchema[] ) { const url = generateURL(options, 'bulk_update') + '/_bulk_update'; const resp = await sendReqAndHandleError({ url, method: 'PUT', auth: options.auth, body: JSON.stringify({ monitors: schemas }), }); const { failedMonitors } = resp; if (failedMonitors && failedMonitors.length > 0) { throw formatFailedMonitors(failedMonitors); } return resp; } export type GetResponse = { total: number; monitors: MonitorHashID[]; after_key?: string; }; export async function bulkGetMonitors( options: PushOptions ): Promise { const allMonitors: MonitorHashID[] = []; const resp = await fetchMonitors(options); allMonitors.push(...resp.monitors); let afterKey = resp.afterKey; const total = resp.total; while (allMonitors.length < total) { const resp = await fetchMonitors(options, afterKey); allMonitors.push(...resp.monitors); afterKey = resp.afterKey; } return { total, monitors: allMonitors, }; } const fetchMonitors = async (options: PushOptions, afterKey?: string) => { let url = generateURL(options, 'bulk_get'); if (afterKey) { url += `?search_after=${afterKey}`; } const resp = await sendReqAndHandleError({ url, method: 'GET', auth: options.auth, }); return { afterKey: resp.after_key, total: resp.total, monitors: resp.monitors, }; }; export type DeleteResponse = { deleted_monitors: string[]; }; export async function bulkDeleteMonitors( options: PushOptions, monitorIDs: string[] ) { return await sendReqAndHandleError({ url: generateURL(options, 'bulk_delete') + '/_bulk_delete', method: 'DELETE', auth: options.auth, body: JSON.stringify({ monitors: monitorIDs }), }); } type StatusResponse = { kibana: { version: string; }; }; export async function getVersion(options: PushOptions) { const data = await sendReqAndHandleError({ url: generateURL(options, 'status'), method: 'GET', auth: options.auth, }); return data.kibana.version; } export type LegacyAPISchema = { project: string; keep_stale: boolean; monitors: MonitorSchema[]; }; export async function createMonitorsLegacy({ schemas, keepStale, options, }: { schemas: MonitorSchema[]; keepStale: boolean; options: PushOptions; }) { const schema: LegacyAPISchema = { project: options.id, keep_stale: keepStale, monitors: schemas, }; const url = generateURL(options, 'legacy'); const { body, statusCode } = await sendRequest({ url, method: 'PUT', auth: options.auth, body: JSON.stringify(schema), }); const resBody = await handleError(statusCode, url, body); const allchunks = []; for await (const data of resBody) { allchunks.push(Buffer.from(data)); } const chunks = safeNDJSONParse(Buffer.concat(allchunks).toString('utf-8')); // Its kind of hacky for now where Kibana streams the response by // writing the data as NDJSON events (data can be interleaved), we // distinguish the final data by checking if the event was a progress vs complete event for (const chunk of chunks) { if (typeof chunk === 'string') { // Ignore the progress from Kibana as we chunk the requests continue; } const { failedMonitors, failedStaleMonitors } = chunk; if (failedMonitors && failedMonitors.length > 0) { throw formatFailedMonitors(failedMonitors); } if (failedStaleMonitors.length > 0) { throw formatStaleMonitors(failedStaleMonitors); } } }