Skip to content

Commit

Permalink
feat: upgrade projects:list command to use ListExtendedProjects endpo…
Browse files Browse the repository at this point in the history
…int (#128)
  • Loading branch information
kruti49 committed Mar 30, 2023
1 parent 47e6bb9 commit 0baeab8
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 31 deletions.
2 changes: 1 addition & 1 deletion src/commands/deployment-flows/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ The organization ID can be retrieved by using the organizations:list command.`

if (options.offset && this.context.get('offset') >= this.context.get('totalSize')) {
this.log()
this.log(`No result to display as value ${this.context.get('offset')} for offset is larger than the total number of results (${this.context.get('totalSize')}).`)
this.log(`No result to display as value ${this.context.get('offset')} for offset lies outside the range of the list of items.`)
this.log(`Use a value lower than ${this.context.get('totalSize')} for offset.`)
} else {
this.log(`No deployment flows found for organization ${chalk.cyan(options.organization)}.`)
Expand Down
90 changes: 73 additions & 17 deletions src/commands/projects/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,34 @@ import MixCommand from '../../utils/base/mix-command'
import {asChannelsList, asDataPackslist} from '../../utils/format'
import {MixClient, MixResponse, MixResult, ProjectsListParams} from '../../mix/types'
import {DomainOption} from '../../utils/validations'
import {defaultLimit} from '../../utils/constants'

const debug = makeDebug('mix:commands:projects:list')
export default class ProjectsList extends MixCommand {
static description = `list projects
Use this command to list projects that are part of a particular organization.`
Use this command to list projects across all organizations.
A number of flags can be used to constrain the returned results.`

static examples = ['mix projects:list -O 64']
static examples = [
'List projects to which you have access, across all organizations',
'mix projects:list',
'',
'List projects that are part of a particular organization',
'mix projects:list -O 64',
]

static flags = {
'include-features': MixFlags.includeFeaturesFlag,
'exclude-channels': MixFlags.excludeChannelsFlag,
json: MixFlags.jsonFlag,
organization: MixFlags.organizationWithDefaultFlag,
...MixFlags.tableFlags({except: ['extended']}),
organization: {
...MixFlags.organizationWithDefaultFlag,
required: false,
},
...MixFlags.limitOffsetSortFlags,
...MixFlags.tableFlags({except: ['extended', 'sort']}),
'with-name': MixFlags.withProjectName,
yaml: MixFlags.yamlFlag,
}

Expand All @@ -38,29 +53,61 @@ Use this command to list projects that are part of a particular organization.`
id: {header: 'ProjectId'},
displayName: {header: 'Name'},
languageTopic: {header: 'LanguageTopic'},
channels: {
header: 'Channels',
get: asChannelsList,
},
...(this.options['exclude-channels'] ? {} : this.channelsColumn),
datapacks: {
header: 'DataPacks',
get: asDataPackslist,
},
...(this.options['include-features'] ? this.featuresColumn : {}),
createTime: {header: 'CreateTime'},
updateTime: {header: 'UpdateTime'},
}
}

get channelsColumn() {
return {
channels: {
header: 'Channels',
get: asChannelsList,
},
}
}

get featuresColumn() {
return {
features: {
header: 'Features',
get: ({enginePackFeatures}: any) => enginePackFeatures.join(','),
},
}
}

get domainOptions(): DomainOption[] {
debug('get domainOptions()')
return ['organization']
}

async buildRequestParameters(options: Partial<FlagOutput>): Promise<ProjectsListParams> {
debug('buildRequestParameters()')
const {organization: orgId} = options
const {
'exclude-channels': excludeChannels,
'include-features': includeFeatures,
limit = defaultLimit,
offset,
organization: orgId,
sort: sortBy,
'with-name': filter,
} = options

return {orgId}
return {
excludeChannels,
includeFeatures,
...(typeof filter === 'undefined' ? {} : {filter}),
...(typeof limit === 'undefined' ? {} : {limit}),
...(typeof offset === 'undefined' ? {} : {offset}),
...(typeof orgId === 'undefined' ? {} : {orgId}),
...(typeof sortBy === 'undefined' ? {} : {sortBy}),
}
}

doRequest(client: MixClient, params: ProjectsListParams): Promise<MixResponse> {
Expand All @@ -70,23 +117,32 @@ Use this command to list projects that are part of a particular organization.`

outputHumanReadable(transformedData: any) {
debug('outputHumanReadable()')
if (transformedData.length === 0) {
this.log('No projects found.')
const {options} = this
const shouldIncludeFeatures = options['include-features']

return
}
super.outputHumanReadable(transformedData, options)

this.outputCLITable(transformedData, this.columns)
if (shouldIncludeFeatures) {
this.log()
this.log("Run the command again with the 'json' flag to see all engine pack features.")
}
}

setRequestActionMessage(options: any) {
debug('setRequestActionMessage()')
this.requestActionMessage = `Retrieving projects for organization ID ${chalk.cyan(options.organization)}`
const optionalOrganizationInfo = options.organization ? ` for organization ID ${chalk.cyan(options.organization)}` : ''
this.requestActionMessage = 'Retrieving projects' + optionalOrganizationInfo
}

transformResponse(result: MixResult) {
debug('transformResponse()')
const data = result.data as any
return data.projects
const {count, totalSize, offset, limit, projects} = data
this.context.set('count', count)
this.context.set('offset', offset)
this.context.set('limit', limit)
this.context.set('totalSize', totalSize)

return projects
}
}
27 changes: 24 additions & 3 deletions src/mix/api/projects-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,34 @@ export type ProjectsCreateBodyParams = {
export type ProjectsCreateParams = Expand<ProjectsCreatePathParams & ProjectsCreateBodyParams>

/** @hidden */
export type ProjectsListPathParams = {
export type ProjectsListBodyParams = {
/** When set to true, the project channels are excluded from the list. This will boost API performance. */
excludeChannels?: boolean

/** When set to true, includes the list of features supported by this project's engine pack. */
includeFeatures?: boolean

/** Filter results parameter: project display name. The search is case sensitive. */
filter?: string

/** The maximum number of items to be returned in the response. */
limit?: number

/** The offset from which elements will get included in the response. */
offset?: number

/** ID of the organization for which to get the list of projects. */
orgId: string
orgId?: string

/**
* Comma-separated properties to sort by.
* Prepend with +/- for ascending/descending.
*/
sortBy?: string
}

/** @hidden */
export type ProjectsListParams = Expand<ProjectsListPathParams>
export type ProjectsListParams = Expand<ProjectsListBodyParams>

/** @hidden */
export type ProjectsLockPathParams = {
Expand Down
3 changes: 1 addition & 2 deletions src/mix/api/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,11 +182,10 @@ export async function getProjectLock(client: MixClient, params: ProjectsLockGetP
*/
export async function listProjects(client: MixClient, params: ProjectsListParams): Promise<MixResponse> {
debug('listProjects()')
const {orgId} = params

return client.request({
method: 'get',
url: buildURL(client.getServer(), `/v4/organizations/${orgId}/projects`),
url: buildURL(client.getServer(), '/v4/projects', params),
})
}

Expand Down
2 changes: 1 addition & 1 deletion src/utils/base/mix-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ that configuration file swiftly.`)

if (options.offset && this.context.get('offset') >= this.context.get('totalSize')) {
this.log()
this.log(`No result to display as value ${this.context.get('offset')} for offset is larger than the total number of results (${this.context.get('totalSize')}).`)
this.log(`No result to display as value ${this.context.get('offset')} for offset lies outside the range of the list of items.`)
this.log(`Use a value lower than ${this.context.get('totalSize')} for offset.`)
} else {
this.log(`No ${this.context?.get('topic')} found.`)
Expand Down
16 changes: 15 additions & 1 deletion src/utils/flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
} from '../mix/types'

// We keep all flag descriptions in a single place to encourage consistency of
// Flags across commands.
// flags across commands.
// Readability in the commands code is not affected if Flags are named properly.

// Flag shortcuts that should not be reused
Expand Down Expand Up @@ -178,6 +178,11 @@ export const entityNameFlag = Flags.string({
required: true,
})

export const excludeChannelsFlag = Flags.boolean({
description: 'exclude project channels from the list',
default: false,
})

export const excludeOverridesFlag = Flags.boolean({
description: 'exclude application configurations that are overridden',
default: false,
Expand All @@ -192,6 +197,11 @@ export const hasAFlag = Flags.string({
multiple: true,
})

export const includeFeaturesFlag = Flags.boolean({
description: "include the list of features supported by project's engine pack",
default: false,
})

export const inputFilePathFlag = Flags.string({
char: filePathShortucut,
description: 'input file path',
Expand Down Expand Up @@ -514,6 +524,10 @@ export const withOrganizationTypeFlag = Flags.string({
options: ['personal', 'standard'],
})

export const withProjectName = Flags.string({
description: 'filter results by project name (case sensitive)',
})

export const withRuntimeApp = Flags.string({
description: 'filter results by fully-qualified runtime app ID',
})
Expand Down
19 changes: 14 additions & 5 deletions test/commands/projects/list.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,23 @@ const td = require('./projects-test-data')
const testData = require('../../test-data')
const serverURL = `https://${testData.server}`

const {orgId} = td.request.params

const endpoint = `/v4/organizations/${orgId}/projects`
const endpoint = '/v4/projects'

describe('projects:list command', () => {
test
.env(testData.env)
.nock(serverURL, api =>
api
.get(endpoint)
.query({
excludeChannels: false,
includeFeatures: false,
limit: 25,
})
.reply(200, td.response)
)
.stdout()
.command(['projects:list', '-O', orgId.toString()])
.command(['projects:list'])
.it('lists basic data for all projects', ctx => {
const lines = ctx.stdout.split('\n').map(ln => ln.trim())
const headers = lines[0].split(/\s+/)
Expand All @@ -38,10 +41,16 @@ describe('projects:list command', () => {
.nock(serverURL, api =>
api
.get(endpoint)
.query({
excludeChannels: false,
includeFeatures: false,
limit: 25,
sortBy: '-displayName',
})
.reply(200, td.response)
)
.stdout()
.command(['projects:list', '-O', orgId.toString(), '--sort=-displayName'])
.command(['projects:list', '--sort=-displayName'])
.it('responds correctly to tabular formatting flags', ctx => {
const lines = ctx.stdout.split('\n').map(ln => ln.trim())
const headers = lines[0].split(/\s+/)
Expand Down
6 changes: 5 additions & 1 deletion test/commands/projects/projects-test-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ module.exports = {
{ id: "8681", orgId: "24", displayName: "EricCTEEllipsisDemo", languageTopic: "gen", channels: [ { channel: { id: "10311", displayName: "Omni Channel VA", codeName: "Omni Channel VA", modes: [ "AUDIO_SCRIPT", "DTMF", "INTERACTIVITY", "RICH_TEXT", "TTS", ], color: "PURPLE", }, isActive: true, }, ], datapacks: [ { displayName: "en-US", version: "4.5.0", isActive: true, }, ], baseDatapack: "", createTime: "2020-03-25T12:32:07.247Z", updateTime: "2020-05-21T15:48:23Z", }, { id: "26066", displayName: "DateEntityTest", languageTopic: "sie", channels: [ { channel: { id: "38122", displayName: "IVR channel", codeName: "IVR channel", modes: ["AUDIO_SCRIPT", "DTMF"], color: "PURPLE", }, isActive: true, }, ], datapacks: [ { displayName: "en-US", version: "4.1.0", isActive: true, }, ], baseDatapack: "", createTime: "2021-05-27T14:42:52.386Z", updateTime: "2021-05-27T14:42:49Z", }, { id: "26793", displayName: "decimalAssingAndCompare", languageTopic: "sie", channels: [ { channel: { id: "39404", displayName: "Digital VA text", codeName: "Digital VA text", modes: ["INTERACTIVITY", "RICH_TEXT"], color: "LIGHT_ORANGE", }, isActive: true, }, { channel: { id: "39403", displayName: "IVR channel", codeName: "IVR channel", modes: ["AUDIO_SCRIPT", "DTMF", "TTS"], color: "PURPLE", }, isActive: true, }, ], datapacks: [ { displayName: "en-US", version: "4.1.0", isActive: true, }, ], baseDatapack: "", createTime: "2021-06-24T06:13:07.528Z", updateTime: "2021-06-24T06:13:07Z", }, { id: "26167", displayName: "IsAEnityRelationship2", languageTopic: "gen", channels: [ { channel: { id: "38278", displayName: "Digital VA text", codeName: "Digital VA text", modes: [ "AUDIO_SCRIPT", "DTMF", "INTERACTIVITY", "RICH_TEXT", "TTS", ], color: "LIGHT_ORANGE", }, isActive: true, }, ], datapacks: [ { displayName: "en-US", version: "4.7.0", isActive: true, }, ], baseDatapack: "", createTime: "2021-06-01T06:52:44.548Z", updateTime: "2021-06-01T06:52:45Z", }, { id: "128", displayName: "21012020_renamed", languageTopic: "gen", channels: [ { channel: { id: "218", displayName: "web", codeName: "web", modes: ["RICH_TEXT"], color: "LIGHT_ORANGE", }, isActive: true, }, { channel: { id: "217", displayName: "ivr", codeName: "ivr", modes: ["AUDIO_SCRIPT", "TTS"], color: "PURPLE", }, isActive: true, }, ], datapacks: [ { displayName: "en-US", version: "4.5.0", isActive: true, }, ], baseDatapack: "", createTime: "2020-01-21T17:47:04.737Z", updateTime: "2020-05-11T15:06:31Z", }, { id: "10064", displayName: "UX New Color Test", languageTopic: "gen", channels: [ { channel: { id: "12924", displayName: "bevelAndEmboss", codeName: "bevelAndEmboss", modes: ["RICH_TEXT"], color: "YELLOW", }, isActive: true, }, { channel: { id: "12923", displayName: "unicorn vomit", codeName: "unicorn vomit", modes: ["AUDIO_SCRIPT", "RICH_TEXT"], color: "PINK", }, isActive: true, }, { channel: { id: "12922", displayName: "TVRainbow", codeName: "TVRainbow", modes: ["AUDIO_SCRIPT", "RICH_TEXT"], color: "CORN_FLOWER", }, isActive: true, }, ], datapacks: [ { displayName: "en-US", version: "4.5.0", isActive: true, }, ], baseDatapack: "", createTime: "2020-04-01T14:03:17.447Z", updateTime: "2020-04-01T20:07:13Z", }, { id: "26211", displayName: "ACME-3978", languageTopic: "sie", channels: [ { channel: { id: "38355", displayName: "Omni Channel VA", codeName: "Omni Channel VA", modes: [ "AUDIO_SCRIPT", "DTMF", "INTERACTIVITY", "RICH_TEXT", "TTS", ], color: "CORN_FLOWER", }, isActive: true, }, { channel: { id: "38354", displayName: "Digital VA voice", codeName: "Digital VA voice", modes: [ "AUDIO_SCRIPT", "DTMF", "INTERACTIVITY", "RICH_TEXT", "TTS", ], color: "GREEN", }, isActive: true, }, ], datapacks: [ { displayName: "en-US", version: "4.1.0", isActive: true, }, ], baseDatapack: "", createTime: "2021-06-02T14:59:55.273Z", updateTime: "2021-06-08T10:45:15Z", }, { id: "10871", displayName: "EricCTPromotionDemo", languageTopic: "gen", channels: [ { channel: { id: "14470", displayName: "Omni Channel VA", codeName: "Omni Channel VA", modes: [ "AUDIO_SCRIPT", "DTMF", "INTERACTIVITY", "RICH_TEXT", "TTS", ], color: "PURPLE", }, isActive: true, }, ], datapacks: [ { displayName: "en-US", version: "4.5.0", isActive: true, }, ], baseDatapack: "", createTime: "2020-04-23T15:51:16.520Z", updateTime: "2020-04-23T18:46:38Z", },
{ id: "26166", orgId: "24", displayName: "IsAEntityRelationship", languageTopic: "gen", channels: [ { channel: { id: "38277", displayName: "Digital VA text", codeName: "Digital VA text", modes: [ "AUDIO_SCRIPT", "DTMF", "INTERACTIVITY", "RICH_TEXT", "TTS", ], color: "LIGHT_ORANGE", }, isActive: true, }, { channel: { id: "38276", displayName: "IVR channel", codeName: "IVR channel", modes: ["AUDIO_SCRIPT", "DTMF"], color: "PURPLE", }, isActive: true, }, ], datapacks: [ { displayName: "en-GB", version: "4.1.0", isActive: true, }, ], baseDatapack: "", createTime: "2021-06-01T06:29:49.087Z", updateTime: "2021-06-01T06:49:09Z", },
{ id: "26836", orgId: "24", displayName: "DecimalDatatype", languageTopic: "gen", channels: [ { channel: { id: "39492", displayName: "Digital VA text", codeName: "Digital VA text", modes: ["INTERACTIVITY", "RICH_TEXT"], color: "LIGHT_ORANGE", }, isActive: true, }, { channel: { id: "39491", displayName: "IVR channel", codeName: "IVR channel", modes: ["AUDIO_SCRIPT", "DTMF", "TTS"], color: "PURPLE", }, isActive: true, }, ], datapacks: [ { displayName: "en-US", version: "4.7.0", isActive: true, }, ], baseDatapack: "", createTime: "2021-06-28T09:12:49.630Z", updateTime: "2021-06-28T09:12:49Z", },
{ id: "26291", orgId: "24", displayName: "XDLGTOOL-4095", languageTopic: "sie", channels: [ { channel: { id: "38501", displayName: "IVR channel", codeName: "IVR channel", modes: ["AUDIO_SCRIPT", "DTMF", "TTS"], color: "PURPLE", }, isActive: true, }, ], datapacks: [ { displayName: "en-US", version: "4.1.0", isActive: true, }, ], baseDatapack: "", createTime: "2021-06-07T07:18:55.840Z", updateTime: "2021-06-07T07:18:55Z", }, ], },
{ id: "26291", orgId: "24", displayName: "XDLGTOOL-4095", languageTopic: "sie", channels: [ { channel: { id: "38501", displayName: "IVR channel", codeName: "IVR channel", modes: ["AUDIO_SCRIPT", "DTMF", "TTS"], color: "PURPLE", }, isActive: true, }, ], datapacks: [ { displayName: "en-US", version: "4.1.0", isActive: true, }, ], baseDatapack: "", createTime: "2021-06-07T07:18:55.840Z", updateTime: "2021-06-07T07:18:55Z", }, ],
"count": 4,
"totalSize": 4,
"limit": 25,
"offset": 0},
buildResponse: { id: "e01669b9-d572-45d4-9fcc-270947494556", type: "BUILD_MODELS", projectId: "1922", status: "RUNNING", createTime: "2021-09-13T17:44:42Z", updateTime: "2021-09-13T17:44:42Z", }
}

0 comments on commit 0baeab8

Please sign in to comment.