Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support default path environment variable #1652

Merged
merged 33 commits into from
Nov 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
d762cc4
save tmp code
liweitian Nov 5, 2019
fc70cee
save tmp code
liweitian Nov 6, 2019
14180f3
save current path in data.json
liweitian Nov 6, 2019
4c44b47
initialize default path according to OS
liweitian Nov 6, 2019
b206858
use default path if current path does not work
liweitian Nov 7, 2019
f258e89
fix bug about updating last accessed path
liweitian Nov 11, 2019
bb67fcb
create default folder
liweitian Nov 11, 2019
8faff5b
handle comments
liweitian Nov 14, 2019
9497928
handle comments
liweitian Nov 15, 2019
7499ffc
fix lint error
liweitian Nov 19, 2019
078ccd0
fix test case
liweitian Nov 19, 2019
2877231
fix test case
liweitian Nov 19, 2019
0d1e5ed
merge data.json and data.template.json and support defautlpath enviro…
liweitian Nov 21, 2019
5c4c890
resolve default path
liweitian Nov 22, 2019
5f08ab2
use warped path utility
liweitian Nov 22, 2019
bcb6a1a
remove console.log
liweitian Nov 22, 2019
865e790
fix bug
liweitian Nov 25, 2019
88aacb5
fix bug when user set default path as D: rather than D:/
liweitian Nov 25, 2019
96b8348
Merge branch 'master' into liweitian/updateDefaultFolder
liweitian Nov 25, 2019
626da44
add debug logger
a-b-r-o-w-n Nov 25, 2019
79ee214
add COMPOSER_BOTS_FOLDER to env settings
a-b-r-o-w-n Nov 25, 2019
efc69e1
resolve default path in settings
a-b-r-o-w-n Nov 25, 2019
0a12d21
log all settings in debug mode
a-b-r-o-w-n Nov 25, 2019
cc82449
use default path from settings
a-b-r-o-w-n Nov 25, 2019
45141d4
move setting default bots folder to settings
a-b-r-o-w-n Nov 25, 2019
ee6b925
run migrations on start up
a-b-r-o-w-n Nov 25, 2019
f474533
Merge branch 'master' into liweitian/updateDefaultFolder
a-b-r-o-w-n Nov 25, 2019
6a6a433
use TestBots directory for e2e tests
a-b-r-o-w-n Nov 25, 2019
a3327d5
create COMPOSER_BOTS_FOLDER if it doesn't exist
a-b-r-o-w-n Nov 25, 2019
4dd4493
use debug log when creating new bots
a-b-r-o-w-n Nov 25, 2019
9af88d2
Merge branch 'master' into liweitian/updateDefaultFolder
a-b-r-o-w-n Nov 26, 2019
e62b5cf
fix bugs
liweitian Nov 26, 2019
0d7b759
fix issue accessing defaultFolder from development settings
a-b-r-o-w-n Nov 26, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Composer/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ junit.xml
cypress/screenshots
cypress/results
cypress/videos

TestBots/
2 changes: 1 addition & 1 deletion Composer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"test:coverage": "yarn test --coverage --no-cache --reporters=default",
"test:integration": "cypress run --browser chrome",
"test:integration:open": "cypress open",
"test:integration:clean": "rimraf ../MyBots/__Test* packages/server/data.json",
"test:integration:clean": "rimraf TestBots/*",
"lint": "wsrun --exclude-missing --collect-logs --report lint",
"lint:fix": "wsrun --exclude-missing --collect-logs --report lint:fix",
"typecheck": "concurrently --kill-others-on-fail \"npm:typecheck:*\"",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,18 @@ import path from 'path';
import { jsx } from '@emotion/core';
import { Fragment, useEffect, useState, useContext, useRef } from 'react';

import storage from '../../utils/storage';

import { FileSelector } from './FileSelector';
import { StoreContext } from './../../store';
import { FileTypes } from './../../constants';

const NEW_BOT_LOCATION_KEY = 'newBotLocation';

export function LocationSelectContent(props) {
const { state, actions } = useContext(StoreContext);
const { storages, focusedStorageFolder, storageFileLoadingStatus } = state;
const { onOpen, onChange, allowOpeningBot = true } = props;

const { fetchFolderItemsByPath } = actions;
const currentStorageIndex = useRef(0);
const [currentPath, setCurrentPath] = useState(storage.get(NEW_BOT_LOCATION_KEY, ''));
const [currentPath, setCurrentPath] = useState('');
const currentStorageId = storages[currentStorageIndex.current] ? storages[currentStorageIndex.current].id : 'default';

useEffect(() => {
Expand All @@ -39,6 +35,7 @@ export function LocationSelectContent(props) {
// const formatedPath = path.normalize(newPath.replace(/\\/g, '/'));
const formatedPath = path.normalize(newPath);
await fetchFolderItemsByPath(storageId, formatedPath);
await actions.updateCurrentPath(formatedPath);
setCurrentPath(formatedPath);
}
};
Expand All @@ -48,7 +45,7 @@ export function LocationSelectContent(props) {
let path = currentPath;
let id = '';
if (storages[index]) {
path = path || storages[index].path;
path = storages[index].path;
id = storages[index].id;
}
updateCurrentPath(path, id);
Expand All @@ -58,7 +55,6 @@ export function LocationSelectContent(props) {
if (onChange) {
onChange(currentPath);
}
storage.set(NEW_BOT_LOCATION_KEY, currentPath);
}, [currentPath]);

const onSelectionChanged = item => {
Expand Down
4 changes: 4 additions & 0 deletions Composer/packages/client/src/store/action/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,7 @@ export const fetchFolderItemsByPath: ActionCreator = async ({ dispatch }, id, pa
});
}
};

export const updateCurrentPath: ActionCreator = async ({ dispatch }, path) => {
await httpClient.put(`/storages/currentPath`, { path: path });
};
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ jest.mock('../../src/store/store', () => {
name: 'This PC',
type: 'LocalDisk',
path: '.',
defaultPath: '.',
},
],
recentBotProjects: [] as any[],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ jest.mock('../../src/store/store', () => {
name: 'This PC',
type: 'LocalDisk',
path: '.',
defaultPath: '.',
},
];
return {
Expand Down
8 changes: 4 additions & 4 deletions Composer/packages/server/src/controllers/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { BotProjectService } from '../services/project';
import AssectService from '../services/asset';
import { LocationRef } from '../models/bot/interface';
import StorageService from '../services/storage';
import settings from '../settings/settings.json';
import settings from '../settings';

import { Path } from './../utility/path';

Expand All @@ -21,7 +21,7 @@ async function createProject(req: Request, res: Response) {
}

// default the path to the default folder.
let path = settings.development.defaultFolder;
let path = settings.botsFolder;
// however, if path is specified as part of post body, use that one.
// this allows developer to specify a custom home for their bot.
if (location) {
Expand Down Expand Up @@ -116,7 +116,7 @@ async function saveProjectAs(req: Request, res: Response) {

const locationRef: LocationRef = {
storageId,
path: Path.resolve(settings.development.defaultFolder, name),
path: Path.resolve(settings.botsFolder, name),
};

try {
Expand Down Expand Up @@ -352,7 +352,7 @@ async function publishLuis(req: Request, res: Response) {

async function getAllProjects(req: Request, res: Response) {
const storageId = 'default';
const folderPath = Path.resolve(settings.development.defaultFolder);
const folderPath = Path.resolve(settings.botsFolder);
try {
res.status(200).json(await StorageService.getBlob(storageId, folderPath));
} catch (e) {
Expand Down
6 changes: 6 additions & 0 deletions Composer/packages/server/src/controllers/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ function createStorageConnection(req: Request, res: Response) {
res.status(200).json(StorageService.getStorageConnections());
}

function updateCurrentPath(req: Request, res: Response) {
StorageService.updateCurrentPath(req.body.path);
res.status(200).json('success');
}

async function getBlob(req: Request, res: Response) {
const storageId = req.params.storageId;
const reqpath = decodeURI(req.params.path);
Expand All @@ -34,4 +39,5 @@ export const StorageController = {
getStorageConnections,
createStorageConnection,
getBlob,
updateCurrentPath,
};
6 changes: 6 additions & 0 deletions Composer/packages/server/src/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import debug from 'debug';

export default debug('composer');
1 change: 1 addition & 0 deletions Composer/packages/server/src/router/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ router.post('/projects/opened/project/saveAs', ProjectController.saveProjectAs);
router.get('/projects/recent', ProjectController.getRecentProjects);

// storages
router.put('/storages/currentPath', StorageController.updateCurrentPath);
router.get('/storages', StorageController.getStorageConnections);
router.post('/storages', StorageController.createStorageConnection);
router.get('/storages/:storageId/blobs/:path(*)', StorageController.getBlob);
Expand Down
30 changes: 28 additions & 2 deletions Composer/packages/server/src/services/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class StorageService {

constructor() {
this.storageConnections = Store.get(this.STORE_KEY);
this.ensureDefaultBotFoldersExist();
}

public getStorageClient = (storageId: string): IFileStorage => {
Expand All @@ -39,11 +40,18 @@ class StorageService {
};

public getStorageConnections = (): StorageConnection[] => {
return this.storageConnections.map(s => {
const connections = this.storageConnections.map(s => {
const temp = Object.assign({}, s);
temp.path = Path.resolve(s.path); // resolve path if path is relative, and change it to unix pattern
// if the last accessed path exist
if (fs.existsSync(s.path)) {
temp.path = Path.resolve(s.path); // resolve path if path is relative, and change it to unix pattern
} else {
temp.path = Path.resolve(s.defaultPath);
}
return temp;
});
this.ensureDefaultBotFoldersExist();
return connections;
};

public checkBlob = async (storageId: string, filePath: string): Promise<boolean> => {
Expand Down Expand Up @@ -85,6 +93,17 @@ class StorageService {
}
};

public updateCurrentPath = (path: string) => {
this.storageConnections[0].path = path;
Store.set(this.STORE_KEY, this.storageConnections);
};

private ensureDefaultBotFoldersExist = () => {
this.storageConnections.forEach(s => {
this.createFolderRecurively(s.defaultPath);
});
};

private isBotFolder = (path: string) => {
// locate Main.dialog
const mainPath = Path.join(path, 'ComposerDialogs/Main', 'Main.dialog');
Expand Down Expand Up @@ -119,6 +138,13 @@ class StorageService {
const result = await Promise.all(children);
return result.filter(item => !!item);
};

private createFolderRecurively = (path: string) => {
if (!fs.existsSync(path)) {
this.createFolderRecurively(Path.dirname(path));
fs.mkdirSync(path);
}
};
}

const service = new StorageService();
Expand Down
7 changes: 7 additions & 0 deletions Composer/packages/server/src/settings/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,10 @@ export const absHosted = process.env.COMPOSER_AUTH_PROVIDER === 'abs-h';
export const absHostRoot = process.env.WEBSITE_HOSTNAME
? `https://${process.env.WEBSITE_HOSTNAME}`
: 'http://localhost:3978';

let folder = process.env.COMPOSER_BOTS_FOLDER;
if (folder && folder.endsWith(':')) {
folder = folder + '/';
}

export const botsFolder = folder;
19 changes: 15 additions & 4 deletions Composer/packages/server/src/settings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,28 @@

import merge from 'lodash/merge';

import settings from './settings.json';
import log from '../logger';

import settings from './settings';

// overall the guidance in settings.json is to list every item in "development"
// section with a default value, and override the value for different environment
// in later sections

interface Settings {
botAdminEndpoint: string;
botEndpoint: string;
assetsLibray: string;
runtimeFolder: string;
botsFolder: string;
}

const defaultSettings = settings.development;
const environment = process.env.NODE_ENV || 'development';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const environmentSettings = (settings as any)[environment];
const environmentSettings = settings[environment];

const finalSettings = merge<Settings, Settings>(defaultSettings, environmentSettings);

const finalSettings = merge(defaultSettings, environmentSettings);
log('App Settings: %O', finalSettings);

export default finalSettings;
12 changes: 0 additions & 12 deletions Composer/packages/server/src/settings/settings.json

This file was deleted.

21 changes: 21 additions & 0 deletions Composer/packages/server/src/settings/settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import os from 'os';

import { Path } from '../utility/path';

import { botsFolder } from './env';

export default {
development: {
botAdminEndpoint: 'http://localhost:3979',
botEndpoint: 'http://localhost:3979',
assetsLibray: Path.resolve('./assets'),
runtimeFolder: Path.resolve('../../../BotProject/Templates'),
botsFolder: botsFolder || Path.join(os.homedir(), 'Documents', 'Composer'),
},
container: {
botAdminEndpoint: 'http://botruntime:80',
},
};
5 changes: 3 additions & 2 deletions Composer/packages/server/src/store/data.template.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import path from 'path';
import settings from '../settings';

export default {
storageConnections: [
{
id: 'default',
name: 'This PC',
type: 'LocalDisk',
path: path.resolve(__dirname, '../../../../../MyBots'),
path: '', // this is used as last accessed path, if it is invalid, use defaultPath
defaultPath: settings.botsFolder,
},
],
recentBotProjects: [],
Expand Down
54 changes: 54 additions & 0 deletions Composer/packages/server/src/store/migrations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

/* eslint-disable @typescript-eslint/no-explicit-any */

import get from 'lodash/get';
import set from 'lodash/set';

import log from '../logger';
import settings from '../settings';

interface Migration {
/**
* Migration label. Will be printed to the console in debug.
* @example 'Update Designer to Composer'
*/
name: string;
/**
* Use to check if a condition exists that requires migration.
* @example data => data.name === 'Designer';
*/
condition: (data: any) => boolean;
/**
* Data transform to run if condition is met.
* @example data => ({ ...data, name: 'Composer' });
*/
run: (data: any) => any;
}

const migrations: Migration[] = [
{
name: 'Add defaultPath',
condition: data => get(data, 'storageConnections.0.defaultPath') !== settings.botsFolder,
run: data => set(data, 'storageConnections[0].defaultPath', settings.botsFolder),
},
];

export function runMigrations(initialData: any): any {
const migrationsToRun: Migration[] = migrations.filter(m => m.condition(initialData));
if (migrationsToRun.length > 0) {
log('migration: running migrations...');

const data = migrationsToRun.reduce((data, m, i) => {
log('migration: %s (%d / %d)', m.name, i + 1, migrationsToRun.length);
return m.run(data);
}, initialData);

log('migration: done!');

return data;
}

return initialData;
}
Loading