fix: check invalid provider options before project actions (#390)

* fix: optomise pdf loading

* fix: check for invalid connection provider options for project actions

* docs: add test case to test runbook

* style: add missing semi colons
This commit is contained in:
stew-ro 2020-07-08 11:06:08 -07:00 коммит произвёл GitHub
Родитель 2abb0cb062
Коммит 212647d432
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
14 изменённых файлов: 103 добавлений и 4 удалений

Просмотреть файл

@ -1,5 +1,33 @@
# Test Runbook
## Fix: check invalid connection provider options before project actions
> ### Feature description ###
- check connection provider options are valid before creating a project
- check connection provider options are valid before opening a recent project
> ### Use Case ###
**As** a user
**I want** a notification when I try to open or create a project with invalid provider options
**So** I know how to fix invalid provider options issue
> ### Acceptance criteria ###
#### Scenario One ####
**Given** I've created a connection with invalid provider options (e.g. invalid SAS token for Azure provider).
**When** I try to create a new project with that connection.
**Then** a notification will be displayed telling me my connection is invalid.
#### Scenario Two ####
**Given** I've created a connection with invalid provider options (e.g. invalid SAS token for Azure provider).
**When** I try to open a recent project that now has an invalid connection provider options (e.g. the Azure container was deleted)
**Then** a notification will be displayed telling me my connection is invalid.
___
## Feat: support distributable releasing
> ### Feature description ###

Просмотреть файл

@ -252,6 +252,7 @@ export const english: IAppStrings = {
instructions: "Please select a connection to edit",
new: "New Connection",
save: "Save Connection",
genericInvalid: "\"${project.sourceConnection.name}\" is an invalid connection. Please check it in the Connections page",
messages: {
saveSuccess: "Successfully saved ${connection.name}",
deleteSuccess: "Successfully deleted ${connection.name}",
@ -282,6 +283,7 @@ export const english: IAppStrings = {
title: "Create Container",
description: "Creates the blob container if it does not already exist",
},
invalidSASMessage: "\"${project.sourceConnection.name}\" has no storage account. Please check it's SAS token in the Connections page",
},
bing: {
title: "Bing Image Search",
@ -298,9 +300,11 @@ export const english: IAppStrings = {
},
local: {
title: "Local file system",
folderPath: "Browse",
folderPath: "Folder",
browse: "Browse",
selectFolder: "Select folder",
chooseFolder: "Choose folder",
invalidFolderMessage: "\"${project.sourceConnection.name}\" has an invalid folder. Please check it's selected folder in the Connections page",
},
},
},

Просмотреть файл

@ -250,6 +250,7 @@ export const spanish: IAppStrings = {
title: "Conexiones",
new: "Nueva conexión",
save: "Guardar Conexión",
genericInvalid: "\"${project.sourceConnection.name}\" es una conexión no válida. Por favor verifíquelo en la página Conexiones",
details: "Detalles de Conexión",
settings: "Configuración de Conexión",
instructions: "Por favor seleccione una conexión para editar",
@ -284,6 +285,7 @@ export const spanish: IAppStrings = {
title: "Crear contenedor",
description: "Crea el contenedor de blobs si aún no existe",
},
invalidSASMessage: "\"${project.sourceConnection.name}\" no tiene cuenta de almacenamiento. Verifique su token SAS en la página de Conexiones",
},
bing: {
title: "Búsqueda de Imágenes Bing",
@ -301,8 +303,10 @@ export const spanish: IAppStrings = {
local: {
title: "Sistema de Archivos Local",
folderPath: "Ruta de la carpeta",
browse: "vistazo",
selectFolder: "Seleccionar la carpeta",
chooseFolder: "Elijir la carpeta",
invalidFolderMessage: "\"${project.sourceConnection.name}\" tiene una carpeta no válida Por favor verifique su carpeta seleccionada en la página de Conexiones",
},
},
},

Просмотреть файл

@ -311,6 +311,7 @@ export default class MockFactory {
readBinary: jest.fn(),
deleteFile: jest.fn(),
writeText: jest.fn(),
isValidProjectConnection: jest.fn(),
writeBinary: jest.fn(),
listFiles: jest.fn(() => Promise.resolve(MockFactory.createFileList())),
listContainers: jest.fn(),

Просмотреть файл

@ -251,6 +251,7 @@ export interface IAppStrings {
instructions: string;
new: string;
save: string;
genericInvalid: string;
messages: {
saveSuccess: string;
deleteSuccess: string;
@ -277,7 +278,8 @@ export interface IAppStrings {
createContainer: {
title: string,
description: string,
}
},
invalidSASMessage: string;
},
bing: {
title: string;
@ -295,8 +297,10 @@ export interface IAppStrings {
local: {
title: string;
folderPath: string;
browse: string;
selectFolder: string;
chooseFolder: string;
invalidFolderMessage: string;
},
}
};

Просмотреть файл

@ -49,6 +49,20 @@ export default class LocalFileSystem implements IStorageProvider {
});
}
public isValidProjectConnection(folderPath) {
return new Promise<boolean>((resolve, reject) => {
try {
if (fs.existsSync(folderPath)) {
resolve(true);
} else {
resolve(false);
}
} catch (err) {
reject(err);
}
});
}
public getFileType(filePath: string): Promise<Buffer> {
return FileType.fromFile(filePath);
}

Просмотреть файл

@ -7,7 +7,7 @@ import { IAsset, AssetType, StorageType, AssetState, AppError } from "../../mode
import { AssetService } from "../../services/assetService";
import {
TokenCredential, AnonymousCredential, ContainerURL,
StorageURL, Credential, Aborter, BlockBlobURL,
StorageURL, Credential, Aborter, BlockBlobURL, ServiceURL
} from "@azure/storage-blob";
import { constants } from "../../common/constants";
import { ErrorCode } from "../../models/applicationState";
@ -61,6 +61,15 @@ export class AzureBlobStorage implements IStorageProvider {
}
}
public async isValidProjectConnection() {
try {
await new ServiceURL(this.options.sas, StorageURL.newPipeline(this.getCredential())).getAccountInfo(Aborter.none);
return (true);
} catch {
return (false);
}
}
/**
* Reads Buffer from specified blob
* @param blobName - Name of blob in container

Просмотреть файл

@ -49,6 +49,10 @@ export class LocalFileSystemProxy implements IStorageProvider, IAssetProvider {
return IpcRendererProxy.send(`${PROXY_NAME}:readText`, [filePath]);
}
public isValidProjectConnection(): Promise<boolean> {
return IpcRendererProxy.send(`${PROXY_NAME}:isValidProjectConnection`, [this.options.folderPath]);
}
public getFileType(fileName: string): Promise<any> {
const filePath = [this.options.folderPath, fileName].join("/");
return IpcRendererProxy.send(`${PROXY_NAME}:getFileType`, [filePath]);

Просмотреть файл

@ -33,6 +33,9 @@ class TestStorageProvider implements IStorageProvider {
public readBinary(filePath: string): Promise<Buffer> {
throw new Error("Method not implemented.");
}
public isValidProjectConnection(filePath: string): Promise<boolean> {
throw new Error("Method not implemented.");
}
public deleteFile(filePath: string): Promise<void> {
throw new Error("Method not implemented.");
}

Просмотреть файл

@ -14,6 +14,7 @@ import getHostProcess, { HostProcessType } from "../../common/hostProcess";
* @member deleteFile - Delete file from path
* @member writeText - Write text to file at path
* @member writeBinary - Write buffer to file at path
* @member isValidProjectConnection
* @member listFiles - List files in container within storage provider
* @member listContainers - List containers in storage provider
* @member createContainer - Create container within storage provider
@ -31,6 +32,8 @@ export interface IStorageProvider extends IAssetProvider {
writeText(filePath: string, contents: string): Promise<void>;
writeBinary(filePath: string, contents: Buffer): Promise<void>;
isValidProjectConnection(filepath?): Promise<boolean>;
listFiles(folderPath?: string, ext?: string): Promise<string[]>;
listContainers(folderPath?: string): Promise<string[]>;

Просмотреть файл

@ -58,7 +58,7 @@ export default class LocalFolderPicker extends React.Component<ILocalFolderPicke
<PrimaryButton
className="keep-button-80px"
theme={getPrimaryGreenTheme()}
text={strings.connections.providers.local.folderPath}
text={strings.connections.providers.local.browse}
autoFocus={true}
onClick={this.selectLocalFolder}
/>

Просмотреть файл

@ -26,6 +26,7 @@ import { StorageProviderFactory } from "../../../../providers/storage/storagePro
import { decryptProject } from "../../../../common/utils";
import { toast } from "react-toastify";
import { isElectron } from "../../../../common/hostProcess";
import ProjectService from "../../../../services/projectService";
export interface IHomePageProps extends RouteComponentProps, React.Props<HomePage> {
recentProjects: IProject[];
@ -174,6 +175,10 @@ export default class HomePage extends React.Component<IHomePageProps, IHomePageS
try {
let projectStr: string;
try {
const projectService = new ProjectService();
if (!(await projectService.isValidProjectConnection(project))) {
return;
}
projectStr = await storageProvider.readText(
`${decryptedProject.name}${constants.projectFileExtension}`);
} catch (err) {

Просмотреть файл

@ -177,6 +177,11 @@ export default class ProjectSettingsPage extends React.Component<IProjectSetting
private onFormSubmit = async (project: IProject) => {
const isNew = !(!!project.id);
const projectService = new ProjectService();
if (!(await projectService.isValidProjectConnection(project))) {
return;
}
if (await this.isValidProjectName(project, isNew)) {
toast.error(interpolate(strings.projectSettings.messages.projectExisted, { project }));
return;

Просмотреть файл

@ -159,6 +159,21 @@ export default class ProjectService implements IProjectService {
return false;
}
public async isValidProjectConnection(project: IProject): Promise<boolean> {
const storageProvider = StorageProviderFactory.createFromConnection(project.sourceConnection);
const isValid = await storageProvider.isValidProjectConnection();
if (!isValid) {
if (project.sourceConnection.providerType === "localFileSystemProxy") {
await toast.error(interpolate(strings.connections.providers.local.invalidFolderMessage, {project}));
} else if (project.sourceConnection.providerType === "azureBlobStorage") {
await toast.error(interpolate(strings.connections.providers.azureBlob.invalidSASMessage, {project}));
} else {
await toast.error(interpolate(strings.connections.genericInvalid, { project }));
}
}
return isValid;
};
public async updateProjectTagsFromFiles(project: IProject, asset?: string): Promise<IProject> {
const updatedProject = Object.assign({}, project);
updatedProject.tags = [];