UNPKG

@splunk/rum-cli

Version:

Tools for handling symbol and mapping files for symbolication

150 lines (149 loc) 7.61 kB
"use strict"; /* * Copyright Splunk Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.mockUploadFile = exports.uploadFile = exports.fetchAndroidMappingMetadata = exports.ErrorCategory = void 0; exports.formatCLIErrorMessage = formatCLIErrorMessage; const axios_1 = __importDefault(require("axios")); const fs_1 = __importDefault(require("fs")); const form_data_1 = __importDefault(require("form-data")); const TOKEN_HEADER = 'X-SF-Token'; var ErrorCategory; (function (ErrorCategory) { ErrorCategory["RequestEntityTooLarge"] = "REQUEST_ENTITY_TOO_LARGE"; ErrorCategory["NetworkIssue"] = "NETWORK_ISSUE"; ErrorCategory["NoResponse"] = "NO_RESPONSE"; ErrorCategory["GeneralHttpError"] = "GENERAL_HTTP_ERROR"; ErrorCategory["Unexpected"] = "UNEXPECTED"; })(ErrorCategory || (exports.ErrorCategory = ErrorCategory = {})); function formatCLIErrorMessage(error) { var _a, _b, _c; const entryIndent = ' '; const detailEntries = []; // core technical message detailEntries.push(`${entryIndent}"message": "${error.message}"`); // attempted url if available if ((_a = error.details) === null || _a === void 0 ? void 0 : _a.url) { detailEntries.push(`${entryIndent}"url": "${error.details.url}"`); } // http status code if available if (((_b = error.details) === null || _b === void 0 ? void 0 : _b.status) !== undefined) { detailEntries.push(`${entryIndent}"status": ${error.details.status}`); } // codes from error.details.data (e.g., 'ENOTFOUND') if available if ((_c = error.details) === null || _c === void 0 ? void 0 : _c.data) { const data = error.details.data; // data is now DetailDataObject | string | undefined // Check if data is an object (and not null) before trying to access properties if (typeof data === 'object' && data !== null) { // Safely check if 'code' property exists and is a string if ('code' in data && typeof data.code === 'string') { detailEntries.push(`${entryIndent}"code": "${data.code}"`); } // can add other fields from data if needed, e.g.: // if ('someOtherField' in data && typeof data.someOtherField === 'expectedType') { // detailEntries.push(`${entryIndent}"someOtherField": "${data.someOtherField}"`); // } } else if (typeof data === 'string') { detailEntries.push(`${entryIndent}"responseData": "${data}"`); } } const detailsBlockContent = detailEntries.length > 0 ? `\n${detailEntries.join(',\n')}\n` : ''; const detailsBlock = `details: [${detailsBlockContent}]`; return `Error: ${error.userFriendlyMessage}\n${detailsBlock}`; } const fetchAndroidMappingMetadata = (_a) => __awaiter(void 0, [_a], void 0, function* ({ url, token, axiosInstance }) { var _b, _c, _d; const headers = { 'X-SF-Token': token, 'Accept': 'application/json', }; try { const client = axiosInstance || axios_1.default; const response = yield client.get(url, { headers }); return response.data; } catch (error) { if (axios_1.default.isAxiosError(error)) { throw new Error(`HTTP ${(_b = error.response) === null || _b === void 0 ? void 0 : _b.status}: ${(_c = error.response) === null || _c === void 0 ? void 0 : _c.statusText}\nResponse Data: ${JSON.stringify((_d = error.response) === null || _d === void 0 ? void 0 : _d.data, null, 2)}`); } else { throw error; } } }); exports.fetchAndroidMappingMetadata = fetchAndroidMappingMetadata; // This uploadFile method will be used by all the different commands that want to upload various types of // symbolication files to o11y cloud. The url, file, and additional parameters are to be prepared by the // calling method. Various errors, Error, axiosErrors and all should be handled by the caller of this method. // Since the API contracts with the backend are not yet determined. This is subject to change const uploadFile = (_a, axiosInstance_1) => __awaiter(void 0, [_a, axiosInstance_1], void 0, function* ({ url, file, token, parameters, onProgress }, axiosInstance) { const formData = new form_data_1.default(); formData.append(file.fieldName, fs_1.default.createReadStream(file.filePath)); for (const [key, value] of Object.entries(parameters)) { formData.append(key, value); } const fileSizeInBytes = fs_1.default.statSync(file.filePath).size; yield (axiosInstance || axios_1.default).put(url, formData, { headers: Object.assign(Object.assign({}, formData.getHeaders()), { [TOKEN_HEADER]: token }), onUploadProgress: (progressEvent) => { const loaded = progressEvent.loaded; const total = progressEvent.total || fileSizeInBytes; const progress = (loaded / total) * 100; if (onProgress) { onProgress({ progress, loaded, total }); } }, }); }); exports.uploadFile = uploadFile; // temporary function // mockUploadFile can be used when the endpoint for the real uploadFile call is not ready const mockUploadFile = (_a) => __awaiter(void 0, [_a], void 0, function* ({ file, onProgress }) { const fileSizeInBytes = fs_1.default.statSync(file.filePath).size; return new Promise((resolve) => { const mbps = 25; const bytes_to_megabits = (bytes) => bytes * 8 / 1000 / 1000; // simulate axios progress events const tick = 50; let msElapsed = 0; const intervalId = setInterval(() => { msElapsed += tick; const loaded = Math.floor((msElapsed / 1000) * mbps / 8 * 1024 * 1024); const total = fileSizeInBytes; const progress = (loaded / total) * 100; onProgress === null || onProgress === void 0 ? void 0 : onProgress({ loaded, total, progress }); }, tick); // simulate axios completion setTimeout(() => { clearInterval(intervalId); resolve(); }, bytes_to_megabits(fileSizeInBytes) / mbps * 1000); }); }); exports.mockUploadFile = mockUploadFile;