Skip to content
This repository was archived by the owner on Jan 15, 2025. It is now read-only.

Commit 20d4599

Browse files
authored
Merge branch 'master' into emilio/luis
2 parents cb1c818 + 959b607 commit 20d4599

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+5756
-1228
lines changed

packages/cli/scripts/postinstall.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const getUserConfig = async () => {
3636
const promptTelemetry = async () => {
3737
try {
3838
const userConfig = await getUserConfig()
39+
userConfig.lastVersionCheck = new Date()
3940
if (userConfig.telemetry === null) {
4041
const disableTelemetry = await cli.prompt(chalk.red('Telemetry is disabled. Would you like to opt in? Only command and flags usage will be sent. (Y/N)'))
4142
if (disableTelemetry === 'Y' || disableTelemetry === 'y') {
@@ -53,7 +54,6 @@ const getUserConfig = async () => {
5354
}
5455

5556
await fs.mkdirp(pathToConfigJson)
56-
5757
await fs.writeFile(path.join(pathToConfigJson, 'config.json'), JSON.stringify(userConfig, null, 2))
5858
}
5959
/* tslint:disable:no-unused */

packages/cli/src/hooks/init/inithook.ts

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,64 @@ const latestVersion = require('latest-version')
77
const semver = require('semver')
88

99
const hook: Hook<'init'> = async function (opts) {
10-
// Look for updates
11-
try {
12-
const latest = await latestVersion(opts.config.name, {version: `>${opts.config.version}`})
13-
if (semver.gt(latest, opts.config.version)) {
14-
this.log('Update available ')
15-
this.log(' Run ')
16-
this.log(`npm i -g ${opts.config.name} `)
17-
}
18-
/* tslint:disable:no-unused */
19-
} catch (err) {
20-
// swallow the exception; we don't want to crash the app
21-
// on a failed attempt to check version
10+
// get config settings
11+
let userConfig: any
12+
const curDateTime = new Date()
13+
const configFileExists = fs.existsSync(path.join(this.config.configDir, 'config.json'))
14+
15+
const writeUserConfig = async (userconfig: any) => {
16+
await fs.mkdirp(this.config.configDir)
17+
await fs.writeFile(path.join(this.config.configDir, 'config.json'), JSON.stringify(userconfig, null, 2))
2218
}
2319

24-
// Ensure telemetry is set
2520
try {
26-
let userConfig: any = ''
27-
if (fs.existsSync(path.join(this.config.configDir, 'config.json'))) {
21+
// if config file exists, load settings
22+
if (configFileExists) {
2823
userConfig = await fs.readJSON(path.join(this.config.configDir, 'config.json'))
2924
} else {
25+
// otherwise create in-memory config
3026
userConfig = {
3127
telemetry: null,
28+
lastVersionCheck: null
29+
}
30+
}
31+
32+
const checkForUpdate = async () => {
33+
const latest = await latestVersion(opts.config.name, {version: `>${opts.config.version}`})
34+
if (semver.gt(latest, opts.config.version)) {
35+
this.log('Update available ')
36+
this.log(' Run ')
37+
this.log(`npm i -g ${opts.config.name} `)
3238
}
3339
}
3440

41+
const updateUserConfig = async (curVersionCheck: Date) => {
42+
userConfig.lastVersionCheck = curVersionCheck
43+
await writeUserConfig(userConfig)
44+
}
45+
46+
const isToday = (dateObj: Date | null, today: Date) => {
47+
return dateObj && dateObj.getDate() === today.getDate() &&
48+
dateObj.getMonth() === today.getMonth() &&
49+
dateObj.getFullYear() === today.getFullYear()
50+
}
51+
52+
// if there's no timestamp in config, create one and check for updates
53+
// if there is a timestamp in config and it's not from today, check for updates
54+
const lastCheck = userConfig.lastVersionCheck ? new Date(userConfig.lastVersionCheck) : null
55+
if (!isToday(lastCheck, curDateTime)) {
56+
await checkForUpdate()
57+
await updateUserConfig(curDateTime)
58+
}
59+
60+
/* tslint:disable:no-unused */
61+
} catch (err) {
62+
// swallow the exception; we don't want to crash the app
63+
// on a failed attempt to check version
64+
}
65+
66+
// Ensure telemetry is set
67+
try {
3568
if (userConfig.telemetry === null) {
3669
const disableTelemetry = await cli.prompt(chalk.red('Telemetry is disabled. Would you like to opt in?. Only command and flags usage will be sent. (Y/N)'))
3770
if (disableTelemetry === 'Y' || disableTelemetry === 'y') {
@@ -48,9 +81,7 @@ const hook: Hook<'init'> = async function (opts) {
4881
this.log(chalk.blue('bf config:telemetry:enable'))
4982
}
5083

51-
await fs.mkdirp(this.config.configDir)
52-
53-
await fs.writeFileSync(path.join(this.config.configDir, 'config.json'), JSON.stringify(userConfig, null, 2))
84+
await writeUserConfig(userConfig)
5485
}
5586

5687
this.config.pjson.telemetry = userConfig.telemetry

packages/cli/test/commands/inithook.test.ts

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const path = require('path')
55
const semver = require('semver')
66
const os = require('os')
77

8+
const lastversioncheck = path.join(__dirname, '../fixtures/lastversioncheck')
89
const upgradeavailable = path.join(__dirname, '../fixtures/upgradeavailable')
910
const rootTelemetryNull = path.join(__dirname, '../fixtures/telemetrynull')
1011

@@ -89,13 +90,13 @@ describe('Check if telemetry is not changed if config is not null', () => {
8990
})
9091

9192
describe('Update available to stdout', () => {
92-
before(function() {
93+
beforeEach(function() {
9394
// runs before all tests in this block
9495
fs.mkdirSync(pathToConfigJsonUpdate)
95-
fs.writeFileSync(path.join(pathToConfigJsonUpdate, 'config.json'), JSON.stringify({telemetry: true,}, null, 2))
96+
fs.writeFileSync(path.join(pathToConfigJsonUpdate, 'config.json'), JSON.stringify({telemetry: true}, null, 2))
9697
});
9798

98-
after(function() {
99+
afterEach(function() {
99100
// runs after all tests in this block
100101
fs.removeSync(pathToConfigJsonUpdate)
101102
});
@@ -110,11 +111,50 @@ describe('Update available to stdout', () => {
110111
})
111112
.it('it should output to stdout if update is available')
112113

113-
test
114+
test
114115
.loadConfig({root: upgradeavailable})
115116
.stub(semver, 'gt', () => false)
116117
.stdout()
117118
.hook('init', {argv: ['arg']}, {root: upgradeavailable})
118119
.do(output => expect(output.stdout).to.equal(''))
119120
.it('it should not output anything if no update is available')
121+
122+
test
123+
.loadConfig({root: lastversioncheck})
124+
.stub(semver, 'gt', () => true)
125+
.stdout()
126+
.hook('init', {argv: ['arg']}, {root: lastversioncheck})
127+
.do(output => {
128+
expect(output.stdout).to.contain('Update available')
129+
})
130+
.it('it should write to message stdout if update is available and last checked timestamp is expired')
131+
120132
})
133+
134+
describe('bypass version update if it\'s already been checked today', async () => {
135+
beforeEach(async () => {
136+
// runs before all tests in this block
137+
const today = new Date()
138+
let userConfig = await fs.readJSON(path.join(lastversioncheck, 'package.json'))
139+
userConfig.lastversioncheck = today
140+
fs.writeFileSync(path.join(lastversioncheck, 'package.json'), JSON.stringify(userConfig, null, 2))
141+
fs.mkdirSync(pathToConfigJsonUpdate)
142+
fs.writeFileSync(path.join(pathToConfigJsonUpdate, 'config.json'), JSON.stringify({telemetry: true}, null, 2))
143+
});
144+
145+
afterEach(function() {
146+
// runs after all tests in this block
147+
fs.removeSync(pathToConfigJsonUpdate)
148+
});
149+
150+
test
151+
.loadConfig({root: lastversioncheck})
152+
.stub(semver, 'gt', () => true)
153+
.stdout()
154+
.hook('init', {argv: ['arg']}, {root: lastversioncheck})
155+
.do(output => {
156+
expect(output.stdout).to.contain('')
157+
})
158+
.it('it should not output anything if last upgrade check was today')
159+
160+
});
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "botbuilder",
3+
"version": "1.0.0",
4+
"lastVersionCheck": "2019-09-23T19:27:24.987Z",
5+
"files": [],
6+
"oclif": {
7+
"commands": "../../../src/commands",
8+
"bin": "bf",
9+
"plugins": [
10+
"@oclif/plugin-help",
11+
"@microsoft/bf-chatdown"
12+
],
13+
"hooks": {
14+
"init": "../../../src/hooks/init/inithook"
15+
}
16+
},
17+
"lastversioncheck": "2019-09-27T23:36:07.377Z"
18+
}

packages/luis/src/commands/luis/convert.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export default class LuisConvert extends Command {
5252
result.desc = flags.desc || result.desc || ''
5353
result.culture = flags.culture || result.culture || 'en-us'
5454
result.culture = result.culture.toLowerCase()
55+
if (result.flatListOfEntityAndRoles) delete result.flatListOfEntityAndRoles
5556
result = JSON.stringify(result, null, 2)
5657
}
5758

packages/luis/src/commands/luis/generate/cs.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Command, flags} from '@microsoft/bf-cli-command'
1+
import {CLIError, Command, flags} from '@microsoft/bf-cli-command'
22
import {camelCase, upperFirst} from 'lodash'
33
import * as path from 'path'
44

@@ -14,6 +14,7 @@ export default class LuisGenerateCs extends Command {
1414
in: flags.string({description: 'Source .lu file(s) or LUIS application JSON model'}),
1515
out: flags.string({description: 'Output file or folder name. If not specified stdout will be used as output', default: ''}),
1616
className: flags.string({description: 'Name of the class'}),
17+
force: flags.boolean({description: 'If --in flag provided with the path to an existing file, overwrites it', default: false}),
1718
}
1819

1920
reorderEntities(app: any, name: string): void {
@@ -28,7 +29,12 @@ export default class LuisGenerateCs extends Command {
2829
let stdInput = await this.readStdin()
2930

3031
const pathPrefix = path.isAbsolute(flags.in) ? '' : process.cwd()
31-
const app = stdInput ? JSON.parse(stdInput as string) : await fs.readJSON(path.join(pathPrefix, flags.in))
32+
let app: any
33+
try {
34+
app = stdInput ? JSON.parse(stdInput as string) : await fs.readJSON(path.join(pathPrefix, flags.in))
35+
} catch (err) {
36+
throw new CLIError(err)
37+
}
3238

3339
flags.className = flags.className || app.name
3440
flags.className = upperFirst(camelCase(flags.className))
@@ -46,7 +52,7 @@ export default class LuisGenerateCs extends Command {
4652
this.reorderEntities(app, 'patternAnyEntities')
4753
this.reorderEntities(app, 'composites')
4854

49-
const outputPath = Utils.validatePath(flags.out, process.cwd(), flags.className + '.cs')
55+
const outputPath = Utils.validatePath(flags.out, process.cwd(), flags.className + '.cs', flags.force)
5056

5157
this.log(
5258
`Generating file at ${outputPath || ''} that contains class ${space}.${flags.className}.`

packages/luis/src/commands/luis/generate/ts.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Command, flags} from '@microsoft/bf-cli-command'
1+
import {CLIError, Command, flags} from '@microsoft/bf-cli-command'
22
import {camelCase, kebabCase, upperFirst} from 'lodash'
33
import * as path from 'path'
44

@@ -14,6 +14,7 @@ export default class LuisGenerateTs extends Command {
1414
in: flags.string({description: 'Source .lu file(s) or LUIS application JSON model'}),
1515
out: flags.string({description: 'Output file or folder name. If not specified stdout will be used as output', default: ''}),
1616
className: flags.string({description: 'Name of the class'}),
17+
force: flags.boolean({description: 'If --in flag provided with the path to an existing file, overwrites it', default: false}),
1718
}
1819

1920
reorderEntities(app: any, name: string): void {
@@ -27,7 +28,12 @@ export default class LuisGenerateTs extends Command {
2728
let stdInput = await this.readStdin()
2829

2930
const pathPrefix = path.isAbsolute(flags.in) ? '' : process.cwd()
30-
const app = stdInput ? JSON.parse(stdInput as string) : await fs.readJSON(path.join(pathPrefix, flags.in))
31+
let app: any
32+
try {
33+
app = stdInput ? JSON.parse(stdInput as string) : await fs.readJSON(path.join(pathPrefix, flags.in))
34+
} catch (err) {
35+
throw new CLIError(err)
36+
}
3137

3238
flags.className = flags.className || app.name
3339
flags.className = upperFirst(camelCase(flags.className))
@@ -39,7 +45,7 @@ export default class LuisGenerateTs extends Command {
3945
this.reorderEntities(app, 'patternAnyEntities')
4046
this.reorderEntities(app, 'composites')
4147

42-
const outputPath = Utils.validatePath(flags.out, process.cwd(), kebabCase(flags.className) + '.ts')
48+
const outputPath = Utils.validatePath(flags.out, process.cwd(), kebabCase(flags.className) + '.ts', flags.force)
4349

4450
this.log(
4551
`Generating file at ${outputPath || ''} that contains class ${flags.className}.`

packages/luis/src/commands/luis/translate.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export default class LuisTranslate extends Command {
6666
for (let file in translatedObject) {
6767
for (let lng in translatedObject[file]) {
6868
filePath = await fileHelper.generateNewTranslatedFilePath(file, lng, out)
69-
await fs.writeFile(filePath, translatedObject[path.basename(file)][lng], 'utf-8')
69+
await fs.writeFile(filePath, translatedObject[path.basename(file)][lng][0], 'utf-8')
7070
}
7171
}
7272
} catch (err) {

0 commit comments

Comments
 (0)