import { Client } from '@botpress/client' import * as sdk from '@botpress/sdk' import fs from 'fs' import pathlib from 'path' import * as uuid from 'uuid' import impl from '../../src' import { fetchAllIntegrations, ApiIntegration } from '../api' import defaults from '../defaults' import * as retry from '../retry' import { Test } from '../typings' import * as utils from '../utils' type SecretDef = NonNullable const fetchIntegration = async (client: Client, integrationName: string): Promise => { const integrations = await fetchAllIntegrations(client) return integrations.find(({ name }) => name === integrationName) } const appendSecretDefinition = (originalTsContent: string, secrets: SecretDef): string => { const regex = /( *)version: (['"].*['"]),/ const replacement = [ 'version: $2,', 'secrets: {', ...Object.entries(secrets).map(([secretName, secretDef]) => ` ${secretName}: ${JSON.stringify(secretDef)},`), '},', ] .map((s) => `$1${s}`) // for indentation .join('\n') const modifiedTsContent = originalTsContent.replace(regex, replacement) return modifiedTsContent } export const requiredIntegrationSecrets: Test = { name: 'cli should require required integration secrets', handler: async ({ tmpDir, workspaceHandle, dependencies, ...creds }) => { const botpressHomeDir = pathlib.join(tmpDir, '.botpresshome') const baseDir = pathlib.join(tmpDir, 'integrations') const integrationSuffix = uuid.v4().replace(/-/g, '') const name = `myintegration${integrationSuffix}` const integrationName = `${workspaceHandle}/${name}` const integrationDir = pathlib.join(baseDir, name) const definitionPath = pathlib.join(integrationDir, 'integration.definition.ts') const argv = { ...defaults, botpressHome: botpressHomeDir, confirm: true, ...creds, } const client = new Client({ apiUrl: creds.apiUrl, token: creds.token, workspaceId: creds.workspaceId, retry: retry.config, }) await impl .init({ ...argv, workDir: baseDir, name: integrationName, type: 'integration', template: 'empty' }) .then(utils.handleExitCode) const originalDefinition: string = fs.readFileSync(definitionPath, 'utf-8') const modifiedDefinition = appendSecretDefinition(originalDefinition, { REQUIRED_SECRET: {}, OPTIONAL_SECRET: { optional: true }, }) fs.writeFileSync(definitionPath, modifiedDefinition, 'utf-8') await utils.fixBotpressDependencies({ workDir: integrationDir, target: dependencies }) await utils.npmInstall({ workDir: integrationDir }).then(utils.handleExitCode) await impl.build({ ...argv, workDir: integrationDir }).then(utils.handleExitCode) await impl.login({ ...argv }).then(utils.handleExitCode) const { exitCode } = await impl.deploy({ ...argv, workDir: integrationDir, secrets: ['OPTIONAL_SECRET=lol'], botId: undefined, createNewBot: undefined, }) if (exitCode === 0) { throw new Error('Expected deploy to fail') } await impl .deploy({ ...argv, workDir: integrationDir, secrets: ['REQUIRED_SECRET=lol'], botId: undefined, createNewBot: undefined, }) .then(utils.handleExitCode) // cleanup deployed integration const { integration: deployedIntegration } = await client.getIntegrationByName({ name: integrationName, version: '0.1.0', }) if (!deployedIntegration.secrets.includes('REQUIRED_SECRET')) { throw new Error( `Integration ${integrationName} should have secret REQUIRED_SECRET, got: ${deployedIntegration.secrets.join(', ')}` ) } await client.deleteIntegration({ id: deployedIntegration.id }) if (await fetchIntegration(client, integrationName)) { throw new Error(`Integration ${integrationName} should have been deleted`) } }, }