Skip to content
Merged
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
59 changes: 1 addition & 58 deletions src/commands/console/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const LibConsoleCLI = require('@adobe/aio-cli-lib-console')
const { CLI } = require('@adobe/aio-lib-ims/src/context')
const { getCliEnv } = require('@adobe/aio-lib-env')
const yaml = require('js-yaml')
const { CONFIG_KEYS, API_KEYS, CONSOLE_API_URLS, ORG_FEATURE_RUNTIME, ORG_TYPE_DEVELOPER, ORG_TYPE_ENTERPRISE } = require('../../config')
const { CONFIG_KEYS, API_KEYS } = require('../../config')

class ConsoleCommand extends Command {
async run () {
Expand All @@ -36,63 +36,6 @@ class ConsoleCommand extends Command {
this.consoleCLI = await LibConsoleCLI.init({ accessToken: this.accessToken, apiKey: this.apiKey, env: this.cliEnv })
}

/**
* Retrieve enabled feature flags for an org from the Developer Console web API.
*
* @param {string} orgId Organization AMS ID
* @returns {Promise<Array<{name: string, description: string}>>} feature flags
*/
async getOrgFeatures (orgId) {
const baseUrl = CONSOLE_API_URLS[this.cliEnv] || CONSOLE_API_URLS.prod
const response = await fetch(`${baseUrl}/console/api/organizations/${orgId}/features`, {
headers: {
accept: 'application/json',
authorization: `Bearer ${this.accessToken}`,
'x-api-key': this.apiKey
}
})
if (!response.ok) {
aioConsoleLogger.debug(`getOrgFeatures: non-ok response ${response.status} for org ${orgId}`)
return []
}
return response.json()
}

/**
* Test whether an org has the Runtime feature.
*
* @param {string} orgId Organization AMS ID
* @returns {Promise<boolean>} true when Runtime is enabled
*/
async hasRuntimeFeature (orgId) {
try {
const features = await this.getOrgFeatures(orgId)
return features.some(feature => feature.name === ORG_FEATURE_RUNTIME)
} catch (err) {
aioConsoleLogger.debug(err)
return false
}
}

/**
* Filter orgs to those that can be used by Developer Console App Builder flows.
*
* @param {Array<{id: string, type: string}>} orgs organizations
* @returns {Promise<Array<object>>} selectable organizations
*/
async getSelectableOrgs (orgs) {
const checks = await Promise.all(orgs.map(async org => {
if (org.type === ORG_TYPE_ENTERPRISE) {
return true
}
if (org.type === ORG_TYPE_DEVELOPER) {
return this.hasRuntimeFeature(org.id)
}
return false
}))
return orgs.filter((_, i) => checks[i])
}

/**
* Output JSON data
*
Expand Down
10 changes: 4 additions & 6 deletions src/commands/console/org/list.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,11 @@ class ListCommand extends ConsoleCommand {
* @returns {Promise<Array<{id, code, name}>>} Array of Orgs
*/
async getConsoleOrgs () {
// this returns the orgs that can be used by
// App Builder flows (entp + developer-with-Runtime), so no need to
// filter more
const response = await this.consoleCLI.getOrganizations()
const selectableOrgs = await this.getSelectableOrgs(response)
const orgs = selectableOrgs
// Omit props
.map(({ id, code, name }) => ({ id, code, name }))

return orgs
return response.map(({ id, code, name }) => ({ id, code, name }))
}

/**
Expand Down
5 changes: 4 additions & 1 deletion src/commands/console/org/select.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ class SelectCommand extends ConsoleCommand {
}

async selectOrgInteractive (preSelectedOrgIdOrCode) {
const orgs = await this.getSelectableOrgs(await this.consoleCLI.getOrganizations())
// this returns the orgs that can be used by
// App Builder flows (entp + developer-with-Runtime), so no need to
// filter more
const orgs = await this.consoleCLI.getOrganizations()
const org = await this.consoleCLI.promptForSelectOrganization(
orgs,
{ orgId: preSelectedOrgIdOrCode, orgCode: preSelectedOrgIdOrCode }
Expand Down
15 changes: 1 addition & 14 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ OF ANY KIND, either express or implied. See the License for the specific languag
governing permissions and limitations under the License.
*/

const ORG_TYPE_ENTERPRISE = 'entp'
const ORG_TYPE_DEVELOPER = 'developer'
const ORG_FEATURE_RUNTIME = 'RUNTIME'

const API_KEYS = {
prod: 'aio-cli-console-auth',
stage: 'aio-cli-console-auth-stage'
Expand All @@ -30,17 +26,8 @@ const OPEN_URLS = {
stage: 'https://developer-stage.adobe.com/console/projects'
}

const CONSOLE_API_URLS = {
prod: 'https://developer.adobe.com',
stage: 'https://developer-stage.adobe.com'
}

module.exports = {
ORG_TYPE_ENTERPRISE,
ORG_TYPE_DEVELOPER,
ORG_FEATURE_RUNTIME,
CONFIG_KEYS,
API_KEYS,
OPEN_URLS,
CONSOLE_API_URLS
OPEN_URLS
}
69 changes: 0 additions & 69 deletions test/commands/console/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,74 +226,5 @@ describe('ConsoleCommand', () => {
command.clearConfig()
expect(config.delete).toHaveBeenCalledWith(CONFIG_KEYS.CONSOLE)
})

test('getOrgFeatures', async () => {
command.cliEnv = STAGE_ENV
command.apiKey = API_KEYS[STAGE_ENV]
command.accessToken = 'token'
global.fetch = jest.fn().mockResolvedValue({
ok: true,
json: jest.fn().mockResolvedValue([{ name: 'RUNTIME' }])
})

await expect(command.getOrgFeatures('304327')).resolves.toEqual([{ name: 'RUNTIME' }])
expect(global.fetch).toHaveBeenCalledWith('https://developer-stage.adobe.com/console/api/organizations/304327/features', {
headers: {
accept: 'application/json',
authorization: 'Bearer token',
'x-api-key': API_KEYS[STAGE_ENV]
}
})
})

test('getOrgFeatures returns empty list for non-ok response', async () => {
command.cliEnv = PROD_ENV
command.apiKey = API_KEYS[PROD_ENV]
command.accessToken = 'token'
global.fetch = jest.fn().mockResolvedValue({ ok: false })

await expect(command.getOrgFeatures('304327')).resolves.toEqual([])
})

test('getOrgFeatures falls back to prod url for unknown env', async () => {
command.cliEnv = 'unknown-env'
command.apiKey = API_KEYS[PROD_ENV]
command.accessToken = 'token'
global.fetch = jest.fn().mockResolvedValue({
ok: true,
json: jest.fn().mockResolvedValue([])
})

await expect(command.getOrgFeatures('304327')).resolves.toEqual([])
expect(global.fetch).toHaveBeenCalledWith('https://developer.adobe.com/console/api/organizations/304327/features', expect.any(Object))
})

test('hasRuntimeFeature', async () => {
command.getOrgFeatures = jest.fn().mockResolvedValue([{ name: 'RUNTIME' }])
await expect(command.hasRuntimeFeature('304327')).resolves.toBe(true)

command.getOrgFeatures = jest.fn().mockResolvedValue([{ name: 'OTHER' }])
await expect(command.hasRuntimeFeature('304327')).resolves.toBe(false)
})

test('hasRuntimeFeature returns false when feature lookup fails', async () => {
command.getOrgFeatures = jest.fn().mockRejectedValue(new Error('bad feature lookup'))
await expect(command.hasRuntimeFeature('304327')).resolves.toBe(false)
})

test('getSelectableOrgs includes enterprise orgs and developer orgs with Runtime', async () => {
command.hasRuntimeFeature = jest.fn(orgId => Promise.resolve(orgId === 'developer-runtime'))
const orgs = [
{ id: 'enterprise', type: 'entp' },
{ id: 'developer-runtime', type: 'developer' },
{ id: 'developer-no-runtime', type: 'developer' },
{ id: 'other', type: 'other' }
]

await expect(command.getSelectableOrgs(orgs)).resolves.toEqual([
{ id: 'enterprise', type: 'entp' },
{ id: 'developer-runtime', type: 'developer' }
])
})
})
})
11 changes: 4 additions & 7 deletions test/commands/console/org/list.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ const { stdout } = require('stdout-stderr')
const mockConsoleCLIInstance = {}
/** @private */
function setDefaultMockConsoleCLI () {
// `aio-cli-lib-console` is responsible for filtering to selectable orgs
// (enterprise + developer-with-RUNTIME). The plugin layer just renders
// whatever the lib returns, so we mock the already-filtered list here.
mockConsoleCLIInstance.getOrganizations = jest.fn().mockResolvedValue([
{ id: 1, code: 'CODE01', name: 'ORG01', type: 'entp' },
{ id: 2, code: 'CODE02', name: 'ORG02', type: 'entp' },
{ id: 3, code: 'CODE03', name: 'ORG03', type: 'entp' },
{ id: 4, code: 'CODE04', name: 'ORGFOURXX', type: 'developer' },
{ id: 5, code: 'CODE05', name: 'ORG05', type: 'not_entp' },
{ id: 6, code: 'CODE06', name: 'ORG06', type: 'developer' }
{ id: 4, code: 'CODE04', name: 'ORGFOURXX', type: 'developer' }
])
}
jest.mock('@adobe/aio-cli-lib-console', () => ({
Expand All @@ -33,10 +34,6 @@ const ListCommand = require('../../../../src/commands/console/org/list')
let command
beforeEach(() => {
setDefaultMockConsoleCLI()
global.fetch = jest.fn(url => Promise.resolve({
ok: true,
json: () => Promise.resolve(url.endsWith('/4/features') ? [{ name: 'RUNTIME' }] : [])
}))
command = new ListCommand([])
})

Expand Down
20 changes: 8 additions & 12 deletions test/commands/console/org/select.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,20 @@ governing permissions and limitations under the License.
const { Command } = require('@oclif/core')

const mockConsoleCLIInstance = {}
const orgs = [
// `aio-cli-lib-console` is responsible for filtering to selectable orgs
// (enterprise + developer-with-RUNTIME), so the plugin layer just hands the
// list straight to `promptForSelectOrganization`.
const selectableOrgs = [
{ id: '1', code: 'CODE01', name: 'ORG01', type: 'entp' },
{ id: '2', code: 'CODE02', name: 'ORG02', type: 'entp' },
{ id: '3', code: 'CODE03', name: 'ORG03', type: 'entp' },
{ id: '4', code: 'CODE04', name: 'ORG04', type: 'developer' },
{ id: '6', code: 'CODE06', name: 'ORG06', type: 'developer' },
{ id: '33', code: 'CODE33', name: 'ORG33', type: 'not_entp' }
{ id: '4', code: 'CODE04', name: 'ORG04', type: 'developer' }
]
const selectableOrgs = orgs.slice(0, 4)
const selectedOrg = { id: '1', code: 'CODE01', name: 'ORG01', type: 'entp' }
const selectedDeveloperOrg = { id: '4', code: 'CODE04', name: 'ORG04', type: 'developer' }
const selectedOrg = selectableOrgs[0]
const selectedDeveloperOrg = selectableOrgs[3]
/** @private */
function setDefaultMockConsoleCLI () {
mockConsoleCLIInstance.getOrganizations = jest.fn().mockResolvedValue(orgs)
mockConsoleCLIInstance.getOrganizations = jest.fn().mockResolvedValue(selectableOrgs)
mockConsoleCLIInstance.promptForSelectOrganization = jest.fn().mockResolvedValue(selectedOrg)
}
jest.mock('@adobe/aio-cli-lib-console', () => ({
Expand All @@ -40,10 +40,6 @@ let command
beforeEach(() => {
command = new SelectCommand([])
setDefaultMockConsoleCLI()
global.fetch = jest.fn(url => Promise.resolve({
ok: true,
json: () => Promise.resolve(url.endsWith('/4/features') ? [{ name: 'RUNTIME' }] : [])
}))
config.set.mockReset()
config.delete.mockReset()
})
Expand Down
Loading