-
Notifications
You must be signed in to change notification settings - Fork 112
/
Copy pathcreate.ts
137 lines (120 loc) · 4.86 KB
/
create.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import { Flags, Errors } from '@oclif/core'
import inquirer from 'inquirer'
import { v4 as uuid } from 'uuid'
import { AppClassification, AppCreateRequest, AppCreationResponse, AppType, PrincipalType } from '@smartthings/core-sdk'
import {
APICommand,
computedDef,
createFromUserInput,
httpsURLValidate,
inputAndOutputItem,
lambdaAuthFlags,
objectDef,
optionalStringDef,
sanitize,
staticDef,
stringDef,
stringValidateFn,
userInputProcessor,
} from '@smartthings/cli-lib'
import { addPermission } from '../../lib/aws-utils'
import { oauthAppScopeDef, redirectUrisDef, smartAppHelpText, tableFieldDefinitions } from '../../lib/commands/apps-util'
const appNameDef = computedDef((context?: unknown[]): string => {
if (!context || context.length === 0) {
throw Error('invalid context for appName computed input definition')
}
const displayName = (context[0] as Pick<AppCreateRequest, 'displayName'>).displayName
const retVal = `${sanitize(displayName)}-${uuid()}`.toLowerCase()
// the app name has to start with a letter or number
return displayName.match(/^[a-z]/) ? retVal : 'a' + retVal
})
const clientNameDef = computedDef((context?: unknown[]): string => {
if (!context || context.length !== 2) {
throw Error('invalid context for appName computed input definition')
}
return (context[1] as Pick<AppCreateRequest, 'displayName'>).displayName
})
const oauthAppCreateRequestInputDefinition = objectDef<AppCreateRequest>('OAuth-In SmartApp', {
displayName: stringDef('Display Name', { validate: stringValidateFn({ maxLength: 75 }) }),
description: stringDef('Description', { validate: stringValidateFn({ maxLength: 250 }) }),
appName: appNameDef,
appType: staticDef(AppType.API_ONLY),
classifications: staticDef([AppClassification.CONNECTED_SERVICE]),
singleInstance: staticDef(true),
iconImage: objectDef('Icon Image', {
url: optionalStringDef('Icon Image URL', { validate: httpsURLValidate }),
}),
apiOnly: objectDef('API Only', { targetUrl: optionalStringDef('Target URL', { validate: httpsURLValidate }) }),
principalType: staticDef(PrincipalType.LOCATION),
oauth: objectDef('OAuth', {
clientName: clientNameDef,
scope: oauthAppScopeDef,
redirectUris: redirectUrisDef,
}),
}, { helpText: smartAppHelpText })
export default class AppCreateCommand extends APICommand<typeof AppCreateCommand.flags> {
static description = 'create an app' +
this.apiDocsURL('createApp')
static flags = {
...APICommand.flags,
...inputAndOutputItem.flags,
authorize: Flags.boolean({
description: 'authorize Lambda functions to be called by SmartThings',
}),
...lambdaAuthFlags,
}
static examples = [
{ description: 'create an OAuth-In app from prompted input', command: 'smartthings apps:create' },
{ description: 'create an app defined in "my-app.yaml"', command: 'smartthings apps:create -i my-app.yaml' },
{
description: 'create an app defined in "my-app.json" and then authorize it\n' +
'(See "smartthings apps:authorize" for more information on authorization.)',
command: 'smartthings apps:create -i my-app.json --authorize',
},
]
async run(): Promise<void> {
const createApp = async (_: void, data: AppCreateRequest): Promise<AppCreationResponse> => {
// TODO extract this authorization block out to util function and use in ./update.ts as well
if (this.flags.authorize) {
if (data.lambdaSmartApp) {
if (data.lambdaSmartApp.functions) {
const requests = data.lambdaSmartApp.functions.map((functionArn) => {
return addPermission(functionArn, this.flags.principal, this.flags.statement)
})
await Promise.all(requests)
}
} else {
throw new Errors.CLIError('Authorization is not applicable to WebHook SmartApps')
}
}
return this.client.apps.create(data)
}
const buildTableOutput = (data: AppCreationResponse): string => {
const basicInfo = this.tableGenerator.buildTableFromItem(data.app, tableFieldDefinitions)
const oauthInfo = data.oauthClientId || data.oauthClientSecret
? this.tableGenerator.buildTableFromItem(data, ['oauthClientId', 'oauthClientSecret'])
: undefined
return oauthInfo
? `Basic App Data:\n${basicInfo}\n\nOAuth Info (you will not be able to see the OAuth info again so please save it now!):\n${oauthInfo}`
: basicInfo
}
await inputAndOutputItem(this, { buildTableOutput }, createApp, userInputProcessor(this))
}
async getInputFromUser(): Promise<AppCreateRequest> {
const action = (await inquirer.prompt({
type: 'list',
name: 'action',
message: 'What kind of app do you want to create? (Currently, only OAuth-In apps are supported.)',
choices: [
{ name: 'OAuth-In App', value: 'oauth-in' },
{ name: 'Cancel', value: 'cancel' },
],
default: 'oauth-in',
})).action
if (action === 'oauth-in') {
return createFromUserInput(this, oauthAppCreateRequestInputDefinition, { dryRun: this.flags['dry-run'] })
} else {
this.cancel()
}
}
}