Skip to content

Commit

Permalink
feat: add env-configs:configure command (#183)
Browse files Browse the repository at this point in the history
Co-authored-by: Shashwat Khanna <Shashwat.Khanna@nuance.com>
  • Loading branch information
Jayk5 and Shashwat Khanna committed Jul 7, 2023
1 parent 44b602e commit 66b0330
Show file tree
Hide file tree
Showing 6 changed files with 286 additions and 2 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"Oliver Daniel",
"Vishal Sangave",
"Pallavi Pawar",
"Krutika Patel"
"Krutika Patel",
"Jayant Kumar"
],
"homepage": "https://github.com/nuance-communications/mix-cli",
"keywords": [
Expand Down
107 changes: 107 additions & 0 deletions src/commands/env-configs/configure.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright 2022, Nuance, Inc. and its contributors.
* All rights reserved.
*
* This source code is licensed under the Apache-2.0 license found in
* the LICENSE file in the root directory of this source tree.
*/

import {FlagOutput} from '@oclif/core/lib/interfaces'
import makeDebug from 'debug'
import chalk from 'chalk'

import * as MixFlags from '../../utils/flags'
import MixCommand from '../../utils/base/mix-command'
import {MixClient, MixResponse} from '../../mix/types'
import {EnvConfigsConfigureParams} from '../../mix/api/env-configs-types'
import {configureEnvConfigWithGeo, configureEnvConfigWithoutGeo} from '../../mix/api/env-configs'

const debug = makeDebug('mix:commands:env-configs:configure')

export default class EnvConfigsList extends MixCommand {
static description = `Configure an environment configuration
Environment configurations provide default values either for the project as a whole
or for a specific environment geography. If an environment geography doest not have
a default for a specific configuration, then the default for the project is used.
Using this command with only the 'project' flag configures the project-level default value
for the given configuration label. Using this command with the 'env' and 'env-geo' flags
in addition to the 'project' flag configures the default value for the given configuration
label targeting the specified environment geography.
`

static examples = [
'Configure an environment configuration project default',
'mix env-configs:configure -P 1922 --label=GRAMMAR_BASE_PATH --value=https://www.example.com/grammars',
'Configure an environment configuration for a specific environment geography',
'mix env-configs:configure -P 1922 --env=1923 --env-geo=9 --label=GRAMMAR_BASE_PATH --value=https://www.example.com/grammars',
]

static flags = {
project: MixFlags.projectFlag,
env: {
...MixFlags.envIDFlag,
dependsOn: ['env-geo'],
},
'env-geo': {
...MixFlags.envGeoIDFlag,
dependsOn: ['env'],
},
label: MixFlags.labelFlag,
value: MixFlags.valueFlag,
}

async buildRequestParameters(options: Partial<FlagOutput>): Promise<EnvConfigsConfigureParams> {
debug('buildRequestParameters()')
const {
env,
'env-geo': envGeoId,
label,
project,
value,
} = options

return {
envId: env,
envGeoId,
label,
projectId: project,
value,
}
}

doRequest(client: MixClient, params: EnvConfigsConfigureParams): Promise<MixResponse> {
debug('doRequest()')
const {envId} = params

if (envId) {
return configureEnvConfigWithGeo(client, params)
}

return configureEnvConfigWithoutGeo(client, params)
}

outputHumanReadable() {
debug('outputHumanReadable()')

this.log()
if (this.options.env) {
this.log(`Environment configuration ${this.options.label} configured successfully for project ${chalk.cyan(this.options.project)}`)
this.log(`In environment geography ${chalk.cyan(this.options['env-geo'])} of environment ${chalk.cyan(this.options.env)}`)
} else {
this.log(`Environment configuration ${this.options.label} default configured successfully for project ${chalk.cyan(this.options.project)}`)
}

this.log()
}

setRequestActionMessage(_options: any) {
debug('setRequestActionMessage()')

this.requestActionMessage = `Configuring environment configuration ${this.options.label} for project ${this.options.project}`
if (this.options.env) {
this.requestActionMessage += ` in environment geography ${this.options['env-geo']} of environment ${this.options.env}`
}
}
}
8 changes: 8 additions & 0 deletions src/mix/api/env-configs-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@ export type EnvConfigsListParams = {
projectId: string
}

export type EnvConfigsConfigureParams = {
projectId: string,
envId: string | undefined,
envGeoId: string | undefined,
label: string,
value: string,
}

export type EnvConfigListResponse = {
projectDefaults: {
label: string,
Expand Down
27 changes: 26 additions & 1 deletion src/mix/api/env-configs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import makeDebug from 'debug'

import buildURL from './utils/build-url'
import {MixClient, MixResponse} from '../types'
import {EnvConfigsListParams} from './env-configs-types'
import {EnvConfigsListParams, EnvConfigsConfigureParams} from './env-configs-types'

const debug = makeDebug('mix:api:env-configs')

Expand All @@ -26,3 +26,28 @@ export async function listEnvConfigs(client: MixClient, params: EnvConfigsListPa
url: buildURL(client.getServer(), `/v4/projects/${projectId}/env-configs`),
})
}

export async function configureEnvConfigWithGeo(client: MixClient, params: EnvConfigsConfigureParams): Promise<MixResponse> {
debug('configureEnvConfigWithGeo()')
const {projectId, envId, envGeoId, label, value} = params
return client.request({
method: 'put',
url: buildURL(client.getServer(), `/v4/environments/${envId}/geographies/${envGeoId}/configs/${label}`),
options: {
params: {
projectId,
},
},
data: `"${value}"`,
})
}

export async function configureEnvConfigWithoutGeo(client: MixClient, params: EnvConfigsConfigureParams): Promise<MixResponse> {
debug('configureEnvConfigWithoutGeo()')
const {projectId, label, value} = params
return client.request({
method: 'put',
url: buildURL(client.getServer(), `/v4/projects/${projectId}/env-configs/${label}`),
data: `"${value}"`,
})
}
12 changes: 12 additions & 0 deletions src/utils/flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ export const entityTypeFlag = Flags.string({
required: true,
})

export const envIDFlag = Flags.integer({
description: 'environment ID',
})

export const envGeoIDFlag = Flags.integer({
description: envGeoIDDesc,
})
Expand Down Expand Up @@ -235,6 +239,10 @@ export const jsonFlag = Flags.boolean({
exclusive: ['csv', 'yaml'],
})

export const labelFlag = Flags.string({
description: 'environment configuration name',
})

export const limitFlag = Flags.integer({
description: 'limit maximum results returned (defaults to Mix API behavior)',
})
Expand Down Expand Up @@ -489,6 +497,10 @@ export const toEntityTypeFlag = Flags.string({
required: true,
})

export const valueFlag = Flags.string({
description: 'environment configuration value',
})

export const watchFlag = Flags.boolean({
default: false,
description: 'poll status of job every minute',
Expand Down
131 changes: 131 additions & 0 deletions test/commands/env-configs/configure.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* Copyright 2022, Nuance, Inc. and its contributors.
* All rights reserved.
*
* This source code is licensed under the Apache-2.0 license found in
* the LICENSE file in the root directory of this source tree.
*/

import { expect, test } from "@oclif/test";

const testData = require("../../test-data");
const serverURL = `https://${testData.server}`;

describe("env-configs:configure command", () => {
let project = 1;
let envId = 1001;
let envGeoId = 9;
let label = "GRAMMAR_BASE_PATH";
let value = "http://www.test.com/";

test
.env(testData.env)
.nock(serverURL, (api) =>
api
.put(`/v4/projects/${project}/env-configs/${label}`, `"${value}"`)
.reply(200)
)
.stdout()
.command([
"env-configs:configure",
`--project=${project}`,
`--label=${label}`,
`--value=${value}`,
])
.it("configures default environment configuration", (ctx) => {
const [firstLine, secondLine] = ctx.stdout.split("\n");
expect(secondLine).to.contain(`configured successfully`);
});

test
.env(testData.env)
.nock(serverURL, (api) =>
api
.put(
`/v4/environments/${envId}/geographies/${envGeoId}/configs/${label}`,
`"${value}"`
)
.query({
projectId: project,
})
.reply(200)
)
.stdout()
.command([
"env-configs:configure",
`--project=${project}`,
`--env=${envId}`,
`--env-geo=${envGeoId}`,
`--label=${label}`,
`--value=${value}`,
])
.it("configures environment configuration with env and env-geo", (ctx) => {
const [firstLine, secondLine] = ctx.stdout.split("\n");
expect(secondLine).to.contain(`configured successfully`);
});

test
.env(testData.env)
.stderr()
.command([
"env-configs:configure",
`--project=${project}`,
`--env=${envId}`,
`--label=${label}`,
`--value=${value}`,
])
.catch((ctx) => {
expect(ctx.message).to.contain(
`All of the following must be provided when using --env: --env-geo`
);
})
.it("fails to configure environment configuration with env but no env-geo");

test
.env(testData.env)
.stderr()
.command([
"env-configs:configure",
`--project=${project}`,
`--env-geo=${envGeoId}`,
`--label=${label}`,
`--value=${value}`,
])
.catch((ctx) => {
expect(ctx.message).to.contain(
`All of the following must be provided when using --env-geo: --env`
);
})
.it("fails to configure environment configuration with env-geo but no env");

test
.env(testData.env)
.stderr()
.command([
"env-configs:configure",
`--label=${label}`,
`--value=${value}`,
])
.catch((ctx) => {
expect(ctx.message).to.contain(
`Missing required flag project`
);
})
.it("fails to configure environment configuration with no project ID");

test
.env(testData.env)
.stderr()
.command([
"env-configs:configure",
`--project=ok123`,
`--label=${label}`,
`--value=${value}`,
])
.catch((ctx) => {
expect(ctx.message).to.contain(
'Expected an integer'
);
})
.it("fails to configure environment configuration with invalid project ID");
});

0 comments on commit 66b0330

Please sign in to comment.