diff --git a/package.json b/package.json index 70d2f8b4..09f4294f 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,6 @@ "request-promise": "^4.2.4", "resolve": "^1.10.0", "semver": "^7.0.0", - "validator": "^13.0.0", "which": "^2.0.1", "yeoman-environment": "^2.5.0" }, @@ -33,6 +32,7 @@ "@oclif/plugin-help": "^2.1.6", "@types/jest": "^25.1.0", "acorn": "^7.1.0", + "ajv": "^6.12.2", "codecov": "^3.6.1", "eol": "^0.9.1", "eslint": "^6.6.0", diff --git a/schema/config.schema.json b/schema/config.schema.json new file mode 100644 index 00000000..69ff5f44 --- /dev/null +++ b/schema/config.schema.json @@ -0,0 +1,156 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://adobe.io/schemas/aio-cli-plugin-app.json", + "type": "object", + "properties": { + "project": { "$ref": "#/definitions/project" } + }, + "required": [ "project"], + "definitions": { + "project": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "name": { + "type": "string", + "pattern": "^[a-zA-Z0-9]+$" + }, + "title": { "type": "string" }, + "description": { "type": "string" }, + "org": { "$ref": "#/definitions/org" }, + "workspace": { "$ref": "#/definitions/workspace" } + }, + "required": [ "id", "name", "title", "org", "workspace" ] + }, + "workspace": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "name": { + "type": "string", + "pattern": "^[a-zA-Z0-9]+$" + }, + "title": { "type": "string" }, + "description": { "type": "string" }, + "action_url": { + "type": "string", + "format": "uri" + }, + "app_url": { + "type": "string", + "format": "uri" + }, + "details": { "$ref": "#/definitions/details" } + }, + "required": [ "id", "name", "action_url", "app_url", "details" ] + }, + "details": { + "type": "object", + "properties": { + "credentials": { + "type": "array", + "items": { "$ref": "#/definitions/credential" }, + "default": [] + }, + "services": { + "type": "array", + "items": { "$ref": "#/definitions/service" }, + "default": [] + }, + "runtime": { + "type": "object", + "properties": { + "namespaces": { + "type": "array", + "items": { "$ref": "#/definitions/namespace" }, + "default": [] + } + }, + "required": [ "namespaces" ] + } + }, + "required": [ "credentials", "services", "runtime" ] + }, + "org": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "name": { + "type": "string", + "pattern": "^[a-zA-Z0-9]+$" + }, + "ims_org_id": { + "type": "string", + "format": "email" + } + }, + "required": [ "id", "name", "ims_org_id" ] + }, + "credential": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "name": { + "type": "string", + "pattern": "^[a-zA-Z0-9]+$" + }, + "integration_type": { + "type": "string", + "enum": [ "oauthweb", "oauthios", "oauthandroid", "service", "apikey" ] + }, + "oauth2": { "$ref": "#/definitions/oauth2" }, + "jwt": { "$ref": "#/definitions/jwt" } + }, + "required": [ "id", "name" ], + "oneOf": [ + { "required": ["oauth2"] }, + { "required": ["jwt"] } + ] + }, + "service": { + "type": "object", + "properties": { + "code": { "type": "string" }, + "name": { "type": "string" } + }, + "required": [ "code", "name" ] + }, + "namespace": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "auth": { "type": "string" } + }, + "required": [ "name", "auth" ] + }, + "oauth2": { + "type": "object", + "properties": { + "client_id": { "type": "string" }, + "client_secret": { "type": "string" }, + "redirect_uri": { + "type": "string", + "format": "uri" + } + }, + "required": [ "client_id", "client_secret", "redirect_uri" ] + }, + "jwt": { + "type": "object", + "properties": { + "client_id": { "type": "string" }, + "client_secret": { "type": "string" }, + "techacct": { + "type": "string", + "format": "email" + }, + "meta_scopes": { + "type": "array", + "items": { "type": "string" }, + "default": [] + } + }, + "required": [ "client_id", "client_secret", "techacct", "meta_scopes" ] + } + } +} \ No newline at end of file diff --git a/src/commands/app/init.js b/src/commands/app/init.js index 8ef79aaf..017930e9 100644 --- a/src/commands/app/init.js +++ b/src/commands/app/init.js @@ -15,7 +15,7 @@ const path = require('path') const fs = require('fs-extra') const aioLogger = require('@adobe/aio-lib-core-logging')('@adobe/aio-cli-plugin-app:init', { provider: 'debug' }) const { flags } = require('@oclif/command') -const { importConfigJson, loadConfigFile, writeAio } = require('../../lib/import') +const { validateConfig, importConfigJson, loadConfigFile, writeAio } = require('../../lib/import') class InitCommand extends BaseCommand { async run () { @@ -32,10 +32,15 @@ class InitCommand extends BaseCommand { let services = 'AdobeTargetSDK,AdobeAnalyticsSDK,CampaignSDK,McDataServicesSdk' // todo fetch those from console when no --import if (flags.import) { - const config = loadConfigFile(flags.import).values + const { values: config } = loadConfigFile(flags.import) + const { valid: configIsValid, errors: configErrors } = validateConfig(config) + if (!configIsValid) { + const message = `Missing or invalid keys in config: ${JSON.stringify(configErrors, null, 2)}` + this.error(message) + } - projectName = config.name // must be defined - services = (config.services && config.services.map(s => s.code).join(',')) || '' + projectName = config.project.name + services = config.project.workspace.details.services.map(s => s.code).join(',') || '' } const env = yeoman.createEnv() diff --git a/src/lib/import.js b/src/lib/import.js index 39d15637..725929fe 100644 --- a/src/lib/import.js +++ b/src/lib/import.js @@ -9,14 +9,13 @@ OF ANY KIND, either express or implied. See the License for the specific languag governing permissions and limitations under the License. */ -const debug = require('debug')('aio-cli-plugin-app:import') +const aioLogger = require('@adobe/aio-lib-core-logging')('@adobe/aio-cli-plugin-app:import', { provider: 'debug' }) const path = require('path') const fs = require('fs-extra') const inquirer = require('inquirer') -const validator = require('validator') const yaml = require('js-yaml') const hjson = require('hjson') -const configUtil = require('@adobe/aio-lib-core-config/src/util') +const Ajv = require('ajv') const AIO_FILE = '.aio' const ENV_FILE = '.env' @@ -25,17 +24,13 @@ const AIO_ENV_SEPARATOR = '_' const FILE_FORMAT_ENV = 'env' const FILE_FORMAT_JSON = 'json' -// by default, all rules are required -// set `notRequired` if a rule is not required (key does not have to exist) -// `rule` can be a regex string or a function that returns a boolean, and takes one input -const gRules = [ - { key: 'name', rule: '^[a-zA-Z0-9]+$' }, - { key: 'project.name', rule: '^[a-zA-Z0-9]+$' }, - { key: 'project.org.name', rule: '^[a-zA-Z0-9]+$' }, - { key: 'app_url', rule: validator.isURL }, - { key: 'action_url', rule: validator.isURL }, - { key: 'credentials.oauth2.redirect_uri', rule: validator.isURL, notRequired: true } -] +function validateConfig (configJson) { + const schema = require('../../schema/config.schema.json') + const ajv = new Ajv({ allErrors: true }) + const validate = ajv.compile(schema) + + return { valid: validate(configJson), errors: validate.errors } +} /** * Load a config file @@ -82,41 +77,6 @@ function prettyPrintJson (json) { return JSON.stringify(json, null, 2) } -/** - * Validate the config.json. - * Throws an Error if any rules are not fulfilled. - * - * (future: use JSON schema) - * - * @param {object} json the json to validate - */ -function checkRules (json, rules = gRules) { - const invalid = rules.filter(item => { - let value = configUtil.getValue(json, item.key) - - if (!value && item.notRequired) { - return false - } - value = value || '' - - if (typeof (item.rule) === 'function') { - return !item.rule(value) - } else { - return (value.match(new RegExp(item.rule)) === null) - } - }) - - if (invalid.length) { - const explanations = invalid.map(item => { - item.value = configUtil.getValue(json, item.key) || '' - return { ...item, rule: undefined } - }) - - const message = `Missing or invalid keys in config: ${JSON.stringify(explanations)}` - throw new Error(message) - } -} - /** * Confirmation prompt for overwriting, or merging a file if it already exists. * @@ -169,6 +129,7 @@ async function checkFileConflict (filePath) { /** * Transform a json object to a flattened version. Any nesting is separated by the `separator` string. * For example, if you have the `_` separator string, flattening this: + * * { * foo: { * bar: 'a', @@ -185,6 +146,8 @@ async function checkFileConflict (filePath) { * 'foo_baz_faz': 'b' * } * + * Any underscores in the object key are escaped with an underscore. + * * @param {object} json the json object to transform * @param {object} result the result object to initialize the function with * @param {string} prefix the prefix to add to the final key @@ -236,8 +199,8 @@ function mergeEnv (oldEnv, newEnv) { } } - debug(`mergeEnv - oldEnv:${oldEnv}`) - debug(`mergeEnv - newEnv:${newEnv}`) + aioLogger.debug(`mergeEnv - oldEnv:${oldEnv}`) + aioLogger.debug(`mergeEnv - newEnv:${newEnv}`) oldEnv.split(NEWLINES).forEach(splitLine) newEnv.split(NEWLINES).forEach(splitLine) @@ -246,7 +209,7 @@ function mergeEnv (oldEnv, newEnv) { .keys(result) .map(key => `${key}=${result[key]}`) .join('\n') - debug(`mergeEnv - mergedEnv:${mergedEnv}`) + aioLogger.debug(`mergeEnv - mergedEnv:${mergedEnv}`) return mergedEnv } @@ -261,11 +224,11 @@ function mergeJson (oldData, newData) { const { values: oldJson } = loadConfigFile(Buffer.from(oldData)) const { values: newJson } = loadConfigFile(Buffer.from(newData)) - debug(`mergeJson - oldJson:${prettyPrintJson(oldJson)}`) - debug(`mergeJson - newJson:${prettyPrintJson(newJson)}`) + aioLogger.debug(`mergeJson - oldJson:${prettyPrintJson(oldJson)}`) + aioLogger.debug(`mergeJson - newJson:${prettyPrintJson(newJson)}`) const mergedJson = prettyPrintJson({ ...oldJson, ...newJson }) - debug(`mergeJson - mergedJson:${mergedJson}`) + aioLogger.debug(`mergeJson - mergedJson:${mergedJson}`) return mergedJson } @@ -278,8 +241,8 @@ function mergeJson (oldData, newData) { * @param {*} fileFormat the file format of the data (env, json) */ function mergeData (oldData, newData, fileFormat) { - debug(`mergeData - oldData: ${oldData}`) - debug(`mergeData - newData:${newData}`) + aioLogger.debug(`mergeData - oldData: ${oldData}`) + aioLogger.debug(`mergeData - newData:${newData}`) if (fileFormat === FILE_FORMAT_ENV) { return mergeEnv(oldData, newData) @@ -302,14 +265,14 @@ function mergeData (oldData, newData, fileFormat) { */ async function writeFile (destination, data, flags = {}) { const { overwrite = false, merge = false, fileFormat = FILE_FORMAT_JSON, interactive = false } = flags - debug(`writeFile - destination: ${destination} flags:${flags}`) - debug(`writeFile - data: ${data}`) + aioLogger.debug(`writeFile - destination: ${destination} flags:${flags}`) + aioLogger.debug(`writeFile - data: ${data}`) let answer = { overwrite, merge } // for non-interactive, get from the flags if (interactive) { answer = await checkFileConflict(destination) - debug(`writeEnv - answer (interactive): ${JSON.stringify(answer)}`) + aioLogger.debug(`writeEnv - answer (interactive): ${JSON.stringify(answer)}`) } if (answer.abort) { @@ -339,19 +302,19 @@ async function writeFile (destination, data, flags = {}) { * @param {boolean} [flags.interactive=false] set to true to prompt the user for file overwrite */ async function writeEnv (json, parentFolder, flags) { - debug(`writeEnv - json: ${JSON.stringify(json)} parentFolder:${parentFolder} flags:${flags}`) + aioLogger.debug(`writeEnv - json: ${JSON.stringify(json)} parentFolder:${parentFolder} flags:${flags}`) const destination = path.join(parentFolder, ENV_FILE) - debug(`writeEnv - destination: ${destination}`) + aioLogger.debug(`writeEnv - destination: ${destination}`) const resultObject = flattenObjectWithSeparator(json) - debug(`convertJsonToEnv - flattened and separated json: ${prettyPrintJson(resultObject)}`) + aioLogger.debug(`convertJsonToEnv - flattened and separated json: ${prettyPrintJson(resultObject)}`) const data = Object .keys(resultObject) .map(key => `${key}=${resultObject[key]}`) .join('\n') - debug(`writeEnv - data: ${data}`) + aioLogger.debug(`writeEnv - data: ${data}`) return writeFile(destination, data, { ...flags, fileFormat: FILE_FORMAT_ENV }) } @@ -367,16 +330,100 @@ async function writeEnv (json, parentFolder, flags) { * @param {boolean} [flags.interactive=false] set to true to prompt the user for file overwrite */ async function writeAio (json, parentFolder, flags) { - debug(`writeAio - parentFolder:${parentFolder} flags:${flags}`) - debug(`writeAio - json: ${prettyPrintJson(json)}`) + aioLogger.debug(`writeAio - parentFolder:${parentFolder} flags:${flags}`) + aioLogger.debug(`writeAio - json: ${prettyPrintJson(json)}`) const destination = path.join(parentFolder, AIO_FILE) - debug(`writeAio - destination: ${destination}`) + aioLogger.debug(`writeAio - destination: ${destination}`) const data = prettyPrintJson(json) return writeFile(destination, data, flags) } +/** + * Transform runtime object value to what this plugin expects (single runtime namespace). + * + * @example + * from: + * { + * "namespaces": [ + * { + * "name": "abc", + * "auth": "123" + * } + * ] + * } + * to: + * { + * "namespace": "abc", + * "auth": "123" + * } + * + * @param {object} runtime the runtime value to transform + * @returns {object} the transformed runtime object + * @private + */ +function transformRuntime (runtime) { + const newRuntime = (runtime.namespaces.length > 0) ? runtime.namespaces[0] : {} + if (newRuntime.name) { + newRuntime.namespace = newRuntime.name + delete newRuntime.name + } + + return newRuntime +} + +/** + * Transforms a credentials array to an object, to what this plugin expects. + * Enrich with ims_org_id if it is a jwt credential. + * + * @example + * from: + * [{ + * "id": "17561142", + * "name": "Project Foo", + * "integration_type": "oauthweb", + * "oauth2": { + * "client_id": "XYXYXYXYXYXYXYXYX", + * "client_secret": "XYXYXYXYZZZZZZ", + * "redirect_uri": "https://test123" + * } + * }] + * to: + * { + * "Project Foo": { + * "client_id": "XYXYXYXYXYXYXYXYX", + * "client_secret": "XYXYXYXYZZZZZZ", + * "redirect_uri": "https://test123" + * } + * } + * + * @param {Array} credentials array from Downloadable File Format + * @returns {object} credentials object + * @private + */ +function transformCredentials (credentials, imsOrgId) { + // find jwt credential + const credential = credentials.find(credential => typeof credential.jwt === 'object') + + // enrich jwt credentials with ims org id + if (credential && credential.jwt && !credential.jwt.ims_org_id) { + aioLogger.debug('adding ims_org_id to $ims.jwt config') + credential.jwt.ims_org_id = imsOrgId + } + + return credentials.reduce((acc, credential) => { + // the json schema enforces either jwt OR oauth2 keys in a credential + let value = credential.oauth2 + if (!value) { + value = credential.jwt + } + acc[credential.name] = value + + return acc + }, {}) +} + /** * Import a downloadable config and write to the appropriate .env (credentials) and .aio (non-credentials) files. * @@ -387,38 +434,34 @@ async function writeAio (json, parentFolder, flags) { * @param {boolean} [flags.merge=false] set to true to merge in the existing .env file (takes precedence over overwrite) */ async function importConfigJson (configFileLocation, destinationFolder = process.cwd(), flags = {}) { - debug(`importConfigJson - configFileLocation: ${configFileLocation} destinationFolder:${destinationFolder} flags:${flags}`) + aioLogger.debug(`importConfigJson - configFileLocation: ${configFileLocation} destinationFolder:${destinationFolder} flags:${flags}`) const { values: config, format } = loadConfigFile(configFileLocation) - const { runtime, credentials } = config - - debug(`importConfigJson - format: ${format} config:${prettyPrintJson(config)} `) + const { valid: configIsValid, errors: configErrors } = validateConfig(config) - checkRules(config) + aioLogger.debug(`importConfigJson - format: ${format} config:${prettyPrintJson(config)} `) - // enrich credentials.jwt with ims org - if (typeof credentials.jwt === 'object' && !credentials.jwt.ims_org_id) { - // not checking for config.project && config.project.org as part of required rules - if (config.project.org.ims_org_id) { - debug('adding ims_org_id to $ims.jwt config') - credentials.jwt.ims_org_id = config.project.org.ims_org_id - } else { - const warning = 'missing project.org.ims_org_id, which is needed for ims jwt config' - debug('warn:', warning) - console.warn(warning) - } + if (!configIsValid) { + const message = `Missing or invalid keys in config: ${JSON.stringify(configErrors, null, 2)}` + throw new Error(message) } - await writeEnv({ runtime, $ims: credentials }, destinationFolder, flags) + const { runtime, credentials } = config.project.workspace.details + + await writeEnv({ + runtime: transformRuntime(runtime), + $ims: transformCredentials(credentials, config.project.org.ims_org_id) + }, destinationFolder, flags) // remove the credentials - delete config.runtime - delete config.credentials + delete config.project.workspace.details.runtime + delete config.project.workspace.details.credentials return writeAio(config, destinationFolder, flags) } module.exports = { + validateConfig, loadConfigFile, writeAio, writeEnv, diff --git a/test/__fixtures__/config.1.aio b/test/__fixtures__/config.1.aio deleted file mode 100644 index a1b9c484..00000000 --- a/test/__fixtures__/config.1.aio +++ /dev/null @@ -1,12 +0,0 @@ -{ - "id": "this is some data", - "name": "myname", - "app_url": "https://my.app.url/foo/bar", - "action_url": "https://my.action.url/foo/bar", - "project": { - "name": "projectname", - "org": { - "name": "orgname" - } - } -} \ No newline at end of file diff --git a/test/__fixtures__/config.1.env b/test/__fixtures__/config.1.env deleted file mode 100644 index b720fe45..00000000 --- a/test/__fixtures__/config.1.env +++ /dev/null @@ -1,11 +0,0 @@ -AIO_runtime_namespace=my-namespace -AIO_runtime_credentials=my-runtime-credentials -AIO_$ims_apikey_client__id=83723cc8e25e455fb5db1ad12cbf16e9 -AIO_$ims_oauth2_client__id=83723cc8e25e455fb5db1ad12cbf16e9 -AIO_$ims_oauth2_client__secret=XXXXXX -AIO_$ims_oauth2_redirect__uri=https://www.adobe.com -AIO_$ims_jwt_client__id=568430a7b22e4a1f993d03ed8283bb26 -AIO_$ims_jwt_client__secret=XXXXX -AIO_$ims_jwt_techacct=E03445FF5D81F8EA0A494034@techacct.adobe.com -AIO_$ims_jwt_meta__scopes=["ent_partners_sdk"] -AIO_$ims_jwt_private__key=["-----BEGIN PRIVATE KEY-----","XXXX...XXX","-----END PRIVATE KEY-----"] \ No newline at end of file diff --git a/test/__fixtures__/config.1.hjson b/test/__fixtures__/config.1.hjson deleted file mode 100644 index 9d60c5c1..00000000 --- a/test/__fixtures__/config.1.hjson +++ /dev/null @@ -1,38 +0,0 @@ -{ - // This is a hjson config file that is entirely valid - "id": "this is some data", - "name": "myname", - "app_url": "https://my.app.url/foo/bar", - "action_url": "https://my.action.url/foo/bar", - "project": { - "name": "projectname", - "org": { - "name": "orgname" - } - }, - "runtime": { - "namespace": "my-namespace", - "credentials": "my-runtime-credentials" - }, - "credentials": { - "apikey": { - "client_id": "83723cc8e25e455fb5db1ad12cbf16e9" - }, - "oauth2": { - "client_id": "83723cc8e25e455fb5db1ad12cbf16e9", - "client_secret": "XXXXXX", - "redirect_uri": "https://www.adobe.com" - }, - "jwt": { - "client_id": "568430a7b22e4a1f993d03ed8283bb26", - "client_secret": "XXXXX", - "techacct": "E03445FF5D81F8EA0A494034@techacct.adobe.com", - "meta_scopes": ["ent_partners_sdk"], - "private_key": [ - "-----BEGIN PRIVATE KEY-----", - "XXXX...XXX", - "-----END PRIVATE KEY-----" - ] - } - } -} diff --git a/test/__fixtures__/config.2.error.hjson b/test/__fixtures__/config.2.error.hjson deleted file mode 100644 index 52a1fe60..00000000 --- a/test/__fixtures__/config.2.error.hjson +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - key: 'name', - value: 'invalid-workspace-name' - }, - { - key: 'project.name', - value: 'invalid-project-name' - }, - { - key: 'project.org.name', - value: 'invalid-org-name' - } -] diff --git a/test/__fixtures__/config.2.hjson b/test/__fixtures__/config.2.hjson deleted file mode 100644 index a804a82c..00000000 --- a/test/__fixtures__/config.2.hjson +++ /dev/null @@ -1,12 +0,0 @@ -{ - "id": "this is some data", - "name": "invalid-workspace-name", // invalid - "app_url": "https://my.app.url/foo/bar", - "action_url": "http://foo.bar", - "project": { - "name": "invalid-project-name", // invalid - "org": { - "name": "invalid-org-name" // invalid - } - } -} diff --git a/test/__fixtures__/config.3.error.hjson b/test/__fixtures__/config.3.error.hjson deleted file mode 100644 index d5f85f29..00000000 --- a/test/__fixtures__/config.3.error.hjson +++ /dev/null @@ -1,22 +0,0 @@ -[ - { - key: 'name', - value: '' - }, - { - key: 'project.name', - value: '' - }, - { - key: 'project.org.name', - value: '' - }, - { - key: 'app_url', - value: '' - }, - { - key: 'action_url', - value: '' - } -] diff --git a/test/__fixtures__/config.3.hjson b/test/__fixtures__/config.3.hjson deleted file mode 100644 index 5f7e142a..00000000 --- a/test/__fixtures__/config.3.hjson +++ /dev/null @@ -1,4 +0,0 @@ -{ - "id": "this is some data" - // missing a lot of required keys! -} diff --git a/test/__fixtures__/config.4.error.hjson b/test/__fixtures__/config.4.error.hjson deleted file mode 100644 index 5e0a47e0..00000000 --- a/test/__fixtures__/config.4.error.hjson +++ /dev/null @@ -1,10 +0,0 @@ -[ - { - key: 'app_url', - value: 'xhttps://my.app.url/foo/bar' - }, - { - key: 'action_url', - value: 'http://a/s' - } -] diff --git a/test/__fixtures__/config.4.hjson b/test/__fixtures__/config.4.hjson deleted file mode 100644 index 4e84e990..00000000 --- a/test/__fixtures__/config.4.hjson +++ /dev/null @@ -1,12 +0,0 @@ -{ - "id": "this is some data", - "name": "myname", // valid - "app_url": "xhttps://my.app.url/foo/bar", // invalid - "action_url": "http://a/s", // invalid - "project": { - "name": "projectname", // valid - "org": { - "name": "orgname" // valid - } - } -} diff --git a/test/__fixtures__/config.5.error.hjson b/test/__fixtures__/config.5.error.hjson deleted file mode 100644 index 5b1d9da6..00000000 --- a/test/__fixtures__/config.5.error.hjson +++ /dev/null @@ -1,7 +0,0 @@ -[ - { - key: 'credentials.oauth2.redirect_uri', - notRequired: true, - value: 'bad://url' - } -] diff --git a/test/__fixtures__/config.5.hjson b/test/__fixtures__/config.5.hjson deleted file mode 100644 index 403d4474..00000000 --- a/test/__fixtures__/config.5.hjson +++ /dev/null @@ -1,19 +0,0 @@ -{ - "id": "this is some data", - "name": "myname", // valid - "app_url": "https://my.app.url/foo/bar", // valid - "action_url": "http://foo.bar", // valid - "project": { - "name": "projectname", // valid - "org": { - "name": "orgname" // valid - } - }, - "credentials": { - "oauth2": { - "client_id": "83723cc8e25e455fb5db1ad12cbf16e9", - "client_secret": "XXXXXX", - "redirect_uri": "bad://url" // invalid credentials.oauth2.redirect_uri, since it is set, we validate it - } - } -} diff --git a/test/__fixtures__/config.6.orgid.aio b/test/__fixtures__/config.6.orgid.aio deleted file mode 100644 index 477b7961..00000000 --- a/test/__fixtures__/config.6.orgid.aio +++ /dev/null @@ -1,13 +0,0 @@ -{ - "id": "this is some data", - "name": "myname", - "app_url": "https://my.app.url/foo/bar", - "action_url": "https://my.action.url/foo/bar", - "project": { - "name": "projectname", - "org": { - "name": "orgname", - "ims_org_id": "abcde@AdobeOrg" - } - } -} \ No newline at end of file diff --git a/test/__fixtures__/config.6.orgid.env b/test/__fixtures__/config.6.orgid.env deleted file mode 100644 index 85484815..00000000 --- a/test/__fixtures__/config.6.orgid.env +++ /dev/null @@ -1,12 +0,0 @@ -AIO_runtime_namespace=my-namespace -AIO_runtime_credentials=my-runtime-credentials -AIO_$ims_apikey_client__id=83723cc8e25e455fb5db1ad12cbf16e9 -AIO_$ims_oauth2_client__id=83723cc8e25e455fb5db1ad12cbf16e9 -AIO_$ims_oauth2_client__secret=XXXXXX -AIO_$ims_oauth2_redirect__uri=https://www.adobe.com -AIO_$ims_jwt_client__id=568430a7b22e4a1f993d03ed8283bb26 -AIO_$ims_jwt_client__secret=XXXXX -AIO_$ims_jwt_techacct=E03445FF5D81F8EA0A494034@techacct.adobe.com -AIO_$ims_jwt_meta__scopes=["ent_partners_sdk"] -AIO_$ims_jwt_private__key=["-----BEGIN PRIVATE KEY-----","XXXX...XXX","-----END PRIVATE KEY-----"] -AIO_$ims_jwt_ims__org__id=abcde@AdobeOrg \ No newline at end of file diff --git a/test/__fixtures__/config.6.orgid.hjson b/test/__fixtures__/config.6.orgid.hjson deleted file mode 100644 index 1dc0ba8d..00000000 --- a/test/__fixtures__/config.6.orgid.hjson +++ /dev/null @@ -1,39 +0,0 @@ -{ - // This is a hjson config file that is entirely valid - "id": "this is some data", - "name": "myname", - "app_url": "https://my.app.url/foo/bar", - "action_url": "https://my.action.url/foo/bar", - "project": { - "name": "projectname", - "org": { - "name": "orgname", - "ims_org_id": "abcde@AdobeOrg" - } - }, - "runtime": { - "namespace": "my-namespace", - "credentials": "my-runtime-credentials" - }, - "credentials": { - "apikey": { - "client_id": "83723cc8e25e455fb5db1ad12cbf16e9" - }, - "oauth2": { - "client_id": "83723cc8e25e455fb5db1ad12cbf16e9", - "client_secret": "XXXXXX", - "redirect_uri": "https://www.adobe.com" - }, - "jwt": { - "client_id": "568430a7b22e4a1f993d03ed8283bb26", - "client_secret": "XXXXX", - "techacct": "E03445FF5D81F8EA0A494034@techacct.adobe.com", - "meta_scopes": ["ent_partners_sdk"], - "private_key": [ - "-----BEGIN PRIVATE KEY-----", - "XXXX...XXX", - "-----END PRIVATE KEY-----" - ] - } - } -} diff --git a/test/__fixtures__/config.6.orgid.no.jwt.aio b/test/__fixtures__/config.6.orgid.no.jwt.aio deleted file mode 100644 index 477b7961..00000000 --- a/test/__fixtures__/config.6.orgid.no.jwt.aio +++ /dev/null @@ -1,13 +0,0 @@ -{ - "id": "this is some data", - "name": "myname", - "app_url": "https://my.app.url/foo/bar", - "action_url": "https://my.action.url/foo/bar", - "project": { - "name": "projectname", - "org": { - "name": "orgname", - "ims_org_id": "abcde@AdobeOrg" - } - } -} \ No newline at end of file diff --git a/test/__fixtures__/config.6.orgid.no.jwt.env b/test/__fixtures__/config.6.orgid.no.jwt.env deleted file mode 100644 index b2eef391..00000000 --- a/test/__fixtures__/config.6.orgid.no.jwt.env +++ /dev/null @@ -1,6 +0,0 @@ -AIO_runtime_namespace=my-namespace -AIO_runtime_credentials=my-runtime-credentials -AIO_$ims_apikey_client__id=83723cc8e25e455fb5db1ad12cbf16e9 -AIO_$ims_oauth2_client__id=83723cc8e25e455fb5db1ad12cbf16e9 -AIO_$ims_oauth2_client__secret=XXXXXX -AIO_$ims_oauth2_redirect__uri=https://www.adobe.com \ No newline at end of file diff --git a/test/__fixtures__/config.6.orgid.no.jwt.hjson b/test/__fixtures__/config.6.orgid.no.jwt.hjson deleted file mode 100644 index 6186de2a..00000000 --- a/test/__fixtures__/config.6.orgid.no.jwt.hjson +++ /dev/null @@ -1,28 +0,0 @@ -{ - // This is a hjson config file that is entirely valid - "id": "this is some data", - "name": "myname", - "app_url": "https://my.app.url/foo/bar", - "action_url": "https://my.action.url/foo/bar", - "project": { - "name": "projectname", - "org": { - "name": "orgname", - "ims_org_id": "abcde@AdobeOrg" - } - }, - "runtime": { - "namespace": "my-namespace", - "credentials": "my-runtime-credentials" - }, - "credentials": { - "apikey": { - "client_id": "83723cc8e25e455fb5db1ad12cbf16e9" - }, - "oauth2": { - "client_id": "83723cc8e25e455fb5db1ad12cbf16e9", - "client_secret": "XXXXXX", - "redirect_uri": "https://www.adobe.com" - } - } -} diff --git a/test/__fixtures__/config.orgid.aio b/test/__fixtures__/config.orgid.aio new file mode 100644 index 00000000..bb68d980 --- /dev/null +++ b/test/__fixtures__/config.orgid.aio @@ -0,0 +1,24 @@ +{ + "project": { + "id": "123", + "name": "TestProject", + "title": "Test Title", + "description": "My project description", + "org": { + "id": "25591412", + "name": "Developers", + "ims_org_id": "XOXOXOXOXOXOX@AdobeOrg" + }, + "workspace": { + "id": "1356", + "name": "TestWorkspace", + "title": "Test Title", + "description": "My workspace description", + "action_url": "https://ABCD-TestProject-TestWorkspace.adobeioruntime.net", + "app_url": "https://ABCD-TestProject-TestWorkspace.adobestatic.net", + "details": { + "services": [] + } + } + } +} \ No newline at end of file diff --git a/test/__fixtures__/config.orgid.env b/test/__fixtures__/config.orgid.env new file mode 100644 index 00000000..6815393d --- /dev/null +++ b/test/__fixtures__/config.orgid.env @@ -0,0 +1,8 @@ +AIO_$ims_ProjectB_client__id=XUXUXUXUXUXUXUX +AIO_$ims_ProjectB_client__secret=XPXPXPXPXPXPXPXPX +AIO_$ims_ProjectB_techacct=XTXTXTXTXTX@techacct.adobe.com +AIO_$ims_ProjectB_meta__scopes=["ent_smartcontent_sdk","ent_adobeio_sdk"] +AIO_$ims_ProjectB_ims__org__id=XOXOXOXOXOXOX@AdobeOrg +AIO_$ims_NewTestIntegration8_client__id=XRXRXRXRXRXRXRXRXR +AIO_$ims_NewTestIntegration8_client__secret=XRXRXRXRXRXRXRXRXRX +AIO_$ims_NewTestIntegration8_redirect__uri=ams:\\+TRTRTRTRTRTR://adobeid/XWXWXWXWXWXWXWX \ No newline at end of file diff --git a/test/__fixtures__/config.orgid.hjson b/test/__fixtures__/config.orgid.hjson new file mode 100644 index 00000000..74d34d9a --- /dev/null +++ b/test/__fixtures__/config.orgid.hjson @@ -0,0 +1,55 @@ +{ + "project": { + "id": "123", + "name": "TestProject", + "title": "Test Title", + "description": "My project description", + "org": { + "id": "25591412", + "name": "Developers", + "ims_org_id": "XOXOXOXOXOXOX@AdobeOrg" + }, + "workspace": { + "id": "1356", + "name": "TestWorkspace", + "title": "Test Title", + "description": "My workspace description", + "action_url": "https://ABCD-TestProject-TestWorkspace.adobeioruntime.net", + "app_url": "https://ABCD-TestProject-TestWorkspace.adobestatic.net", + "details": { + "credentials": [ + { + "id": "17606512", + "name": "ProjectB", + "integration_type": "service", + "jwt": { + "client_id": "XUXUXUXUXUXUXUX", + "client_secret": "XPXPXPXPXPXPXPXPX", + "techacct": "XTXTXTXTXTX@techacct.adobe.com", + "meta_scopes": [ + "ent_smartcontent_sdk", + "ent_adobeio_sdk" + ] + } + }, + { + "id": "17950", + "name": "NewTestIntegration8", + "integration_type": "oauthandroid", + "oauth2": { + "client_id": "XRXRXRXRXRXRXRXRXR", + "client_secret": "XRXRXRXRXRXRXRXRXRX", + "redirect_uri": "ams:\\\\+TRTRTRTRTRTR://adobeid/XWXWXWXWXWXWXWX" + } + } + ], + "services": [ + ], + "runtime": { + "namespaces": [ + ] + } + } + } + } +} \ No newline at end of file diff --git a/test/__fixtures__/config.orgid.no.jwt.aio b/test/__fixtures__/config.orgid.no.jwt.aio new file mode 100644 index 00000000..bb68d980 --- /dev/null +++ b/test/__fixtures__/config.orgid.no.jwt.aio @@ -0,0 +1,24 @@ +{ + "project": { + "id": "123", + "name": "TestProject", + "title": "Test Title", + "description": "My project description", + "org": { + "id": "25591412", + "name": "Developers", + "ims_org_id": "XOXOXOXOXOXOX@AdobeOrg" + }, + "workspace": { + "id": "1356", + "name": "TestWorkspace", + "title": "Test Title", + "description": "My workspace description", + "action_url": "https://ABCD-TestProject-TestWorkspace.adobeioruntime.net", + "app_url": "https://ABCD-TestProject-TestWorkspace.adobestatic.net", + "details": { + "services": [] + } + } + } +} \ No newline at end of file diff --git a/test/__fixtures__/config.orgid.no.jwt.env b/test/__fixtures__/config.orgid.no.jwt.env new file mode 100644 index 00000000..e329b7de --- /dev/null +++ b/test/__fixtures__/config.orgid.no.jwt.env @@ -0,0 +1,3 @@ +AIO_$ims_NewTestIntegration8_client__id=XRXRXRXRXRXRXRXRXR +AIO_$ims_NewTestIntegration8_client__secret=XRXRXRXRXRXRXRXRXRX +AIO_$ims_NewTestIntegration8_redirect__uri=ams:\\+TRTRTRTRTRTR://adobeid/XWXWXWXWXWXWXWX \ No newline at end of file diff --git a/test/__fixtures__/config.orgid.no.jwt.hjson b/test/__fixtures__/config.orgid.no.jwt.hjson new file mode 100644 index 00000000..83a5d967 --- /dev/null +++ b/test/__fixtures__/config.orgid.no.jwt.hjson @@ -0,0 +1,41 @@ +{ + "project": { + "id": "123", + "name": "TestProject", + "title": "Test Title", + "description": "My project description", + "org": { + "id": "25591412", + "name": "Developers", + "ims_org_id": "XOXOXOXOXOXOX@AdobeOrg" + }, + "workspace": { + "id": "1356", + "name": "TestWorkspace", + "title": "Test Title", + "description": "My workspace description", + "action_url": "https://ABCD-TestProject-TestWorkspace.adobeioruntime.net", + "app_url": "https://ABCD-TestProject-TestWorkspace.adobestatic.net", + "details": { + "credentials": [ + { + "id": "17950", + "name": "NewTestIntegration8", + "integration_type": "oauthandroid", + "oauth2": { + "client_id": "XRXRXRXRXRXRXRXRXR", + "client_secret": "XRXRXRXRXRXRXRXRXRX", + "redirect_uri": "ams:\\\\+TRTRTRTRTRTR://adobeid/XWXWXWXWXWXWXWX" + } + } + ], + "services": [ + ], + "runtime": { + "namespaces": [ + ] + } + } + } + } +} \ No newline at end of file diff --git a/test/__fixtures__/existing.aio b/test/__fixtures__/existing.aio index 47e88bac..cb96b8ed 100644 --- a/test/__fixtures__/existing.aio +++ b/test/__fixtures__/existing.aio @@ -1,5 +1,5 @@ { - "id": "this should be overwritten", + "id": "this should not be overwritten", "this_is_a_merged_key": "this is a merged value", "some": { "data1": "value1", diff --git a/test/__fixtures__/existing.env b/test/__fixtures__/existing.env index 09fe7ab0..7b9f7248 100644 --- a/test/__fixtures__/existing.env +++ b/test/__fixtures__/existing.env @@ -2,6 +2,6 @@ # AIO_bla=bloo AIO_foo_bar=baz AIO_boo=faz +AIO_runtime_auth=this should be overwritten AIO_runtime_namespace=this should be overwritten -AIO_runtime_credentials=this should be overwritten badData \ No newline at end of file diff --git a/test/__fixtures__/existing.merged.aio b/test/__fixtures__/existing.merged.aio index 944c5e39..5499d54f 100644 --- a/test/__fixtures__/existing.merged.aio +++ b/test/__fixtures__/existing.merged.aio @@ -1,5 +1,5 @@ { - "id": "this is some data", + "id": "this should not be overwritten", "this_is_a_merged_key": "this is a merged value", "some": { "data1": "value1", @@ -8,12 +8,34 @@ } }, "project": { - "name": "projectname", + "id": "123", + "name": "TestProject", + "title": "Test Title", + "description": "My project description", "org": { - "name": "orgname" + "id": "25591412", + "name": "Developers", + "ims_org_id": "XOXOXOXOXOXOX@AdobeOrg" + }, + "workspace": { + "id": "1356", + "name": "TestWorkspace", + "title": "Test Title", + "description": "My workspace description", + "action_url": "https://ABCD-TestProject-TestWorkspace.adobeioruntime.net", + "app_url": "https://ABCD-TestProject-TestWorkspace.adobestatic.net", + "details": { + "services": [ + { + "code": "AdobeIOManagementAPISDK", + "name": "I/O Management API" + }, + { + "code": "CC SDK", + "name": "Creative SDK" + } + ] + } } - }, - "name": "myname", - "app_url": "https://my.app.url/foo/bar", - "action_url": "https://my.action.url/foo/bar" + } } \ No newline at end of file diff --git a/test/__fixtures__/existing.merged.env b/test/__fixtures__/existing.merged.env index 1b1a37b2..5cbc39f2 100644 --- a/test/__fixtures__/existing.merged.env +++ b/test/__fixtures__/existing.merged.env @@ -1,13 +1,15 @@ AIO_foo_bar=baz AIO_boo=faz -AIO_runtime_namespace=my-namespace -AIO_runtime_credentials=my-runtime-credentials -AIO_$ims_apikey_client__id=83723cc8e25e455fb5db1ad12cbf16e9 -AIO_$ims_oauth2_client__id=83723cc8e25e455fb5db1ad12cbf16e9 -AIO_$ims_oauth2_client__secret=XXXXXX -AIO_$ims_oauth2_redirect__uri=https://www.adobe.com -AIO_$ims_jwt_client__id=568430a7b22e4a1f993d03ed8283bb26 -AIO_$ims_jwt_client__secret=XXXXX -AIO_$ims_jwt_techacct=E03445FF5D81F8EA0A494034@techacct.adobe.com -AIO_$ims_jwt_meta__scopes=["ent_partners_sdk"] -AIO_$ims_jwt_private__key=["-----BEGIN PRIVATE KEY-----","XXXX...XXX","-----END PRIVATE KEY-----"] \ No newline at end of file +AIO_runtime_auth=Auth +AIO_runtime_namespace=Name +AIO_$ims_ProjectA_client__id=XYXYXYXYXYXYXYXYX +AIO_$ims_ProjectA_client__secret=XYXYXYXYZZZZZZ +AIO_$ims_ProjectA_redirect__uri=https://test123 +AIO_$ims_ProjectB_client__id=XUXUXUXUXUXUXUX +AIO_$ims_ProjectB_client__secret=XPXPXPXPXPXPXPXPX +AIO_$ims_ProjectB_techacct=XTXTXTXTXTX@techacct.adobe.com +AIO_$ims_ProjectB_meta__scopes=["ent_smartcontent_sdk","ent_adobeio_sdk"] +AIO_$ims_ProjectB_ims__org__id=XOXOXOXOXOXOX@AdobeOrg +AIO_$ims_NewTestIntegration8_client__id=XRXRXRXRXRXRXRXRXR +AIO_$ims_NewTestIntegration8_client__secret=XRXRXRXRXRXRXRXRXRX +AIO_$ims_NewTestIntegration8_redirect__uri=ams:\\+TRTRTRTRTRTR://adobeid/XWXWXWXWXWXWXWX \ No newline at end of file diff --git a/test/__fixtures__/invalid.config.json b/test/__fixtures__/invalid.config.json new file mode 100644 index 00000000..979987ff --- /dev/null +++ b/test/__fixtures__/invalid.config.json @@ -0,0 +1,75 @@ +{ + "project": { + "id": "123", + "name": "TestProject", + "title": "Test Title", + "description": "My project description", + "org": { + "id": "25591412", + "ims_org_id": "XOXOXOXOXOXOX@AdobeOrg" + }, + "workspace": { + "id": "1356", + "name": "TestWorkspace", + "title": "Test Title", + "description": "My workspace description", + "action_url": "https://ABCD-TestProject-TestWorkspace.adobeioruntime.net", + "app_url": "https://ABCD-TestProject-TestWorkspace.adobestatic.net", + "details": { + "credentials": [ + { + "id": "17561142", + "integration_type": "oauthweb", + "oauth2": { + "client_id": "XYXYXYXYXYXYXYXYX", + "client_secret": "XYXYXYXYZZZZZZ", + "redirect_uri": "https://test123" + } + }, + { + "id": "17606512", + "name": "Project1583191213473J", + "integration_type": "service", + "jwt": { + "client_id": "XUXUXUXUXUXUXUX", + "client_secret": "XPXPXPXPXPXPXPXPX", + "techacct": "XTXTXTXTXTX@techacct.adobe.com", + "meta_scopes": [ + "ent_smartcontent_sdk", + "ent_adobeio_sdk" + ] + } + }, + { + "id": "17950", + "name": "NewTestIntegration8", + "integration_type": "oauthandroid", + "oauth2": { + "client_id": "XRXRXRXRXRXRXRXRXR", + "client_secret": "XRXRXRXRXRXRXRXRXRX", + "redirect_uri": "ams:\\\\+TRTRTRTRTRTR://adobeid/XWXWXWXWXWXWXWX" + } + } + ], + "services": [ + { + "code": "AdobeIOManagementAPISDK", + "name": "I/O Management API" + }, + { + "code": "CC SDK", + "name": "Creative SDK" + } + ], + "runtime": { + "namespaces": [ + { + "name": "Name", + "auth": "Auth" + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/test/__fixtures__/valid.config.aio b/test/__fixtures__/valid.config.aio new file mode 100644 index 00000000..7df881ae --- /dev/null +++ b/test/__fixtures__/valid.config.aio @@ -0,0 +1,33 @@ +{ + "project": { + "id": "123", + "name": "TestProject", + "title": "Test Title", + "description": "My project description", + "org": { + "id": "25591412", + "name": "Developers", + "ims_org_id": "XOXOXOXOXOXOX@AdobeOrg" + }, + "workspace": { + "id": "1356", + "name": "TestWorkspace", + "title": "Test Title", + "description": "My workspace description", + "action_url": "https://ABCD-TestProject-TestWorkspace.adobeioruntime.net", + "app_url": "https://ABCD-TestProject-TestWorkspace.adobestatic.net", + "details": { + "services": [ + { + "code": "AdobeIOManagementAPISDK", + "name": "I/O Management API" + }, + { + "code": "CC SDK", + "name": "Creative SDK" + } + ] + } + } + } +} \ No newline at end of file diff --git a/test/__fixtures__/valid.config.env b/test/__fixtures__/valid.config.env new file mode 100644 index 00000000..871c69a9 --- /dev/null +++ b/test/__fixtures__/valid.config.env @@ -0,0 +1,13 @@ +AIO_runtime_auth=Auth +AIO_runtime_namespace=Name +AIO_$ims_ProjectA_client__id=XYXYXYXYXYXYXYXYX +AIO_$ims_ProjectA_client__secret=XYXYXYXYZZZZZZ +AIO_$ims_ProjectA_redirect__uri=https://test123 +AIO_$ims_ProjectB_client__id=XUXUXUXUXUXUXUX +AIO_$ims_ProjectB_client__secret=XPXPXPXPXPXPXPXPX +AIO_$ims_ProjectB_techacct=XTXTXTXTXTX@techacct.adobe.com +AIO_$ims_ProjectB_meta__scopes=["ent_smartcontent_sdk","ent_adobeio_sdk"] +AIO_$ims_ProjectB_ims__org__id=XOXOXOXOXOXOX@AdobeOrg +AIO_$ims_NewTestIntegration8_client__id=XRXRXRXRXRXRXRXRXR +AIO_$ims_NewTestIntegration8_client__secret=XRXRXRXRXRXRXRXRXRX +AIO_$ims_NewTestIntegration8_redirect__uri=ams:\\+TRTRTRTRTRTR://adobeid/XWXWXWXWXWXWXWX \ No newline at end of file diff --git a/test/__fixtures__/valid.config.json b/test/__fixtures__/valid.config.json new file mode 100644 index 00000000..be4bd652 --- /dev/null +++ b/test/__fixtures__/valid.config.json @@ -0,0 +1,77 @@ +{ + "project": { + "id": "123", + "name": "TestProject", + "title": "Test Title", + "description": "My project description", + "org": { + "id": "25591412", + "name": "Developers", + "ims_org_id": "XOXOXOXOXOXOX@AdobeOrg" + }, + "workspace": { + "id": "1356", + "name": "TestWorkspace", + "title": "Test Title", + "description": "My workspace description", + "action_url": "https://ABCD-TestProject-TestWorkspace.adobeioruntime.net", + "app_url": "https://ABCD-TestProject-TestWorkspace.adobestatic.net", + "details": { + "credentials": [ + { + "id": "17561142", + "name": "ProjectA", + "integration_type": "oauthweb", + "oauth2": { + "client_id": "XYXYXYXYXYXYXYXYX", + "client_secret": "XYXYXYXYZZZZZZ", + "redirect_uri": "https://test123" + } + }, + { + "id": "17606512", + "name": "ProjectB", + "integration_type": "service", + "jwt": { + "client_id": "XUXUXUXUXUXUXUX", + "client_secret": "XPXPXPXPXPXPXPXPX", + "techacct": "XTXTXTXTXTX@techacct.adobe.com", + "meta_scopes": [ + "ent_smartcontent_sdk", + "ent_adobeio_sdk" + ] + } + }, + { + "id": "17950", + "name": "NewTestIntegration8", + "integration_type": "oauthandroid", + "oauth2": { + "client_id": "XRXRXRXRXRXRXRXRXR", + "client_secret": "XRXRXRXRXRXRXRXRXRX", + "redirect_uri": "ams:\\\\+TRTRTRTRTRTR://adobeid/XWXWXWXWXWXWXWX" + } + } + ], + "services": [ + { + "code": "AdobeIOManagementAPISDK", + "name": "I/O Management API" + }, + { + "code": "CC SDK", + "name": "Creative SDK" + } + ], + "runtime": { + "namespaces": [ + { + "name": "Name", + "auth": "Auth" + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/test/commands/app/init.test.js b/test/commands/app/init.test.js index 2839ef31..a8db7233 100644 --- a/test/commands/app/init.test.js +++ b/test/commands/app/init.test.js @@ -217,14 +217,40 @@ describe('run', () => { ) }) + test('no-path --import file=invalid config', async () => { + // mock config file + importLib.loadConfigFile.mockReturnValue({ + values: { + foo: { + bar: 'yolo' + } + } + }) + importLib.validateConfig.mockReturnValue({ + valid: false + }) + + await expect(TheCommand.run(['--import', 'config.json'])).rejects.toThrow('Missing or invalid keys in config:') + }) + test('no-path --import file={name: yolo, services:AdobeTargetSDK,CampaignSDK}', async () => { // mock config file - importLib.loadConfigFile.mockReturnValueOnce({ + importLib.loadConfigFile.mockReturnValue({ values: { - name: 'yolo', - services: [{ code: 'AdobeTargetSDK' }, { code: 'CampaignSDK' }] + project: { + name: 'yolo', + workspace: { + details: { + services: [{ code: 'AdobeTargetSDK' }, { code: 'CampaignSDK' }] + } + } + } } }) + importLib.validateConfig.mockReturnValue({ + valid: true + }) + await TheCommand.run(['--import', 'config.json']) // no args.path @@ -246,12 +272,21 @@ describe('run', () => { test('no-path --yes --import file={name: yolo, services:AdobeTargetSDK,CampaignSDK}', async () => { // mock config file - importLib.loadConfigFile.mockReturnValueOnce({ + importLib.loadConfigFile.mockReturnValue({ values: { - name: 'yolo', - services: [{ code: 'AdobeTargetSDK' }, { code: 'CampaignSDK' }] + project: { + name: 'yolo', + workspace: { + details: { + services: [{ code: 'AdobeTargetSDK' }, { code: 'CampaignSDK' }] + } + } + } } }) + importLib.validateConfig.mockReturnValue({ + valid: true + }) await TheCommand.run(['--yes', '--import', 'config.json']) // no args.path @@ -273,11 +308,21 @@ describe('run', () => { test('some-path --import file={name: yolo, services:undefined}', async () => { // mock config file - importLib.loadConfigFile.mockReturnValueOnce({ + importLib.loadConfigFile.mockReturnValue({ values: { - name: 'yolo' + project: { + name: 'yolo', + workspace: { + details: { + services: [] + } + } + } } }) + importLib.validateConfig.mockReturnValue({ + valid: true + }) await TheCommand.run(['some-path', '--import', 'config.json']) // no args.path diff --git a/test/commands/lib/import.test.js b/test/commands/lib/import.test.js index 045c8fc3..938c9b7a 100644 --- a/test/commands/lib/import.test.js +++ b/test/commands/lib/import.test.js @@ -94,24 +94,32 @@ test('writeEnv', async () => { return expect(fs.writeFile).toHaveBeenCalledTimes(2) }) +test('invalid config', async () => { + const workingFolder = 'my-working-folder' + const configPath = '/some/config/path' + + fs.readFileSync.mockReturnValueOnce(fixtureFile('invalid.config.json')) + return expect(importConfigJson(configPath, workingFolder, { overwrite: true })).rejects.toThrow('Missing or invalid keys in config:') +}) + test('importConfigJson', async () => { const workingFolder = 'my-working-folder' const aioPath = path.join(workingFolder, '.aio') const envPath = path.join(workingFolder, '.env') const configPath = '/some/config/path' - fs.readFileSync.mockReturnValueOnce(fixtureFile('config.1.hjson')) + fs.readFileSync.mockReturnValueOnce(fixtureFile('valid.config.json')) await importConfigJson(configPath, workingFolder, { overwrite: true }) await expect(fs.writeFile.mock.calls[0][0]).toMatch(envPath) - await expect(fs.writeFile.mock.calls[0][1]).toMatchFixture('config.1.env') + await expect(fs.writeFile.mock.calls[0][1]).toMatchFixture('valid.config.env') await expect(fs.writeFile.mock.calls[0][2]).toMatchObject({ flag: 'w' }) await expect(fs.writeFile.mock.calls[1][0]).toMatch(aioPath) - await expect(fs.writeFile.mock.calls[1][1]).toMatchFixture('config.1.aio') + await expect(fs.writeFile.mock.calls[1][1]).toMatchFixture('valid.config.aio') await expect(fs.writeFile.mock.calls[1][2]).toMatchObject({ flag: 'w' }) - fs.readFileSync.mockReturnValueOnce(fixtureFile('config.1.hjson')) + fs.readFileSync.mockReturnValueOnce(fixtureFile('valid.config.json')) await importConfigJson(configPath) // for coverage (defaults), no overwrite await expect(fs.writeFile.mock.calls[2][2]).toMatchObject({ flag: 'wx' }) await expect(fs.writeFile.mock.calls[3][2]).toMatchObject({ flag: 'wx' }) @@ -155,7 +163,7 @@ test('importConfigJson - interactive (merge)', async () => { fs.readFileSync.mockImplementation((source) => { switch (source) { case configPath: - return fixtureFile('config.1.hjson') + return fixtureFile('valid.config.json') case envPath: return fixtureFile('existing.env') case aioPath: @@ -185,15 +193,24 @@ test('importConfigJson - interactive (merge)', async () => { test('importConfigJson - interactive', async () => { const workingFolder = 'my-working-folder' const configPath = '/some/config/path' - const configJson = fixtureFile('config.1.hjson') + const configJson = fixtureFile('valid.config.json') fs.existsSync.mockReturnValue(true) // there is a write conflict - fs.readFileSync.mockReturnValueOnce(configJson) + fs.readFileSync.mockImplementation((source) => { + switch (source) { + case configPath: + return configJson + } + + throw new Error(`File ${source} not found.`) + }) + + fs.readFileSync.mockReturnValue(configJson) + inquirer.prompt.mockResolvedValue({ conflict: 'abort' }) // no writes await importConfigJson(configPath, workingFolder, { interactive: true }) - fs.readFileSync.mockReturnValueOnce(configJson) inquirer.prompt.mockResolvedValue({ conflict: 'merge' }) // two writes await importConfigJson(configPath, workingFolder, { interactive: true }) @@ -212,51 +229,19 @@ test('importConfigJson - interactive', async () => { await expect(fs.writeFile).toHaveBeenCalledTimes(8) }) -test('enforce alphanumeric content rule - name, project.name, project.org.name invalid', async () => { - fs.readFileSync.mockReturnValueOnce(fixtureFile('config.2.hjson')) - const invalid = fixtureHjson('config.2.error.hjson') - await expect(importConfigJson('/some/config/path')).rejects.toThrow(`Missing or invalid keys in config: ${JSON.stringify(invalid)}`) - - await expect(fs.writeFile).toHaveBeenCalledTimes(0) -}) - -test('enforce alphanumeric content rule - missing all keys (undefined)', async () => { - fs.readFileSync.mockReturnValueOnce(fixtureFile('config.3.hjson')) // for coverage (missing keys) - const invalid = fixtureHjson('config.3.error.hjson') - await expect(importConfigJson('/some/config/path')).rejects.toThrow(`Missing or invalid keys in config: ${JSON.stringify(invalid)}`) - - await expect(fs.writeFile).toHaveBeenCalledTimes(0) -}) - -test('enforce alphanumeric content rule - app_url, action_url are both invalid', async () => { - fs.readFileSync.mockReturnValueOnce(fixtureFile('config.4.hjson')) // invalid urls - const invalid = fixtureHjson('config.4.error.hjson') - await expect(importConfigJson('/some/config/path')).rejects.toThrow(`Missing or invalid keys in config: ${JSON.stringify(invalid)}`) - - await expect(fs.writeFile).toHaveBeenCalledTimes(0) -}) - -test('enforce alphanumeric content rule - credentials.oauth2.redirect_uri set and invalid', async () => { - fs.readFileSync.mockReturnValueOnce(fixtureFile('config.5.hjson')) // invalid url (credentials.oauth2.redirect_uri) - const invalid = fixtureHjson('config.5.error.hjson') - await expect(importConfigJson('/some/config/path')).rejects.toThrow(`Missing or invalid keys in config: ${JSON.stringify(invalid)}`) - - await expect(fs.writeFile).toHaveBeenCalledTimes(0) -}) - -test('enrich $ims.jwt with project.org.ims_org_id', async () => { +test('enrich $ims jwt credential with project.org.ims_org_id', async () => { const workingFolder = 'my-working-folder' const aioPath = path.join(workingFolder, '.aio') const envPath = path.join(workingFolder, '.env') const configPath = '/some/config/path' - fs.readFileSync.mockReturnValueOnce(fixtureFile('config.6.orgid.hjson')) + fs.readFileSync.mockReturnValueOnce(fixtureFile('config.orgid.hjson')) await importConfigJson(configPath, workingFolder, { overwrite: true }) await expect(fs.writeFile.mock.calls[0][0]).toMatch(envPath) - await expect(fs.writeFile.mock.calls[0][1]).toMatchFixture('config.6.orgid.env') + await expect(fs.writeFile.mock.calls[0][1]).toMatchFixture('config.orgid.env') await expect(fs.writeFile.mock.calls[0][2]).toMatchObject({ flag: 'w' }) await expect(fs.writeFile.mock.calls[1][0]).toMatch(aioPath) - await expect(fs.writeFile.mock.calls[1][1]).toMatchFixture('config.6.orgid.aio') + await expect(fs.writeFile.mock.calls[1][1]).toMatchFixture('config.orgid.aio') await expect(fs.writeFile.mock.calls[1][2]).toMatchObject({ flag: 'w' }) expect(fs.writeFile).toHaveBeenCalledTimes(2) @@ -268,13 +253,13 @@ test('do not enrich $ims.jwt with ims_org_id if no jwt credentials defined ', as const envPath = path.join(workingFolder, '.env') const configPath = '/some/config/path' - fs.readFileSync.mockReturnValueOnce(fixtureFile('config.6.orgid.no.jwt.hjson')) + fs.readFileSync.mockReturnValueOnce(fixtureFile('config.orgid.no.jwt.hjson')) await importConfigJson(configPath, workingFolder, { overwrite: true }) await expect(fs.writeFile.mock.calls[0][0]).toMatch(envPath) - await expect(fs.writeFile.mock.calls[0][1]).toMatchFixture('config.6.orgid.no.jwt.env') + await expect(fs.writeFile.mock.calls[0][1]).toMatchFixture('config.orgid.no.jwt.env') await expect(fs.writeFile.mock.calls[0][2]).toMatchObject({ flag: 'w' }) await expect(fs.writeFile.mock.calls[1][0]).toMatch(aioPath) - await expect(fs.writeFile.mock.calls[1][1]).toMatchFixture('config.6.orgid.no.jwt.aio') + await expect(fs.writeFile.mock.calls[1][1]).toMatchFixture('config.orgid.no.jwt.aio') await expect(fs.writeFile.mock.calls[1][2]).toMatchObject({ flag: 'w' }) expect(fs.writeFile).toHaveBeenCalledTimes(2) }) diff --git a/test/schema.test.js b/test/schema.test.js new file mode 100644 index 00000000..9203c2d5 --- /dev/null +++ b/test/schema.test.js @@ -0,0 +1,19 @@ +const Ajv = require('ajv') +const schema = require('../schema/config.schema.json') + +test('validate success', () => { + const ajv = new Ajv({ allErrors: true }) + const validate = ajv.compile(schema) + const valid = validate(fixtureJson('valid.config.json')) + expect(validate.errors).toEqual(null) + expect(valid).toBeTruthy() +}) + +test('validate failure', () => { + const ajv = new Ajv({ allErrors: true }) + const validate = ajv.compile(schema) + const valid = validate(fixtureJson('invalid.config.json')) + // the two errors are the missing name properties + expect(validate.errors.length).toEqual(2) + expect(valid).toBeFalsy() +})