@unito/integration-cli
Version:
Integration CLI
193 lines (192 loc) • 8.78 kB
JavaScript
;
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;
}