Skip to content

Commit

Permalink
refactor: convert apps:settings:update command to yargs
Browse files Browse the repository at this point in the history
  • Loading branch information
rossiam committed Dec 10, 2024
1 parent e35965f commit 10a097b
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 43 deletions.
37 changes: 0 additions & 37 deletions packages/cli/src/commands/apps/settings/update.ts

This file was deleted.

115 changes: 115 additions & 0 deletions src/__tests__/commands/apps/settings/update.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { jest } from '@jest/globals'

import type { ArgumentsCamelCase, Argv } from 'yargs'

import type { AppsEndpoint, AppSettingsResponse, SmartThingsClient } from '@smartthings/core-sdk'

import type { APICommand, APICommandFlags } from '../../../../lib/command/api-command.js'
import type {
inputAndOutputItem,
inputAndOutputItemBuilder,
} from '../../../../lib/command/input-and-output-item.js'
import { type buildTableOutput, type chooseApp } from '../../../../lib/command/util/apps-util.js'
import type { CommandArgs } from '../../../../commands/locations/update.js'
import { apiCommandMocks } from '../../../test-lib/api-command-mock.js'
import { buildArgvMock, buildArgvMockStub } from '../../../test-lib/builder-mock.js'
import { CustomCommonOutputProducer } from '../../../../lib/command/format.js'
import { tableGeneratorMock } from '../../../test-lib/table-mock.js'


const { apiCommandMock, apiCommandBuilderMock, apiDocsURLMock } = apiCommandMocks('../../../..')

const inputAndOutputItemMock = jest.fn<typeof inputAndOutputItem>()
const inputAndOutputItemBuilderMock = jest.fn<typeof inputAndOutputItemBuilder>()
jest.unstable_mockModule('../../../../lib/command/input-and-output-item.js', () => ({
inputAndOutputItem: inputAndOutputItemMock,
inputAndOutputItemBuilder: inputAndOutputItemBuilderMock,
}))

const buildTableOutputMock = jest.fn<typeof buildTableOutput>()
const chooseAppMock = jest.fn<typeof chooseApp>().mockResolvedValue('chosen-id')
jest.unstable_mockModule('../../../../lib/command/util/apps-util.js', () => ({
buildTableOutput: buildTableOutputMock,
chooseApp: chooseAppMock,
}))


const { default: cmd } = await import('../../../../commands/apps/settings/update.js')


test('builder', () => {
const yargsMock = buildArgvMockStub<object>()
const {
yargsMock: apiCommandBuilderArgvMock,
positionalMock,
exampleMock,
epilogMock,
argvMock,
} = buildArgvMock<APICommandFlags, CommandArgs>()

apiCommandBuilderMock.mockReturnValueOnce(apiCommandBuilderArgvMock)
inputAndOutputItemBuilderMock.mockReturnValueOnce(argvMock)

const builder = cmd.builder as (yargs: Argv<object>) => Argv<CommandArgs>
expect(builder(yargsMock)).toBe(argvMock)

expect(apiCommandBuilderMock).toHaveBeenCalledExactlyOnceWith(yargsMock)
expect(inputAndOutputItemBuilderMock).toHaveBeenCalledExactlyOnceWith(apiCommandBuilderArgvMock)

expect(positionalMock).toHaveBeenCalledTimes(1)
expect(exampleMock).toHaveBeenCalledTimes(1)
expect(apiDocsURLMock).toHaveBeenCalledTimes(1)
expect(epilogMock).toHaveBeenCalledTimes(1)
})

describe('handler', () => {
const apiAppsUpdateSettingsMock = jest.fn<typeof AppsEndpoint.prototype.updateSettings>()
const clientMock = {
apps: {
updateSettings: apiAppsUpdateSettingsMock,
},
} as unknown as SmartThingsClient
const command = {
client: clientMock,
tableGenerator: tableGeneratorMock,
} as APICommand<APICommandFlags>
apiCommandMock.mockResolvedValue(command)

const inputArgv = { profile: 'default' } as ArgumentsCamelCase<CommandArgs>

it('queries user for appId and process via inputAndOutputItem', async () => {
await expect(cmd.handler(inputArgv)).resolves.not.toThrow()

expect(apiCommandMock).toHaveBeenCalledExactlyOnceWith(inputArgv)
expect(chooseAppMock).toHaveBeenCalledExactlyOnceWith(command, undefined)
expect(inputAndOutputItemMock).toHaveBeenCalledExactlyOnceWith(
command,
{ buildTableOutput: expect.any(Function) },
expect.any(Function),
)

const executeFunction = inputAndOutputItemMock.mock.calls[0][2]

const settings = { settings: { balloonColor: 'gross color' } } as AppSettingsResponse
const updatedSettings = { settings: { balloonColor: 'orange' } } as AppSettingsResponse
apiAppsUpdateSettingsMock.mockResolvedValueOnce(updatedSettings)

expect(await executeFunction(undefined, settings)).toBe(updatedSettings)

expect(apiAppsUpdateSettingsMock).toHaveBeenCalledExactlyOnceWith('chosen-id', settings)

const config = inputAndOutputItemMock.mock.calls[0][1] as
CustomCommonOutputProducer<AppSettingsResponse>
buildTableOutputMock.mockReturnValueOnce('table output')

expect(config.buildTableOutput(updatedSettings)).toBe('table output')

expect(buildTableOutputMock).toHaveBeenCalledExactlyOnceWith(tableGeneratorMock, updatedSettings)
})

it('passes command line id on to chooseApp', async () => {
await expect(cmd.handler({ ...inputArgv, id: 'argv-id' })).resolves.not.toThrow()

expect(chooseAppMock).toHaveBeenCalledExactlyOnceWith(command, 'argv-id')
})
})
12 changes: 6 additions & 6 deletions src/__tests__/commands/locations/update.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { jest } from '@jest/globals'

import { ArgumentsCamelCase, Argv } from 'yargs'
import type { ArgumentsCamelCase, Argv } from 'yargs'

import { Location, LocationUpdate, LocationsEndpoint, SmartThingsClient } from '@smartthings/core-sdk'
import type { Location, LocationUpdate, LocationsEndpoint, SmartThingsClient } from '@smartthings/core-sdk'

import { chooseLocation, tableFieldDefinitions } from '../../../lib/command/util/locations-util.js'
import { APICommand, APICommandFlags } from '../../../lib/command/api-command.js'
import { inputAndOutputItem, inputAndOutputItemBuilder } from '../../../lib/command/input-and-output-item.js'
import { CommandArgs } from '../../../commands/locations/update.js'
import { type chooseLocation, tableFieldDefinitions } from '../../../lib/command/util/locations-util.js'
import type { APICommand, APICommandFlags } from '../../../lib/command/api-command.js'
import type { inputAndOutputItem, inputAndOutputItemBuilder } from '../../../lib/command/input-and-output-item.js'
import type { CommandArgs } from '../../../commands/locations/update.js'
import { apiCommandMocks } from '../../test-lib/api-command-mock.js'
import { buildArgvMock, buildArgvMockStub } from '../../test-lib/builder-mock.js'

Expand Down
53 changes: 53 additions & 0 deletions src/commands/apps/settings/update.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { type ArgumentsCamelCase, type Argv, type CommandModule } from 'yargs'

import { type AppSettingsRequest, type AppSettingsResponse } from '@smartthings/core-sdk'

import {
apiCommand,
apiCommandBuilder,
apiDocsURL,
type APICommandFlags,
} from '../../../lib/command/api-command.js'
import {
inputAndOutputItem,
inputAndOutputItemBuilder,
type InputAndOutputItemFlags,
} from '../../../lib/command/input-and-output-item.js'
import { buildTableOutput, chooseApp } from '../../../lib/command/util/apps-util.js'


export type CommandArgs = APICommandFlags & InputAndOutputItemFlags & {
id?: string
}

const command = 'apps:settings:update [id]'

const describe = 'update the settings of an app'

const builder = (yargs: Argv): Argv<CommandArgs> =>
inputAndOutputItemBuilder(apiCommandBuilder(yargs))
.positional('id', { describe: 'app id', type: 'string' })
.example([
[
'$0 apps:settings:update 392bcb11-e251-44f3-b58b-17f93015f3aa -i app-settings.json',
'update the settings of the app with the given id using the data in "app-settings.json"',
],
[
'$0 apps:settings:update -i app-settings.json',
'ask for the ID of an app to update and then update it using the data in "app-settings.json"',
],
])
.epilog(apiDocsURL('updateAppSettings'))

const handler = async (argv: ArgumentsCamelCase<CommandArgs>): Promise<void> => {
const command = await apiCommand(argv)
const appId = await chooseApp(command, argv.id)
await inputAndOutputItem(
command,
{ buildTableOutput: (data: AppSettingsResponse) => buildTableOutput(command.tableGenerator, data) },
(_, data: AppSettingsRequest) => command.client.apps.updateSettings(appId, data),
)
}

const cmd: CommandModule<object, CommandArgs> = { command, describe, builder, handler }
export default cmd
2 changes: 2 additions & 0 deletions src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import appsCreateCommand from './apps/create.js'
import appsAuthorizeCommand from './apps/authorize.js'
import appsOAuthGenerateCommand from './apps/oauth/generate.js'
import appsOAuthUpdateCommand from './apps/oauth/update.js'
import appsSettingsUpdateCommand from './apps/settings/update.js'
import capabilitiesCommand from './capabilities.js'
import configCommand from './config.js'
import devicepreferencesCommand from './devicepreferences.js'
Expand Down Expand Up @@ -32,6 +33,7 @@ export const commands: CommandModule<object, any>[] = [
appsAuthorizeCommand,
appsOAuthGenerateCommand,
appsOAuthUpdateCommand,
appsSettingsUpdateCommand,
capabilitiesCommand,
configCommand,
devicepreferencesCommand,
Expand Down

0 comments on commit 10a097b

Please sign in to comment.