UNPKG

@adobe/aio-cli-lib-console

Version:
321 lines (284 loc) 10.8 kB
/* Copyright 2020 Adobe. All rights reserved. This file is licensed to you 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 REPRESENTATIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* All functions defined here must be pure, i.e. no I/O call should be done in this file */ const path = require('path') const clonedeep = require('lodash.clonedeep') const loggerNamespace = '@adobe/aio-cli-lib-console/pure-helpers' const logger = require('@adobe/aio-lib-core-logging')( loggerNamespace, { provider: 'debug', level: process.env.LOG_LEVEL || 'debug' } ) /** @private */ function orgsToPromptChoices (orgs) { return orgs // we only support entp orgs for now .filter(item => item.type === 'entp') .map(item => ({ name: item.name, value: item })) } /** @private */ function findOrgOrThrow (orgId, orgCode, workspaces) { // either orgId or orgCode is defined const orgById = orgId && workspaces.find(o => o.id === orgId) const orgByCode = orgCode && workspaces.find(o => o.code === orgCode) if (!orgById && !orgByCode) { throw new Error(`Organization '${orgCode || orgId}' not found.`) } if (orgById && orgByCode && orgByCode.id !== orgById.id) { throw new Error(`Organization code '${orgCode}' and id '${orgId}' do not refer to the same Organization.`) } return orgByCode || orgById } /** @private */ function findProjectOrThrow ({ projectId, projectName }, projects, options = { throwOnNotFound: true }) { // either projectId or projectName is defined const { throwOnNotFound } = options const projectByName = projectName && projects.find(p => p.name === projectName) const projectById = projectId && projects.find(p => p.id === projectId) if (!projectByName && !projectById && throwOnNotFound) { throw new Error(`Project '${projectName || projectId}' not found.`) } if (projectByName && projectById && projectById.id !== projectByName.id && throwOnNotFound) { throw new Error(`Project name '${projectName}' and id '${projectId}' do not refer to the same Project.`) } return projectById || projectByName } /** @private */ function findWorkspaceOrThrow ({ workspaceId, workspaceName }, workspaces, options = { throwOnNotFound: true }) { // either workspaceId or workspaceName is defined const { throwOnNotFound } = options const workspaceByName = workspaceName && workspaces.find(w => w.name === workspaceName) const workspaceById = workspaceId && workspaces.find(w => w.id === workspaceId) if (!workspaceByName && !workspaceById && throwOnNotFound) { throw new Error(`Workspace '${workspaceName || workspaceId}' not found.`) } if (workspaceByName && workspaceById && workspaceById.id !== workspaceByName.id && throwOnNotFound) { throw new Error(`Workspace name '${workspaceName}' and id '${workspaceId}' do not refer to the same Workspace.`) } return workspaceById || workspaceByName } /** @private */ function projectsToPromptChoices (projects) { const projectChoices = projects .filter(p => !p.deleted && p.enabled) .filter(p => p.type === 'jaeger') .map(item => ({ name: item.title, value: item })) // show projects by title and reverse order to show latest first, note reverse is in // place, let's make sure projects are not modified by copying the array return [...projectChoices].reverse() } /** @private */ function workspacesToPromptChoices (workspaces) { return workspaces .filter(p => p.enabled) .filter(p => p.runtime_enabled) .map(item => ({ name: item.name, value: item })) } /** @private */ function servicesToPromptChoices (services) { return filterEnabledServices(services) // we only support entp integrations for now .filter(s => s.type === 'entp') .map(s => ({ name: s.name, value: s })) } /** @private */ function licenseConfigsToPromptChoices (licenseConfigs) { // should handle licenseConfigs = null return licenseConfigs .map(l => ({ name: l.name, value: l })) } /** @private */ function filterEnabledServices (services) { return services.filter(s => s.enabled) } /** @private */ function findFirstEntpCredential (credentials) { return credentials.find(c => c.flow_type === 'entp' && (c.integration_type === 'service' || c.integration_type === 'oauth_server_to_server_migrate')) } /** @private */ function findFirstOAuthServerToServerCredential (credentials) { return credentials.find(c => c.flow_type === 'entp' && (c.integration_type === 'oauth_server_to_server' || c.integration_type === 'oauth_server_to_server_migrate')) } /** @private */ function servicePropertiesToNames (serviceProperties) { return serviceProperties.map(s => s.name) } // NOTE: this is a workaround for a bug in the returned LicenseConfigs lists (from getIntegration api call), every list // contains all the LicenseConfigs for all services, so this list must be filtered to map // to service specific licenseConfigs /** @private */ function fixServiceProperties (serviceProperties, supportedServices) { return serviceProperties.map(sp => { const orgService = supportedServices.find(os => os.code === sp.sdkCode) if (!orgService) { logger.warn(`Supported services did not contain a service with matching sdkCode : ${sp.sdkCode}`) } let fixedLicenseConfigs = null if (sp.licenseConfigs && orgService && orgService.properties && orgService.properties.licenseConfigs) { fixedLicenseConfigs = sp.licenseConfigs .filter(l => orgService.properties.licenseConfigs.find(ol => ol.id === l.id)) } return { ...sp, licenseConfigs: fixedLicenseConfigs } }) } /** @private */ function servicePropertiesToServiceSubscriptionPayload (serviceProperties) { return serviceProperties.map(sp => { return { sdkCode: sp.sdkCode, roles: sp.roles, licenseConfigs: (sp.licenseConfigs || null) && sp.licenseConfigs.map(l => ({ op: 'add', id: l.id, productId: l.productId })) } }) } /** @private */ function getCertFilesLocation (orgId, projectName, workspaceName, certDir) { const projectCertDir = path.join(certDir, `${orgId}-${projectName}`) const publicKeyFileName = `${workspaceName}.pem` const privateKeyFileName = `${workspaceName}.key` const publicKeyFilePath = path.join(projectCertDir, publicKeyFileName) const privateKeyFilePath = path.join(projectCertDir, privateKeyFileName) return { projectCertDir, publicKeyFileName, publicKeyFilePath, privateKeyFileName, privateKeyFilePath } } /** @private */ function getServiceSubscriptionsOperationPromptChoices (options = { cloneChoice: false, nopChoice: true }) { const addChoice = { name: 'Add new Services', value: 'select' } const cloneChoice = { name: 'Clone Services from existing Workspace', value: 'clone' } const nopChoice = { name: 'Skip', value: 'nop' } const choices = [addChoice] if (options.cloneChoice) { choices.push(cloneChoice) } if (options.nopChoice) { choices.push(nopChoice) } return choices } /** @private */ function enhanceWorkspaceConfiguration (workspaceConfig, supportedServices) { // enhance configuration with supported services (immutably) const enabledServices = filterEnabledServices(supportedServices) const orgServices = enabledServices.map(s => ({ name: s.name, code: s.code, type: s.type })) return { ...workspaceConfig, project: { ...workspaceConfig.project, org: { ...workspaceConfig.project.org, details: { ...workspaceConfig.project.org.details, services: orgServices } } } } } /** @private */ function workspaceNamesToPromptString (workspaceName) { if (!Array.isArray(workspaceName)) { workspaceName = [workspaceName] } if (workspaceName.length > 1) { const allButLast = workspaceName.slice(0, workspaceName.length - 1) const last = workspaceName[workspaceName.length - 1] return `Workspaces ${allButLast.join(', ')} and ${last}` } return `Workspace ${workspaceName[0]}` } /** @private */ function mergeExtensionPoints (currPayload, updatePayload) { // make sure to not overwrite fields in incoming objects const currPayloadEndpoints = clonedeep(currPayload.endpoints) const updatePayloadEndpoints = clonedeep(updatePayload.endpoints) // set merged to currPayload const merged = currPayloadEndpoints // add new payload to merged Object.entries(updatePayloadEndpoints) .forEach(([endpointName, newEndpoint]) => { const oldEndpoint = currPayloadEndpoints[endpointName] // merge endpoint operations merged[endpointName] = { ...oldEndpoint, ...newEndpoint } }) return { endpoints: merged } } /** @private */ function removeExtensionPoints (currPayload, payloadToRemove) { const currPayloadEndpoints = clonedeep(currPayload.endpoints) const payloadToRemoveEndpoints = clonedeep(payloadToRemove.endpoints) const ret = currPayloadEndpoints Object.entries(payloadToRemoveEndpoints) .forEach(([endpointName, endpoint]) => { const endpointToRemoveFrom = ret[endpointName] Object.keys(endpoint).forEach(operationName => { if (endpointToRemoveFrom && endpointToRemoveFrom[operationName]) { delete endpointToRemoveFrom[operationName] } }) if (endpointToRemoveFrom && Object.keys(endpointToRemoveFrom).length <= 0) { delete ret[endpointName] } }) return { endpoints: ret } } /** * @typedef {object} DevTerms * @property {string} text Text of Developer Terms * @property {string} locale Language of the Developer Terms text */ /** @private * @returns {DevTerms | {}} dev terms * */ function getDevTerms (payload) { if (payload && Array.isArray(payload.tc)) { return payload.tc[0] } return {} } module.exports = { orgsToPromptChoices, projectsToPromptChoices, workspacesToPromptChoices, servicesToPromptChoices, licenseConfigsToPromptChoices, filterEnabledServices, findFirstEntpCredential, findFirstOAuthServerToServerCredential, servicePropertiesToNames, fixServiceProperties, servicePropertiesToServiceSubscriptionPayload, getCertFilesLocation, findOrgOrThrow, findProjectOrThrow, findWorkspaceOrThrow, getServiceSubscriptionsOperationPromptChoices, enhanceWorkspaceConfiguration, workspaceNamesToPromptString, mergeExtensionPoints, removeExtensionPoints, getDevTerms }