UNPKG

@unito/integration-cli

Version:

Integration CLI

193 lines (192 loc) 8.78 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ENCRYPTION_PREFIX = exports.DEFAULT_CONFIGURATION_NAME = exports.CredentialScope = exports.CrawlMode = void 0; exports.getConfigurationPath = getConfigurationPath; exports.getConfiguration = getConfiguration; exports.writeConfiguration = writeConfiguration; exports.writeTestAccount = writeTestAccount; const tslib_1 = require("tslib"); const fs = tslib_1.__importStar(require("fs")); const ajv_1 = tslib_1.__importDefault(require("ajv")); const ajv_formats_1 = tslib_1.__importDefault(require("ajv-formats")); const better_ajv_errors_1 = tslib_1.__importDefault(require("better-ajv-errors")); const configurationTypes_1 = require("../configurationTypes"); const errors_1 = require("../errors"); const integrations_1 = require("./integrations"); const globalConfiguration_1 = require("./globalConfiguration"); var CrawlMode; (function (CrawlMode) { CrawlMode["FULL"] = "full"; CrawlMode["SAMPLE"] = "sample"; CrawlMode["SINGLE"] = "single"; })(CrawlMode || (exports.CrawlMode = CrawlMode = {})); var CredentialScope; (function (CredentialScope) { CredentialScope["DEVELOPMENT"] = "development"; CredentialScope["COMPLIANCE"] = "compliance"; })(CredentialScope || (exports.CredentialScope = CredentialScope = {})); exports.DEFAULT_CONFIGURATION_NAME = '.unito.json'; exports.ENCRYPTION_PREFIX = 'unito-secret-v1://'; /** * Check for environment specific configuration, otherwise uses the default one. * * Production environment always uses the default configuration. * @param environment targeted environment * @returns path to the environment configuration file */ function getConfigurationPath(environment = globalConfiguration_1.Environment.Production) { const environmentBasedPath = `${process.cwd()}/.unito.${environment}.json`; if (environment !== globalConfiguration_1.Environment.Production && fs.existsSync(environmentBasedPath)) { return environmentBasedPath; } else { return `${process.cwd()}/${exports.DEFAULT_CONFIGURATION_NAME}`; } } async function getConfiguration(environment, customConfigPath) { (0, integrations_1.validateIsIntegrationDirectory)(); let configuration; let fileContent; const configurationPath = customConfigPath ? `${process.cwd()}${customConfigPath.startsWith('/') ? customConfigPath : `/${customConfigPath}`}` : getConfigurationPath(environment); try { fileContent = await fs.promises.readFile(configurationPath, 'utf-8'); } catch (error) { throw new errors_1.NoConfigurationFileError(); } try { configuration = JSON.parse(fileContent); } catch (error) { throw new errors_1.ConfigurationMalformed(); } // Initialize the required structure in the configuration. if (!configuration.testAccounts) { configuration.testAccounts = { development: {} }; } if (!configuration.testAccounts.development) { configuration.testAccounts.development = {}; } for (const authorization of configuration.authorizations ?? []) { if (authorization.oauth2) { if (!authorization.oauth2.grantType) { authorization.oauth2.grantType = configurationTypes_1.GrantType.AUTHORIZATION_CODE; } if (!authorization.oauth2.requestContentType) { authorization.oauth2.requestContentType = configurationTypes_1.RequestContentType.URL_ENCODED; } if (!authorization.oauth2.responseContentType) { authorization.oauth2.responseContentType = configurationTypes_1.RequestContentType.JSON; } } } await validateConfiguration(configuration); return configuration; } /** * Write the configuration to the default configuration file. */ async function writeConfiguration(configuration, environment = globalConfiguration_1.Environment.Production, customConfigPath) { (0, integrations_1.validateIsIntegrationDirectory)(); await validateConfiguration(configuration); const configurationPath = customConfigPath ? `${process.cwd()}${customConfigPath.startsWith('/') ? customConfigPath : `/${customConfigPath}`}` : getConfigurationPath(environment); await fs.promises.writeFile(configurationPath, JSON.stringify(configuration, null, 2)); } async function writeTestAccount(configuration, environment = globalConfiguration_1.Environment.Production, customConfigPath, account, accountName) { // istanbul ignore next if (accountName !== 'development' && accountName !== 'compliance') { throw new Error('Invalid account name'); } configuration.testAccounts ??= {}; configuration.testAccounts[accountName] = { ...configuration.testAccounts[accountName], ...account }; await writeConfiguration(configuration, environment, customConfigPath); } async function validateConfiguration(configuration) { const directory = `${__dirname}/../../schemas`; const files = await fs.promises.readdir(directory); const jsonFiles = files.filter(filename => filename.endsWith('.json')); const schemasContent = await Promise.all(jsonFiles.map(filename => fs.promises.readFile(`${directory}/${filename}`, 'utf8'))); const schemas = schemasContent.map(content => JSON.parse(content)); const spec = new ajv_1.default({ schemas, keywords: ['tsEnumNames'], allErrors: true }); (0, ajv_formats_1.default)(spec); const schema = 'https://unito.io/integration_cli/configuration.schema.json'; const validate = spec.getSchema(schema); if (!validate) { throw new Error(`Schema ${schema} not found'`); } // ESLint seems confused about this AJV function and thinks it is async. // eslint-disable-next-line @typescript-eslint/no-floating-promises validate(configuration); if (validate.errors) { throw new errors_1.ConfigurationInvalid('Your configuration file is invalid!', validate.errors, (0, better_ajv_errors_1.default)(schema, configuration, validate.errors, { indent: 2 })); } // Validate test accounts. if (configuration.testAccounts && configuration.authorizations?.length) { for (const [name, account] of Object.entries(configuration.testAccounts)) { if (Object.keys(account).length) { validateTestAccount(name, account, configuration.authorizations, spec); } } } } function validateTestAccount(credentialName, credential, authorizationDefinitions, spec) { const authorizationSchemas = authorizationDefinitions.map(constructAuthorizationSchema); const schema = { anyOf: authorizationSchemas }; const validate = spec.compile(schema); validate(credential); if (validate.errors) { throw new errors_1.ConfigurationInvalid(`Your testAccount ${credentialName} is invalid!`, validate.errors, (0, better_ajv_errors_1.default)(schema, credential, validate.errors, { indent: 2 })); } } function constructAuthorizationSchema(authorizationDefinition) { const authorizationSchema = { type: 'object', additionalProperties: true, }; if (authorizationDefinition.method === configurationTypes_1.Method.OAUTH2) { authorizationSchema.properties = { accessToken: { type: 'string', }, refreshToken: { type: 'string', }, expiresIn: { type: 'number', }, tokenType: { type: 'string', }, scope: { type: 'string', }, oauth2ExtraParameters: { type: 'object', additionalProperties: true, }, }; authorizationSchema.required = ['accessToken']; } const authorizationVariables = Object.entries(authorizationDefinition.variables ?? {}); if (authorizationVariables.length) { const variableProperties = Object.fromEntries(authorizationVariables.map(([name, definition]) => [ name, { // Sanitize Type to compile schema type: ['boolean', 'number'].includes(definition.type) ? definition.type : 'string', format: definition.format, pattern: definition.pattern, }, ])); authorizationSchema.properties = { ...authorizationSchema.properties, ...variableProperties }; const required = Array.from(new Set(authorizationVariables.filter(([, definition]) => definition.required).map(([name]) => name))); if (required.length) { (authorizationSchema.required ??= []).push(...required); } } return authorizationSchema; }