From dbe20f6baa30f0b039ef21482636c97353d5833c Mon Sep 17 00:00:00 2001 From: leileizhang Date: Mon, 27 Jul 2020 22:28:26 +0800 Subject: [PATCH 1/3] fix: loop infinitely on publish page when get publish status (#3716) * Regression on publish page after recoil * clean some code * fix object can only read --- .../src/recoilModel/dispatchers/publisher.ts | 47 +++++++++---------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/Composer/packages/client/src/recoilModel/dispatchers/publisher.ts b/Composer/packages/client/src/recoilModel/dispatchers/publisher.ts index e1be835b04..7bab646389 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/publisher.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/publisher.ts @@ -53,12 +53,7 @@ export const publisherDispatcher = () => { set(publishTypesState, data); }; - const updatePublishStatus = async ( - { set, snapshot }: CallbackInterface, - projectId: string, - target: any, - data: any - ) => { + const updatePublishStatus = ({ set }: CallbackInterface, projectId: string, target: any, data: any) => { const { endpointURL, status, id } = data; // the action below only applies to when a bot is being started using the "start bot" button // a check should be added to this that ensures this ONLY applies to the "default" profile. @@ -70,27 +65,26 @@ export const publisherDispatcher = () => { })); } - const publishHistory = await snapshot.getPromise(publishHistoryState); - const history = { ...data, target: target }; - const historys = publishHistory[target.name]; - let tempHistorys = historys ? [...historys] : []; - // if no history exists, create one with the latest status - // otherwise, replace the latest publish history with this one - if (!historys && status !== 404) { - tempHistorys = [history]; - } else if (status !== 404) { - // make sure this status payload represents the same item as item 0 (most of the time) - // otherwise, prepend it to the list to indicate a NEW publish has occurred since last loading history - if (tempHistorys.length && tempHistorys[0].id === id) { - tempHistorys.splice(0, 1, history); - } else { - tempHistorys.unshift(history); - } + if (status !== 404) { + set(publishHistoryState, (publishHistory) => { + const currentHistory = { ...data, target: target }; + let targetHistories = publishHistory[target.name] ? [...publishHistory[target.name]] : []; + // if no history exists, create one with the latest status + // otherwise, replace the latest publish history with this one + if (!targetHistories) { + targetHistories = [currentHistory]; + } else { + // make sure this status payload represents the same item as item 0 (most of the time) + // otherwise, prepend it to the list to indicate a NEW publish has occurred since last loading history + if (targetHistories.length && targetHistories[0].id === id) { + targetHistories[0] = currentHistory; + } else { + targetHistories.unshift(currentHistory); + } + } + return { ...publishHistory, [target.name]: targetHistories }; + }); } - set(publishHistoryState, (publishHistory) => ({ - ...publishHistory, - [target.name]: tempHistorys, - })); }; const getPublishTargetTypes = useRecoilCallback((callbackHelpers: CallbackInterface) => async () => { @@ -160,6 +154,7 @@ export const publisherDispatcher = () => { const response = await httpClient.get(`/publish/${projectId}/status/${target.name}`); updatePublishStatus(callbackHelpers, projectId, target, response.data); } catch (err) { + console.log(err); updatePublishStatus(callbackHelpers, projectId, target, err.response.data); } } From da3e9701aba58f37cb01c797e9f70fe15567610c Mon Sep 17 00:00:00 2001 From: VanyLaw Date: Tue, 28 Jul 2020 04:35:32 +0800 Subject: [PATCH 2/3] fix: fix deploy issue because botproject files structure changed (#3719) * fix files structure change * add botProject interface and move some interface into shared folder * fix lint Co-authored-by: Chris Whitten --- .../TestController/publishDialog.tsx | 2 +- .../client/src/pages/publish/Publish.tsx | 2 +- .../src/pages/publish/createPublishTarget.tsx | 3 +- .../src/pages/skills/skill-settings.tsx | 3 +- .../client/src/recoilModel/atoms/botState.ts | 4 +- .../src/recoilModel/dispatchers/project.ts | 3 +- .../src/recoilModel/dispatchers/setting.ts | 4 +- .../persistence/FilePersistence.ts | 4 +- .../packages/client/src/recoilModel/types.ts | 49 ++++--------------- .../extensions/plugin-loader/src/types.ts | 17 ++++--- .../packages/lib/shared/src/types/server.ts | 38 ++++++++++++++ .../packages/lib/shared/src/types/settings.ts | 39 +++++++++++++++ .../server/src/controllers/publisher.ts | 20 ++++---- .../server/src/models/bot/botProject.ts | 6 +-- .../server/src/models/bot/interface.ts | 21 -------- .../server/src/models/bot/luPublisher.ts | 3 +- .../azureFunctionsPublish/src/index.ts | 9 ++-- Composer/plugins/azurePublish/src/index.ts | 10 ++-- 18 files changed, 131 insertions(+), 106 deletions(-) diff --git a/Composer/packages/client/src/components/TestController/publishDialog.tsx b/Composer/packages/client/src/components/TestController/publishDialog.tsx index 958470f95b..7d44f10a86 100644 --- a/Composer/packages/client/src/components/TestController/publishDialog.tsx +++ b/Composer/packages/client/src/components/TestController/publishDialog.tsx @@ -14,10 +14,10 @@ import { IconButton } from 'office-ui-fabric-react/lib/Button'; import { Stack } from 'office-ui-fabric-react/lib/Stack'; import { TooltipHost } from 'office-ui-fabric-react/lib/Tooltip'; import formatMessage from 'format-message'; +import { ILuisConfig } from '@bfc/shared'; import { Text, Tips, Links, nameRegex } from '../../constants'; import { FieldConfig, useForm } from '../../hooks/useForm'; -import { ILuisConfig } from '../../recoilModel/types'; // -------------------- Styles -------------------- // const textFieldLabel = css` diff --git a/Composer/packages/client/src/pages/publish/Publish.tsx b/Composer/packages/client/src/pages/publish/Publish.tsx index 4303e1b6c5..0134579a22 100644 --- a/Composer/packages/client/src/pages/publish/Publish.tsx +++ b/Composer/packages/client/src/pages/publish/Publish.tsx @@ -9,10 +9,10 @@ import formatMessage from 'format-message'; import { Dialog, DialogType } from 'office-ui-fabric-react/lib/Dialog'; import { TextField } from 'office-ui-fabric-react/lib/TextField'; import { useRecoilValue } from 'recoil'; +import { PublishTarget } from '@bfc/shared'; import settingsStorage from '../../utils/dialogSettingStorage'; import { projectContainer } from '../design/styles'; -import { PublishTarget } from '../../recoilModel/types'; import { settingsState, botNameState, diff --git a/Composer/packages/client/src/pages/publish/createPublishTarget.tsx b/Composer/packages/client/src/pages/publish/createPublishTarget.tsx index 89416855f6..343caf91d3 100644 --- a/Composer/packages/client/src/pages/publish/createPublishTarget.tsx +++ b/Composer/packages/client/src/pages/publish/createPublishTarget.tsx @@ -11,8 +11,9 @@ import { Fragment, useState, useMemo } from 'react'; import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button'; import { JsonEditor } from '@bfc/code-editor'; import { useRecoilValue } from 'recoil'; +import { PublishTarget } from '@bfc/shared'; -import { PublishTarget, PublishType } from '../../recoilModel/types'; +import { PublishType } from '../../recoilModel/types'; import { userSettingsState } from '../../recoilModel'; import { label } from './styles'; diff --git a/Composer/packages/client/src/pages/skills/skill-settings.tsx b/Composer/packages/client/src/pages/skills/skill-settings.tsx index 861ea0b7c1..3a1aabe6da 100644 --- a/Composer/packages/client/src/pages/skills/skill-settings.tsx +++ b/Composer/packages/client/src/pages/skills/skill-settings.tsx @@ -6,8 +6,7 @@ import { jsx } from '@emotion/core'; import React, { useState, useEffect } from 'react'; import formatMessage from 'format-message'; import { TextField } from 'office-ui-fabric-react/lib/TextField'; - -import { DialogSetting } from '../../recoilModel/types'; +import { DialogSetting } from '@bfc/shared'; import { FormFieldAlignHorizontalBotSettings } from './styles'; diff --git a/Composer/packages/client/src/recoilModel/atoms/botState.ts b/Composer/packages/client/src/recoilModel/atoms/botState.ts index 7758e90fe8..13685624e5 100644 --- a/Composer/packages/client/src/recoilModel/atoms/botState.ts +++ b/Composer/packages/client/src/recoilModel/atoms/botState.ts @@ -2,11 +2,11 @@ // Licensed under the MIT License. import { atom } from 'recoil'; -import { DialogInfo, Diagnostic, LgFile, LuFile, BotSchemas, Skill } from '@bfc/shared'; +import { DialogInfo, Diagnostic, LgFile, LuFile, BotSchemas, Skill, DialogSetting } from '@bfc/shared'; import { BotLoadError, DesignPageLocation } from '../../recoilModel/types'; -import { PublishType, DialogSetting, BreadcrumbItem } from './../../recoilModel/types'; +import { PublishType, BreadcrumbItem } from './../../recoilModel/types'; import { BotStatus } from './../../constants'; const getFullyQualifiedKey = (value: string) => { diff --git a/Composer/packages/client/src/recoilModel/dispatchers/project.ts b/Composer/packages/client/src/recoilModel/dispatchers/project.ts index 40cc19b14e..9b4ae7b751 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/project.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/project.ts @@ -2,7 +2,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. import { useRecoilCallback, CallbackInterface } from 'recoil'; -import { dereferenceDefinitions, LuFile, DialogInfo, SensitiveProperties } from '@bfc/shared'; +import { dereferenceDefinitions, LuFile, DialogInfo, SensitiveProperties, DialogSetting } from '@bfc/shared'; import { indexer, validateDialog } from '@bfc/indexers'; import objectGet from 'lodash/get'; import objectSet from 'lodash/set'; @@ -15,7 +15,6 @@ import httpClient from '../../utils/httpUtil'; import { BotStatus } from '../../constants'; import { getReferredFiles } from '../../utils/luUtil'; import luFileStatusStorage from '../../utils/luFileStatusStorage'; -import { DialogSetting } from '../../recoilModel/types'; import settingStorage from '../../utils/dialogSettingStorage'; import filePersistence from '../persistence/FilePersistence'; import { navigateTo } from '../../utils/navigation'; diff --git a/Composer/packages/client/src/recoilModel/dispatchers/setting.ts b/Composer/packages/client/src/recoilModel/dispatchers/setting.ts index 2201c98ee7..910556a9b9 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/setting.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/setting.ts @@ -3,14 +3,12 @@ /* eslint-disable react-hooks/rules-of-hooks */ import { CallbackInterface, useRecoilCallback } from 'recoil'; -import { SensitiveProperties } from '@bfc/shared'; +import { SensitiveProperties, DialogSetting, PublishTarget } from '@bfc/shared'; import get from 'lodash/get'; import has from 'lodash/has'; import settingStorage from '../../utils/dialogSettingStorage'; import { settingsState } from '../atoms/botState'; -import { DialogSetting, PublishTarget } from '../../recoilModel/types'; - export const settingsDispatcher = () => { const setSettings = useRecoilCallback<[string, DialogSetting], Promise>( ({ set }: CallbackInterface) => async (projectId: string, settings: DialogSetting) => { diff --git a/Composer/packages/client/src/recoilModel/persistence/FilePersistence.ts b/Composer/packages/client/src/recoilModel/persistence/FilePersistence.ts index 16ccedb084..e965f810c5 100644 --- a/Composer/packages/client/src/recoilModel/persistence/FilePersistence.ts +++ b/Composer/packages/client/src/recoilModel/persistence/FilePersistence.ts @@ -3,9 +3,7 @@ import keys from 'lodash/keys'; import differenceWith from 'lodash/differenceWith'; import isEqual from 'lodash/isEqual'; -import { DialogInfo } from '@bfc/shared'; - -import { DialogSetting } from '../../recoilModel/types'; +import { DialogInfo, DialogSetting } from '@bfc/shared'; import { SkillManifest } from './../../pages/design/exportSkillModal/constants'; import { LuFile, LgFile } from './../../../../lib/shared/src/types/indexers'; diff --git a/Composer/packages/client/src/recoilModel/types.ts b/Composer/packages/client/src/recoilModel/types.ts index 845c3d5c09..c7fa5e96b0 100644 --- a/Composer/packages/client/src/recoilModel/types.ts +++ b/Composer/packages/client/src/recoilModel/types.ts @@ -1,7 +1,15 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. import { JSONSchema7 } from '@bfc/extension'; -import { AppUpdaterSettings, CodeEditorSettings, DialogInfo, LuFile, LgFile, PromptTab } from '@bfc/shared'; +import { + AppUpdaterSettings, + CodeEditorSettings, + DialogInfo, + LuFile, + LgFile, + PromptTab, + DialogSetting, +} from '@bfc/shared'; import { AppUpdaterStatus } from '../constants'; @@ -58,17 +66,6 @@ export interface BotLoadError { link?: { url: string; text: string }; } -export interface ILuisConfig { - name: string; - authoringKey: string; - endpointKey: string; - endpoint: string; - authoringEndpoint: string; - authoringRegion: string | 'westus'; - defaultLanguage: string | 'en-us'; - environment: string | 'composer'; -} - export interface DesignPageLocation { projectId: string; dialogId: string; @@ -86,40 +83,12 @@ export interface AppUpdateState { version?: string; } -export interface PublishTarget { - name: string; - type: string; - configuration: string; - lastPublished?: Date; -} - export interface BreadcrumbItem { dialogId: string; selected: string; focused: string; } -export interface DialogSetting { - MicrosoftAppId?: string; - MicrosoftAppPassword?: string; - luis: ILuisConfig; - publishTargets?: PublishTarget[]; - runtime: { - customRuntime: boolean; - path: string; - command: string; - }; - defaultLanguage: string; - languages: string[]; - skill?: { - name: string; - manifestUrl: string; - }[]; - botId?: string; - skillHostEndpoint?: string; - [key: string]: unknown; -} - export type dialogPayload = { id: string; content: any; diff --git a/Composer/packages/extensions/plugin-loader/src/types.ts b/Composer/packages/extensions/plugin-loader/src/types.ts index ba8d174e35..d6795e26a1 100644 --- a/Composer/packages/extensions/plugin-loader/src/types.ts +++ b/Composer/packages/extensions/plugin-loader/src/types.ts @@ -2,7 +2,7 @@ // Licensed under the MIT License. import { RequestHandler } from 'express-serve-static-core'; import { JSONSchema7 } from 'json-schema'; - +import { IBotProject } from '@bfc/shared'; // TODO: this will be possible when ifilestorage is in a shared module // import { IFileStorage } from '../../../server/src/models/storage/interface'; @@ -37,16 +37,21 @@ export interface BotTemplate { // TODO: Add types for project, metadata export interface PublishPlugin { - publish: (config: Config, project: any, metadata: any, user?: UserIdentity) => Promise; - getStatus?: (config: Config, project: any, user?: UserIdentity) => Promise; - getHistory?: (config: Config, project: any, user?: UserIdentity) => Promise; - rollback?: (config: Config, project: any, rollbackToVersion: string, user?: UserIdentity) => Promise; + publish: (config: Config, project: IBotProject, metadata: any, user?: UserIdentity) => Promise; + getStatus?: (config: Config, project: IBotProject, user?: UserIdentity) => Promise; + getHistory?: (config: Config, project: IBotProject, user?: UserIdentity) => Promise; + rollback?: ( + config: Config, + project: IBotProject, + rollbackToVersion: string, + user?: UserIdentity + ) => Promise; [key: string]: any; } export interface RuntimeTemplate { /** method used to eject the runtime into a project. returns resulting path of runtime! */ - eject: (project: any, localDisk?: any) => Promise; + eject: (project: IBotProject, localDisk?: any) => Promise; /** internal use key */ key: string; diff --git a/Composer/packages/lib/shared/src/types/server.ts b/Composer/packages/lib/shared/src/types/server.ts index 1407ec1358..07fbfd4087 100644 --- a/Composer/packages/lib/shared/src/types/server.ts +++ b/Composer/packages/lib/shared/src/types/server.ts @@ -1,5 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +import { Skill, FileInfo } from './indexers'; +import { Diagnostic } from './diagnostic'; +import { DialogSetting } from './settings'; export interface ProjectTemplate { id: string; @@ -12,3 +15,38 @@ export interface ProjectTemplate { /* list of supported runtime versions */ support?: string[]; } + +export interface IBotProject { + fileStorage: any; + dir: string; + dataDir: string; + id: string | undefined; + name: string; + luPublisher: any; + defaultSDKSchema: { + [key: string]: string; + }; + defaultUISchema: { + [key: string]: string; + }; + skills: Skill[]; + diagnostics: Diagnostic[]; + settingManager: ISettingManager; + settings: DialogSetting | null; + getProject: () => { + botName: string; + files: FileInfo[]; + location: string; + schemas: any; + skills: Skill[]; + diagnostics: Diagnostic[]; + settings: DialogSetting | null; + }; + [key: string]: any; +} + +export interface ISettingManager { + get(obfuscate?: boolean): Promise; + set(settings: any): Promise; + getFileName: () => string; +} diff --git a/Composer/packages/lib/shared/src/types/settings.ts b/Composer/packages/lib/shared/src/types/settings.ts index b646b6c923..51e787233b 100644 --- a/Composer/packages/lib/shared/src/types/settings.ts +++ b/Composer/packages/lib/shared/src/types/settings.ts @@ -18,3 +18,42 @@ export interface AppUpdaterSettings { autoDownload: boolean; useNightly: boolean; } + +export interface DialogSetting { + MicrosoftAppId?: string; + MicrosoftAppPassword?: string; + luis: ILuisConfig; + publishTargets?: PublishTarget[]; + runtime: { + customRuntime: boolean; + path: string; + command: string; + }; + defaultLanguage: string; + languages: string[]; + skill?: { + name: string; + manifestUrl: string; + }[]; + botId?: string; + skillHostEndpoint?: string; + [key: string]: any; +} + +export interface ILuisConfig { + name: string; + endpoint: string; + authoringKey: string; + endpointKey: string; + authoringEndpoint: string; + authoringRegion: string | 'westus'; + defaultLanguage: string | 'en-us'; + environment: string | 'composer'; +} + +export interface PublishTarget { + name: string; + type: string; + configuration: string; + lastPublished?: Date; +} diff --git a/Composer/packages/server/src/controllers/publisher.ts b/Composer/packages/server/src/controllers/publisher.ts index 8cc1c6c8a3..24cfd8d243 100644 --- a/Composer/packages/server/src/controllers/publisher.ts +++ b/Composer/packages/server/src/controllers/publisher.ts @@ -53,7 +53,7 @@ export const PublishController = { const profile = profiles.length ? profiles[0] : undefined; const method = profile ? profile.type : undefined; - if (profile && pluginLoader?.extensions?.publish[method]?.methods?.publish) { + if (profile && method && pluginLoader?.extensions?.publish[method]?.methods?.publish) { // append config from client(like sensitive settings) const configuration = { profileName: profile.name, @@ -105,6 +105,7 @@ export const PublishController = { const method = profile ? profile.type : undefined; if ( profile && + method && pluginLoader.extensions.publish[method] && pluginLoader.extensions.publish[method].methods && pluginLoader.extensions.publish[method].methods.getStatus @@ -152,6 +153,7 @@ export const PublishController = { if ( profile && + method && pluginLoader.extensions.publish[method] && pluginLoader.extensions.publish[method].methods && pluginLoader.extensions.publish[method].methods.history @@ -192,20 +194,20 @@ export const PublishController = { const profile = profiles.length ? profiles[0] : undefined; const method = profile ? profile.type : undefined; - // append config from client(like sensitive settings) - const configuration = { - profileName: profile.name, - fullSettings: merge({}, currentProject.settings, sensitiveSettings), - templatePath: path.resolve(runtimeFolder, DEFAULT_RUNTIME), - ...JSON.parse(profile.configuration), - }; - if ( profile && + method && pluginLoader.extensions.publish[method] && pluginLoader.extensions.publish[method].methods && pluginLoader.extensions.publish[method].methods.rollback ) { + // append config from client(like sensitive settings) + const configuration = { + profileName: profile.name, + fullSettings: merge({}, currentProject.settings, sensitiveSettings), + templatePath: path.resolve(runtimeFolder, DEFAULT_RUNTIME), + ...JSON.parse(profile.configuration), + }; // get the externally defined method const pluginMethod = pluginLoader.extensions.publish[method].methods.rollback; if (typeof pluginMethod === 'function') { diff --git a/Composer/packages/server/src/models/bot/botProject.ts b/Composer/packages/server/src/models/bot/botProject.ts index c66fbac466..dca2bf617e 100644 --- a/Composer/packages/server/src/models/bot/botProject.ts +++ b/Composer/packages/server/src/models/bot/botProject.ts @@ -6,7 +6,7 @@ import fs from 'fs'; import axios from 'axios'; import { autofixReferInDialog } from '@bfc/indexers'; -import { getNewDesigner, FileInfo, Skill, Diagnostic } from '@bfc/shared'; +import { getNewDesigner, FileInfo, Skill, Diagnostic, IBotProject, DialogSetting, ILuisConfig } from '@bfc/shared'; import { UserIdentity, pluginLoader } from '@bfc/plugin-loader'; import { Path } from '../../utility/path'; @@ -17,13 +17,11 @@ import { DefaultSettingManager } from '../settings/defaultSettingManager'; import log from '../../logger'; import { BotProjectService } from '../../services/project'; -import { ILuisConfig } from './interface'; import { ICrossTrainConfig } from './luPublisher'; import { IFileStorage } from './../storage/interface'; import { LocationRef } from './interface'; import { LuPublisher } from './luPublisher'; import { extractSkillManifestUrl } from './skillManager'; -import { DialogSetting } from './interface'; import { defaultFilePath, serializeFiles } from './botStructure'; const debug = log.extend('bot-project'); @@ -36,7 +34,7 @@ const oauthInput = () => ({ const defaultLanguage = 'en-us'; // default value for settings.defaultLanguage -export class BotProject { +export class BotProject implements IBotProject { public ref: LocationRef; // TODO: address need to instantiate id - perhaps do so in constructor based on Store.get(projectLocationMap) public id: string | undefined; diff --git a/Composer/packages/server/src/models/bot/interface.ts b/Composer/packages/server/src/models/bot/interface.ts index efdd4167c8..be6ebf8081 100644 --- a/Composer/packages/server/src/models/bot/interface.ts +++ b/Composer/packages/server/src/models/bot/interface.ts @@ -21,17 +21,6 @@ export enum FileUpdateType { DELETE = 'delete', } -export interface ILuisConfig { - name: string; - endpoint: string; - authoringKey: string; - endpointKey: string; - authoringEndpoint: string; - authoringRegion: string | 'westus'; - defaultLanguage: string | 'en-us'; - environment: string | 'composer'; -} - export interface IOperationLUFile { diagnostics?: any[]; // ludown parser output relativePath?: string; @@ -43,13 +32,3 @@ export interface IOperationLUFile { export interface ILuisStatusOperation { [key: string]: IOperationLUFile; } - -export interface DialogSetting { - MicrosoftAppId: string; - MicrosoftAppPassword: string; - luis: ILuisConfig; - skill: { manifestUrl: string; name: string }[]; - defaultLanguage: string; - languages: string[]; - [key: string]: any; -} diff --git a/Composer/packages/server/src/models/bot/luPublisher.ts b/Composer/packages/server/src/models/bot/luPublisher.ts index 0a258e07cc..6734b75757 100644 --- a/Composer/packages/server/src/models/bot/luPublisher.ts +++ b/Composer/packages/server/src/models/bot/luPublisher.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import { FileInfo } from '@bfc/shared'; +import { FileInfo, ILuisConfig } from '@bfc/shared'; import { Path } from '../../utility/path'; import { IFileStorage } from '../storage/interface'; @@ -9,7 +9,6 @@ import log from '../../logger'; import { ComposerReservoirSampler } from './sampler/ReservoirSampler'; import { ComposerBootstrapSampler } from './sampler/BootstrapSampler'; -import { ILuisConfig } from './interface'; // eslint-disable-next-line @typescript-eslint/no-var-requires const crossTrainer = require('@microsoft/bf-lu/lib/parser/cross-train/crossTrainer.js'); diff --git a/Composer/plugins/azureFunctionsPublish/src/index.ts b/Composer/plugins/azureFunctionsPublish/src/index.ts index cacdf2f453..76fc8cb46d 100644 --- a/Composer/plugins/azureFunctionsPublish/src/index.ts +++ b/Composer/plugins/azureFunctionsPublish/src/index.ts @@ -7,6 +7,7 @@ import { BotProjectDeploy } from '@bfc/botframeworkdeploy'; import { v4 as uuid } from 'uuid'; import md5 from 'md5'; import { copy, rmdir, emptyDir, readJson, pathExists, writeJson, mkdirSync, writeFileSync } from 'fs-extra'; +import { IBotProject } from '@bfc/shared'; import schema from './schema'; @@ -202,7 +203,7 @@ class AzurePublisher { /************************************************************************************************** * plugin methods *************************************************************************************************/ - publish = async (config: PublishConfig, project, metadata, user) => { + publish = async (config: PublishConfig, project: IBotProject, metadata, user) => { // templatePath point to the dotnet code const { fullSettings, @@ -219,7 +220,7 @@ class AzurePublisher { } = config; // point to the declarative assets (possibly in remote storage) - const botFiles = project.files; + const botFiles = project.getProject().files; // get the bot id from the project const botId = project.id; @@ -328,7 +329,7 @@ class AzurePublisher { } }; - getStatus = async (config: PublishConfig, project, user) => { + getStatus = async (config: PublishConfig, project: IBotProject, user) => { const profileName = config.profileName; const botId = project.id; // return latest status @@ -349,7 +350,7 @@ class AzurePublisher { } }; - history = async (config: PublishConfig, project, user) => { + history = async (config: PublishConfig, project: IBotProject, user) => { const profileName = config.profileName; const botId = project.id; return await this.getHistory(botId, profileName); diff --git a/Composer/plugins/azurePublish/src/index.ts b/Composer/plugins/azurePublish/src/index.ts index 02ea0693c3..d1d2180d12 100644 --- a/Composer/plugins/azurePublish/src/index.ts +++ b/Composer/plugins/azurePublish/src/index.ts @@ -7,9 +7,9 @@ import { BotProjectDeploy } from '@bfc/botframeworkdeploy'; import { v4 as uuid } from 'uuid'; import md5 from 'md5'; import { copy, rmdir, emptyDir, readJson, pathExists, writeJson, mkdirSync, writeFileSync } from 'fs-extra'; +import { IBotProject } from '@bfc/shared'; import schema from './schema'; - // This option controls whether the history is serialized to a file between sessions with Composer // set to TRUE for history to be saved to disk // set to FALSE for history to be cached in memory only @@ -218,7 +218,7 @@ class AzurePublisher { /************************************************************************************************** * plugin methods *************************************************************************************************/ - publish = async (config: PublishConfig, project, metadata, user) => { + publish = async (config: PublishConfig, project: IBotProject, metadata, user) => { // templatePath point to the dotnet code const { // these are provided by Composer @@ -238,7 +238,7 @@ class AzurePublisher { } = config; // point to the declarative assets (possibly in remote storage) - const botFiles = project.files; + const botFiles = project.getProject().files; // get the bot id from the project const botId = project.id; @@ -349,7 +349,7 @@ class AzurePublisher { } }; - getStatus = async (config: PublishConfig, project, user) => { + getStatus = async (config: PublishConfig, project: IBotProject, user) => { const profileName = config.profileName; const botId = project.id; // return latest status @@ -370,7 +370,7 @@ class AzurePublisher { } }; - history = async (config: PublishConfig, project, user) => { + history = async (config: PublishConfig, project: IBotProject, user) => { const profileName = config.profileName; const botId = project.id; return await this.getHistory(botId, profileName); From 9d20afb52570792373bc754f96aebe1fe3da4993 Mon Sep 17 00:00:00 2001 From: Ben Brown Date: Mon, 27 Jul 2020 16:01:47 -0500 Subject: [PATCH 3/3] fix: allow storage implementation to determine writability (#3630) * Allow the Storage implementation to determine if a folder is writable or not * Remove legacy fs check on writability Co-authored-by: Chris Whitten --- Composer/packages/server/src/models/storage/interface.ts | 1 + .../server/src/models/storage/localDiskStorage.ts | 8 ++++++++ Composer/packages/server/src/services/storage.ts | 8 +------- Composer/plugins/mongoStorage/src/index.ts | 4 ++++ Composer/plugins/mongoStorage/src/interface.ts | 1 + 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Composer/packages/server/src/models/storage/interface.ts b/Composer/packages/server/src/models/storage/interface.ts index e07aa7b049..f09775fb39 100644 --- a/Composer/packages/server/src/models/storage/interface.ts +++ b/Composer/packages/server/src/models/storage/interface.ts @@ -11,6 +11,7 @@ export interface StorageConnection { export interface Stat { isDir: boolean; isFile: boolean; + isWritable: boolean; lastModified: string; size: string; } diff --git a/Composer/packages/server/src/models/storage/localDiskStorage.ts b/Composer/packages/server/src/models/storage/localDiskStorage.ts index 7f3747afc5..675bb7b77a 100644 --- a/Composer/packages/server/src/models/storage/localDiskStorage.ts +++ b/Composer/packages/server/src/models/storage/localDiskStorage.ts @@ -25,9 +25,17 @@ const rename = promisify(fs.rename); export class LocalDiskStorage implements IFileStorage { async stat(path: string): Promise { const fstat = await stat(path); + // test to see if this file is writable + let writable = true; + try { + fs.accessSync(path, fs.constants.W_OK); + } catch (err) { + writable = false; + } return { isDir: fstat.isDirectory(), isFile: fstat.isFile(), + isWritable: writable, lastModified: fstat.mtime.toString(), size: fstat.isFile() ? fstat.size.toString() : '', }; diff --git a/Composer/packages/server/src/services/storage.ts b/Composer/packages/server/src/services/storage.ts index b6c828711d..efbbbd4d22 100644 --- a/Composer/packages/server/src/services/storage.ts +++ b/Composer/packages/server/src/services/storage.ts @@ -95,17 +95,11 @@ class StorageService { // TODO: fix this behavior and the upper layer interface accordingly return JSON.parse(await storageClient.readFile(filePath)); } else { - let writable = true; - try { - fs.accessSync(filePath, fs.constants.W_OK); - } catch (err) { - writable = false; - } return { name: Path.basename(filePath), parent: Path.dirname(filePath), children: await this.getChildren(storageClient, filePath), - writable: writable, + writable: stat.isWritable, }; } }; diff --git a/Composer/plugins/mongoStorage/src/index.ts b/Composer/plugins/mongoStorage/src/index.ts index b07af196dc..8149fc7a49 100644 --- a/Composer/plugins/mongoStorage/src/index.ts +++ b/Composer/plugins/mongoStorage/src/index.ts @@ -82,6 +82,7 @@ class MongoStorage implements IFileStorage { isDir: true, isFile: false, lastModified: file.lastModified, + isWritable: true, size: 1, // TODO: real size }); } else { @@ -89,6 +90,7 @@ class MongoStorage implements IFileStorage { isDir: false, isFile: true, lastModified: file.lastModified, + isWritable: true, size: 1, // TODO: real size }); } @@ -103,6 +105,7 @@ class MongoStorage implements IFileStorage { isDir: true, isFile: false, lastModified: new Date(), + isWritable: true, size: 0, // TODO: ?? }); } else { @@ -113,6 +116,7 @@ class MongoStorage implements IFileStorage { isDir: true, isFile: false, lastModified: file.lastModified, + isWritable: true, size: 0, // TODO: ?? }); } diff --git a/Composer/plugins/mongoStorage/src/interface.ts b/Composer/plugins/mongoStorage/src/interface.ts index 898fe94a88..2343218cac 100644 --- a/Composer/plugins/mongoStorage/src/interface.ts +++ b/Composer/plugins/mongoStorage/src/interface.ts @@ -11,6 +11,7 @@ export interface StorageConnection { export interface Stat { isDir: boolean; isFile: boolean; + isWritable: boolean; lastModified: string; size: string; }