Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/commands/app/console/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
Copyright 2026 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.
*/

const { Help } = require('@oclif/core')
const BaseCommand = require('../../../BaseCommand')

class ConsoleCommand extends BaseCommand {
async run () {
const help = new Help(this.config)
await help.showHelp(['app:console', '--help'])
}
}

ConsoleCommand.description = 'Display Adobe Developer Console org/project/workspace configuration for this app'

ConsoleCommand.args = {}

module.exports = ConsoleCommand
42 changes: 42 additions & 0 deletions src/commands/app/console/open.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
Copyright 2026 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.
*/

const open = require('open')
const { getCliEnv } = require('@adobe/aio-lib-env')
const BaseCommand = require('../../../BaseCommand')
const { loadCurrentConfiguration, hasLocalConfiguration } = require('../../../lib/console-helper')
const { OPEN_URLS } = require('../../../lib/defaults')

class OpenCommand extends BaseCommand {
async run () {
await this.parse(OpenCommand)

if (!hasLocalConfiguration()) {
this.error('This app is not set to use any Org/Project/Workspace yet. Run `aio app use` to configure it.')
}

const { org, project, workspace } = loadCurrentConfiguration('local')

let url = `${OPEN_URLS[getCliEnv()]}/${org.id}/${project.id}/`
url += workspace.id ? `workspaces/${workspace.id}/details` : 'overview'
await open(url)
}
}

OpenCommand.description = 'Open the Adobe Developer Console workspace this app is set to use (as set by `aio app use`) in the default web browser'

OpenCommand.flags = {
...BaseCommand.flags
}

OpenCommand.args = {}

module.exports = OpenCommand
59 changes: 59 additions & 0 deletions src/commands/app/console/where.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
Copyright 2026 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.
*/

const { Flags } = require('@oclif/core')
const yaml = require('js-yaml')
const BaseCommand = require('../../../BaseCommand')
const { loadCurrentConfiguration, configString, hasLocalConfiguration } = require('../../../lib/console-helper')
const { EOL } = require('os')

class WhereCommand extends BaseCommand {
async run () {
const { flags } = await this.parse(WhereCommand)

if (!hasLocalConfiguration()) {
this.error('This app is not set to use any Org/Project/Workspace yet. Run `aio app use` to configure it.')
}

const currentConfig = loadCurrentConfiguration('local')

if (flags.json) {
this.log(JSON.stringify(currentConfig, null, 2))
return
}
if (flags.yml) {
this.log(yaml.dump(JSON.parse(JSON.stringify(currentConfig)), {}))
return
}

this.log(`This app is set to use:${EOL}${configString(currentConfig)}`)
}
}

WhereCommand.description = 'Display the Adobe Developer Console org/project/workspace configuration this app is set to use (as set by `aio app use`)'

WhereCommand.flags = {
...BaseCommand.flags,
json: Flags.boolean({
description: 'Output json',
char: 'j',
exclusive: ['yml']
}),
yml: Flags.boolean({
description: 'Output yml',
char: 'y',
exclusive: ['json']
})
}

WhereCommand.args = {}

module.exports = WhereCommand
43 changes: 8 additions & 35 deletions src/commands/app/use.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ governing permissions and limitations under the License.
const BaseCommand = require('../../BaseCommand')
const { CONSOLE_CONFIG_KEY, getProjectCredentialType } = require('../../lib/import-helper')
const { importConsoleConfig, downloadConsoleConfigToBuffer } = require('../../lib/import')
const { loadCurrentConfiguration, configString, isCompleteConfig } = require('../../lib/console-helper')
const { Flags, Args } = require('@oclif/core')
const inquirer = require('inquirer')
const config = require('@adobe/aio-lib-core-config')
Expand All @@ -38,9 +39,9 @@ class Use extends BaseCommand {
const prompt = inquirer.createPromptModule({ output: process.stderr })

// load local config
const currentConfig = this.loadCurrentConfiguration()
const currentConfigString = this.configString(currentConfig)
const currentConfigIsComplete = this.isCompleteConfig(currentConfig)
const currentConfig = loadCurrentConfiguration()
const currentConfigString = configString(currentConfig)
const currentConfigIsComplete = isCompleteConfig(currentConfig)
this.log(`You are currently in:${EOL}${currentConfigString}${EOL}`)

if (args.config_file_path) {
Expand All @@ -55,7 +56,7 @@ class Use extends BaseCommand {

// load global console config
const globalConfig = this.loadGlobalConfiguration()
const globalConfigString = this.configString(globalConfig, 4)
const globalConfigString = configString(globalConfig, 4)

// load from global configuration or select workspace ?
const globalOperationFromFlag = flags.global ? 'global' : null
Expand All @@ -71,7 +72,7 @@ class Use extends BaseCommand {
// load the new workspace, project, org config
let newConfig
if (useOperation === 'global') {
if (!this.isCompleteConfig(globalConfig)) {
if (!isCompleteConfig(globalConfig)) {
const message = `Your global Console configuration is incomplete.${EOL}` +
'Use the `aio console` commands to select your Organization, Project, and Workspace.'
this.error(message)
Expand Down Expand Up @@ -129,31 +130,10 @@ class Use extends BaseCommand {
}
}

loadCurrentConfiguration () {
const projectConfig = config.get('project') || {}
const org = (projectConfig.org && { id: projectConfig.org.id, name: projectConfig.org.name }) || {}
const project = { name: projectConfig.name, id: projectConfig.id }
const workspace = (projectConfig.workspace && { ...projectConfig.workspace }) || {}
return { org, project, workspace }
}

loadGlobalConfiguration () {
return config.get(CONSOLE_CONFIG_KEY) || {}
}

configString (config, spaces = 0) {
const { org = {}, project = {}, workspace = {} } = config
const list = [
`1. Org: ${org.name || '<no org selected>'}`,
`2. Project: ${project.name || '<no project selected>'}`,
`3. Workspace: ${workspace.name || '<no workspace selected>'}`
]

return list
.map(line => ' '.repeat(spaces) + line)
.join(EOL)
}

async promptForUseOperation (prompt, globalConfigString) {
const op = await prompt([
{
Expand All @@ -169,13 +149,6 @@ class Use extends BaseCommand {
return op.res
}

isCompleteConfig (config) {
return config &&
config.org && config.org.id && config.org.name &&
config.project && config.project.id && config.project.name &&
config.workspace && config.workspace.id && config.workspace.name
}

/**
* @param {LibConsoleCLI} consoleCLI lib console config
* @param {object} config local configuration
Expand Down Expand Up @@ -322,9 +295,9 @@ class Use extends BaseCommand {

async finalLogMessage (consoleConfig) {
const config = { org: consoleConfig.project.org, project: consoleConfig.project, workspace: consoleConfig.project.workspace }
const configString = this.configString(config)
const configStr = configString(config)
this.log(chalk.green(chalk.bold(
`${EOL}✔ Successfully imported configuration for:${EOL}${configString}.`
`${EOL}✔ Successfully imported configuration for:${EOL}${configStr}.`
)))
}

Expand Down
78 changes: 78 additions & 0 deletions src/lib/console-helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
Copyright 2026 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.
*/

const config = require('@adobe/aio-lib-core-config')
const { EOL } = require('os')

/**
* Loads the per-app org/project/workspace configuration, as set by `aio app use`.
*
* @param {string} [source] pass 'local' to only read from the local `.aio` file,
* bypassing the merge with the global config
* @returns {object} { org, project, workspace }
*/
function loadCurrentConfiguration (source) {
const projectConfig = config.get('project', source) || {}
const org = (projectConfig.org && { id: projectConfig.org.id, name: projectConfig.org.name }) || {}
const project = { name: projectConfig.name, id: projectConfig.id }
const workspace = (projectConfig.workspace && { ...projectConfig.workspace }) || {}
return { org, project, workspace }
}

/**
* @param {object} config { org, project, workspace }
* @param {number} spaces number of leading spaces for each line
* @returns {string} human readable representation of the org/project/workspace configuration
*/
function configString (config, spaces = 0) {
const { org = {}, project = {}, workspace = {} } = config
const list = [
`1. Org: ${org.name || '<no org selected>'}`,
`2. Project: ${project.name || '<no project selected>'}`,
`3. Workspace: ${workspace.name || '<no workspace selected>'}`
]

return list
.map(line => ' '.repeat(spaces) + line)
.join(EOL)
}

/**
* @param {object} config { org, project, workspace }
* @returns {boolean} true if org, project, and workspace are all fully defined
*/
function isCompleteConfig (config) {
return config &&
config.org && config.org.id && config.org.name &&
config.project && config.project.id && config.project.name &&
config.workspace && config.workspace.id && config.workspace.name
}

/**
* Checks whether a local `.aio` file (as written by `aio app use`) identifies an
* Org and Project on its own. Workspace is intentionally not required here, as
* an Org/Project can be locally selected before a Workspace is (e.g. via `aio app use --global`
* flows or partial imports) - callers should treat a missing local Workspace as
* "no workspace selected", not as "no local configuration at all".
*
* @returns {boolean} true if the local `.aio` file identifies an Org and Project
*/
function hasLocalConfiguration () {
const { org, project } = loadCurrentConfiguration('local')
return !!(org.id && org.name && project.id && project.name)
}

module.exports = {
loadCurrentConfiguration,
configString,
isCompleteConfig,
hasLocalConfiguration
}
4 changes: 4 additions & 0 deletions src/lib/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ module.exports = {
prod: 'aio-cli-console-auth',
stage: 'aio-cli-console-auth-stage'
},
OPEN_URLS: {
prod: 'https://developer.adobe.com/console/projects',
stage: 'https://developer-stage.adobe.com/console/projects'
},
defaultHttpServerPort: 9080,
AIO_CONFIG_WORKSPACE_SERVICES: 'project.workspace.details.services',
AIO_CONFIG_ORG_SERVICES: 'project.org.details.services',
Expand Down
22 changes: 22 additions & 0 deletions test/commands/app/console/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
Copyright 2026 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.
*/

const TheCommand = require('../../../../src/commands/app/console/index.js')
const { Help } = require('@oclif/core')

test('returns help file for app:console command', () => {
const command = new TheCommand([])
command.config = global.createOclifMockConfig()
const spy = jest.spyOn(Help.prototype, 'showHelp').mockReturnValue(true)
return command.run().then(() => {
expect(spy).toHaveBeenCalledWith(['app:console', '--help'])
})
})
Loading
Loading