UNPKG

@sassoftware/vi-solution-extension-upload

Version:

Uploads controls to a SAS Visual Investigator instance

275 lines (240 loc) 8.17 kB
// This is used so that we can call https without a certificate process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0; const env = process.env; const cwd = process.cwd(); const fs = require("fs"); const glob = require("glob"); const axios = require("axios"); const packageJson = require(`${cwd}/package.json`); const NOT_APPLICABLE_DIRECTIVE = "not-applicable"; const NOT_APPLICABLE_LABEL = "Not Applicable"; const solutionName = packageJson.name; const solutionDisplayName = packageJson.name || packageJson.displayName; const projectTypes = { DESKTOP: "elements", MOBILE: "mobile-elements" }; const config = { hostname: env.SVI_HOSTNAME, username: !env.SVI_USERNAME || env.SVI_USERNAME === "" ? null : env.SVI_USERNAME, password: !env.SVI_PASSWORD || env.SVI_PASSWORD === "" ? null : env.SVI_PASSWORD, bundlePath: getBundlePath() || `${cwd}/dist/solution-extensions/main.*.js`, controlsPath: env.SVI_CONTROLS_PATH, configPath: `${cwd}/projects/components/src/lib`, bearerToken: env.SVI_BEARER_TOKEN ?? null }; async function main() { require("@babel/register")({ presets: ["@babel/preset-env", "@babel/preset-typescript"], plugins: ["@babel/plugin-transform-runtime"], ignore: [/node_modules\/(?!@sassoftware\/vi-api)/], extensions: [".js", ".ts"] }); // ignore any react component imports. // https://github.com/nodejs/node/issues/32483 // eslint-disable-next-line node/no-deprecated-api require.extensions[".tsx"] = () => {}; const logonToken = config.bearerToken ?? (await getLogonToken()); verifyConfig(); configureAxios(logonToken); const controlMetadata = getConfigs(solutionName, solutionDisplayName, config.bundlePath); const controlActionsObj = getActions(solutionDisplayName); // Upload the controls to configured svi server. uploadControls(controlMetadata, controlActionsObj).catch(console.log); } function verifyConfig() { if (!config.hostname) { throw new Error("Set SAS Visual Investigator server host name - environment variable SVI_HOSTNAME"); } // If bearer token property exists but is empty, throw an error. if (env.SVI_BEARER_TOKEN === "") { throw new Error("Set SAS Visual Investigator bearer token - environment variable SVI_BEARER_TOKEN"); } // If the SVI_BEARER_TOKEN doesn't exist, then the username and password must be set. if (!env.SVI_BEARER_TOKEN && !config.username) { throw new Error("Set SAS Visual Investigator admin username - environment variable SVI_USERNAME"); } if (!env.SVI_BEARER_TOKEN && !config.password) { throw new Error("Set SAS Visual Investigator admin password - environment variable SVI_PASSWORD"); } } function getProjectName() { const projectIndex = process.argv.indexOf("--project"); const projectValue = projectIndex !== -1 && process.argv[projectIndex + 1]; return projectValue; } function getBundlePath() { const projectName = getProjectName(); if (projectName === projectTypes.MOBILE) { return env.SMI_BUNDLE_PATH; } else { return env.SVI_BUNDLE_PATH; } } function configureAxios(logonToken) { axios.defaults.headers.common.Authorization = "Bearer " + logonToken; } function getSourceBundle(bundleGlob) { const codeSrc = glob.sync(bundleGlob); // Only expect a single file to be found, if anything else error. if (codeSrc.length > 1) { throw new Error("Source matched > 1 file - does the dist directory need to be cleaned?"); } else if (codeSrc.length === 0) { throw new Error("No source file found"); } return fs.readFileSync(codeSrc[0], "utf-8"); } function getConfigs(key, name, bundleGlob) { const code = getSourceBundle(bundleGlob); const isMobile = getProjectName() === projectTypes.MOBILE; if (isMobile) { return [ { controlCategory: "MobileHomepageControls", controlDescription: name, directiveName: NOT_APPLICABLE_DIRECTIVE, displayName: name, name: `${key}-mobile`, customControl: true, propertiesTitle: NOT_APPLICABLE_LABEL, controlAttributes: { metadata: { showInToolbox: "false" } }, directive: code } ]; } else { return [ { controlCategory: "Fields", controlDescription: name, directiveName: NOT_APPLICABLE_DIRECTIVE, displayName: name, name: key, customControl: true, propertiesTitle: NOT_APPLICABLE_LABEL, controlAttributes: { metadata: { showInToolbox: "false" } }, directive: code } ]; } } function getActions() { const controlActionsObj = { controls: [], actions: [] }; let controls = []; if (config.controlsPath) { controls = require(`${cwd}/${config.controlsPath}`).default; } else { const files = glob.sync(config.configPath + "/**/*.control.ts"); controls = files?.map((f) => require(f).control); } controls?.forEach(function (data) { if (data.category === "ToolbarItems") { controlActionsObj.controls.push({ controlCategory: data.category, controlDescription: data.name, directiveName: data.directiveName, displayName: data.displayName.defaultText, name: data.name, customControl: true, propertiesTitle: NOT_APPLICABLE_LABEL, controlAttributes: data.controlAttributes, directive: "" }); controlActionsObj.actions.push({ actionName: data.name, controlName: data.name, displayName: data.displayName, clientApplication: "desktop", requiredCapabilities: [], customAction: true, actionAttributes: data.controlAttributes, solutionName: "sas_visual_investigator_default" }); } }); return controlActionsObj; } async function uploadControls(controlsArray, controlActionsObj) { const isMobile = getProjectName() === projectTypes.MOBILE; console.log("Uploading solution: start"); await axios .post( getUrl("/svi-datahub/config"), JSON.stringify({ controls: [...controlsArray, ...controlActionsObj.controls], actions: controlActionsObj.actions }), { headers: { "Content-Type": "application/json" } } ) .then(async () => { if (isMobile) { // Clear the mobile cache after each upload. await axios .put(getUrl("/SASMobileInvestigator/cache"), {}, {}) .then(() => { console.log("Clear mobile cache: success"); }) .catch(() => { console.error("Clear mobile cache: failure"); }); } console.log("Uploading solution: done"); }) .catch((err) => { handleAxiosError(err); console.error("Error calling svi-datahub"); console.error("Uploading solution: failed"); }); } function getLogonToken() { const url = getUrl("/SASLogon/oauth/token"); const logonData = `grant_type=password&username=${config.username}&password=${config.password}`; const logonOptions = { headers: { accept: "application/json", Authorization: "Basic c2FzLmVjOg==" } }; return axios .post(url, logonData, logonOptions) .then((tokenResponse) => { return tokenResponse.data.access_token; }) .catch((err) => { console.error("Error Calling SASLogon"); handleAxiosError(err); }); } function getUrl(path) { return config.hostname + path; } function handleAxiosError(error) { if (error.response) { /* * The request was made and the server responded with a * status code that falls out of the range of 2xx */ console.log(error.response.data); console.log(error.response.status); console.log(error.response.headers); } else if (error.request) { /* * The request was made but no response was received, `error.request` * is an instance of XMLHttpRequest in the browser and an instance * of http.ClientRequest in Node.js */ console.log(error.request); } else { // Something happened in setting up the request and triggered an Error console.log("Error", error.message); } } exports.upload = main;