UNPKG

@pradyumn-el/pollycli

Version:

pollycli lets users access the functionalities of Polly over a command line interface

318 lines (291 loc) 9.99 kB
const { pollyApi } = require('./api-client'); import { organizationDetails } from './organization'; const fs = require('fs'); const axios = require('axios'); const pollyEnv = require('./env.json'); const pollyHeaders = require('./pollyheaders'); const pollymsg = require('./message'); const pollyHelpers = require('./polly-helpers'); const pollyLogs = require('./logs'); export async function getLogList(run_id, log_id) { try { let runUrl = `${pollyEnv.baseV2Api}/runs/${run_id}?state=project`; const runDetails = await axios.get(runUrl, await pollyHeaders.getV2Headers()); const projectId = runDetails.data.data['attributes']['project']['id']; if (log_id) { await pollyLogs.getAppLogs(projectId, run_id, log_id); } else { const logsUrl = `/workspaces/${projectId}/runs/${run_id}/compute-app-sessions/logs`; const logDetailsResponse = await pollyApi.get(logsUrl); const logDetails = logDetailsResponse.data.data; const tableData = [ ['Log ID', 'Created Time'] ]; const tableConfig = { columns: { 0: { width: 80 } } }; for (const logDetail of logDetails) { tableData.push([logDetail['attributes']['log_id'], pollyHelpers.dateFormat(logDetail['attributes']['timestamp'])]); } pollymsg.pollyTable(tableData, 1, tableData.length, tableConfig); } } catch (error) { pollymsg.pollyError(`Not able to access the logs for run ID ${run_id}`); } } function isAppFileValid(appFile) { try { if (fs.existsSync(appFile)) { // extracting file let appInfo = null; try { appInfo = fs.readFileSync(appFile) appInfo = JSON.parse(appInfo); } catch (error) { pollymsg.pollyError(`File ${appFile} not valid.`) } } else { pollymsg.pollyError(`File ${appFile} does not exist.`) } } catch (error) { pollymsg.pollyError(`File path ${appFile} is not valid.`) } return true; } function isAppInfoValid(appInfo) { // checking if necessary fields are present const minRequiredKeys = ["name", "display_name", "description", "docker_image", "cpu", "memory", "port"]; const missingFields = minRequiredKeys.filter(x => !Object.keys(appInfo).includes(x) || !appInfo[x]); if (missingFields.length > 0) { pollymsg.pollyError("missing required fields: " + missingFields.join(", ")); } if (Object.keys(appInfo).includes('resource_access') && appInfo.resource_access != "public" && appInfo.resource_access != "private" ) { pollymsg.pollyError(`resource_access field should be either "public" or "private"`); } return true; } function readAppFile(appFile) { isAppFileValid(appFile); let appInfo = fs.readFileSync(appFile); appInfo = JSON.parse(appInfo); isAppInfoValid(appInfo); return appInfo; } export async function getAppInfo(appName) { const organizationInfo = await organizationDetails(); const orgId = organizationInfo.org_id; const orgSlug = organizationInfo.slug; let baseUrl = `/organizations/${orgId}/resources/compute-apps?filter[app_name]=${orgSlug}/${appName}`; try { const appData = (await pollyApi.get(baseUrl)).data.data; return appData } catch (error) { return [] } } export async function appUpdate(name, appFile, appInfo) { if(appFile) { isAppFileValid(appFile); appInfo = fs.readFileSync(appFile); appInfo = JSON.parse(appInfo); } const appDetails = await getAppInfo(name); const appUrl = appDetails[0].links.self; let patchObject = { "app_spec": appDetails[0].attributes.resource_info.app_spec }; let updates = 0; if (appInfo.description) { updates++; patchObject['description'] = appInfo.description; } if (appInfo.cpu) { updates++; patchObject['app_spec']['app_container_cpu_limit'] = appInfo.cpu; patchObject['app_spec']['app_container_cpu_requests'] = appInfo.cpu; } if (appInfo.memory) { updates++; patchObject['app_spec']['app_container_memory_requests'] = appInfo.memory; patchObject['app_spec']['app_container_memory_limit'] = appInfo.memory; } if (appInfo.docker_image) { updates++; patchObject['app_spec']['app_image'] = appInfo.docker_image; } if (appInfo.port) { updates++; patchObject['app_spec']['port'] = parseInt(appInfo.port); } const patchBaseData = { "data": { "type": "compute-apps", "id": appUrl.split('compute-apps/')[1], "attributes": { "resource_info": patchObject } } } if (updates < 1) { pollymsg.pollyMessage("Nothing to update"); process.exit(0); } try { await pollyApi.patch(appUrl, patchBaseData); pollymsg.pollyMessage("Update complete"); } catch (error) { pollymsg.pollyMessage("Not able to update"); } } export async function appAdd(appFile, appInfo) { if(appFile) { appInfo = readAppFile(appFile); } const { name, description, cpu, memory, display_name, port, docker_image, group, resource_access } = appInfo; const organizationInfo = await organizationDetails(); const orgId = organizationInfo.org_id; const orgSlug = organizationInfo.slug; const postData = { "data": { "type": "compute-apps", "attributes": { "resource_access": resource_access, "resource_name" : name, "resource_info": { "app_type": "docker_app", "display_name": display_name, "app_spec": { "app_image": docker_image, "app_container_memory_requests": parseInt(memory), "app_container_cpu_requests": cpu, "app_container_memory_limit": parseInt(memory), "app_container_cpu_limit": cpu, "port": parseInt(port) }, "description": description } } } } try { const val = name.split("-") if (val.length < 2) { pollymsg.pollyError("Name should have atleast one -"); } const finalUrl = `/organizations/${orgId}/resources/compute-apps` await pollyApi.post(finalUrl, postData); const appConfigurationJson = { component: `${orgSlug}/${name}`, url: `/apps/${name}`, frontend_info: { name: display_name, version: `elucidata/${val[0]}`, icon_image_url: 'https://s3-us-west-2.amazonaws.com/mithoo-public-data/MithooImages/MetScape.svg', description: description, app_type: 'docker', requestURL: val.splice(1).join("-"), app_initials: "", group: group ||"miscellaneous" } } if(group) { appConfigurationJson.frontend_info["tags"] = [group.toUpperCase()] } // Discover, Miscellaneous, Metabolomic Data, Screening Data, Multi-omic Data, Lipidomic Data // Proteomic Data, Sequencing Data const submitBody = { "data": [{ "type": "components", "attributes": appConfigurationJson }] } const appUrl = "/components"; try { await pollyApi.post(appUrl, submitBody); pollymsg.pollySuccess("Added the new application"); } catch (error) { pollymsg.pollyError("Not able to add application"); } } catch (error) { pollymsg.pollyError("Not able to add application"); } } export async function getAppsList(internalCall = false) { const organizationInfo = await organizationDetails(); const orgId = organizationInfo.org_id; const orgSlug = organizationInfo.slug; let baseUrl = `/organizations/${orgId}/resources/compute-apps`; let appsList = [] try { let tempData = null; do { tempData = (await pollyApi.get(baseUrl)).data; appsList.push(...tempData.data); baseUrl = tempData.links.next; } while (tempData.links && tempData.links.next); } catch (error) { pollymsg.pollyError("Not able to get the list of applications"); } if (internalCall) { const finalList = [] for (const eachApp of appsList) { if(!/elucidata\/.+3-\d-\dr$/.test(eachApp.attributes.resource_name) && !/elucidata\/.+el-maven$/.test(eachApp.attributes.resource_name)) { finalList.push(eachApp); } } return finalList; } const hostedApps = []; const displayList = [["Resource ID", "Resource Name", "Description", "Display Name", "Resource Info"]]; for (const eachApp of appsList) { // This is shiny App regex matcher and elmaven if(!/elucidata\/.+3-\d-\dr$/.test(eachApp.attributes.resource_name) && !/elucidata\/.+el-maven$/.test(eachApp.attributes.resource_name)) { hostedApps.push(eachApp.attributes); // console.log(eachApp.attributes.resource_info); const reso = JSON.stringify({"cpu": eachApp.attributes.resource_info.app_spec.app_container_cpu_requests, "memory": eachApp.attributes.resource_info.app_spec.app_container_memory_requests, "port": eachApp.attributes.resource_info.app_spec.port, "docker": eachApp.attributes.resource_info.app_spec.app_image }, null , ' '); displayList.push([eachApp.attributes.resource_id, eachApp.attributes.resource_name, eachApp.attributes.resource_info.description, eachApp.attributes.resource_info.display_name, reso]) } } const tableConfig = { columns: { 0: { width: 35 }, 1: { width: 30 }, 2: { width: 25 }, 3: { width: 25 }, 4: { width: 30 } } }; pollymsg.pollyTable(displayList, 1, displayList.length, tableConfig); } export function sampleAppFile() { const appFormat = { "name": "name of the application", "display_name": "name to be displayed on UI", "description": "small description about app", "docker_image": "docker image 7 tag for the app", "cpu": "no. of cpu cores to be assigned to the app (2,4,8) etc", "memory": "ram to be assigned to the app (2Gi, 4Gi, 8Gi) etc", "port": "port on which your app is running", "group": "group of app (metabolomics, proteomics, single cell etc)", "resource_access": "public/private", } console.log(appFormat); }