Merge pull request #6 from microsoft/codegen_api
feat(modelrepository): provide api for code generation
This commit is contained in:
Коммит
b18fbc5b02
|
@ -7,26 +7,20 @@
|
|||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": ["--extensionDevelopmentPath=${workspaceRoot}"],
|
||||
"stopOnEntry": false,
|
||||
"args": ["--extensionDevelopmentPath=${workspaceFolder}"],
|
||||
"sourceMaps": true,
|
||||
"outFiles": ["${workspaceRoot}/out/src/**/*.js"],
|
||||
"preLaunchTask": "npm: watch"
|
||||
"outFiles": ["${workspaceFolder/out/**/*.js"]
|
||||
},
|
||||
{
|
||||
"name": "Extension Tests",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": [
|
||||
"${workspaceRoot}/test/resources/project1/project.code-workspace",
|
||||
"--extensionDevelopmentPath=${workspaceRoot}",
|
||||
"--extensionTestsPath=${workspaceRoot}/out/test"
|
||||
],
|
||||
"args": ["--extensionDevelopmentPath=${workspaceFolder}", "--extensionTestsPath=${workspaceFolder}/out/test"],
|
||||
"stopOnEntry": false,
|
||||
"sourceMaps": true,
|
||||
"outFiles": ["${workspaceRoot}/out/test/**/*.js"],
|
||||
"preLaunchTask": "npm: watch"
|
||||
"outFiles": ["${workspaceFolder}/out/test/**/*.js"],
|
||||
"preLaunchTask": "npm: compile"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -96,21 +96,22 @@
|
|||
"scripts": {
|
||||
"vscode:prepublish": "npm run compile",
|
||||
"compile": "tsc -p ./",
|
||||
"watch": "tsc -watch -p ./",
|
||||
"tslint": "tslint -t verbose src/**/*.ts",
|
||||
"test": "node ./out/test/runTest.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/fs-extra": "^7.0.0",
|
||||
"@types/glob": "^7.1.1",
|
||||
"@types/keytar": "^4.4.0",
|
||||
"@types/mocha": "^5.2.6",
|
||||
"@types/node": "^10.12.21",
|
||||
"@types/vscode": "^1.36.0",
|
||||
"@types/request-promise": "^4.1.44",
|
||||
"@types/keytar": "^4.4.0",
|
||||
"@types/vscode": "^1.36.0",
|
||||
"cz-conventional-changelog": "^3.0.2",
|
||||
"glob": "^7.1.4",
|
||||
"mocha": "^6.1.4",
|
||||
"prettier": "^1.19.1",
|
||||
"prettier-tslint": "^0.4.2",
|
||||
"tslint": "^5.12.1",
|
||||
"typescript": "^3.3.1",
|
||||
"vscode-test": "^1.0.0-next.0"
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { ModelType } from "../deviceModel/deviceModelManager";
|
||||
import { ModelRepositoryManager } from "../modelRepository/modelRepositoryManager";
|
||||
import { UI } from "../view/ui";
|
||||
import { UIConstants } from "../view/uiConstants";
|
||||
|
||||
/**
|
||||
* Api provider for extension integration
|
||||
*/
|
||||
export class ApiProvider {
|
||||
constructor(private readonly modelRepositoryManager: ModelRepositoryManager) {}
|
||||
|
||||
/**
|
||||
* select capability model
|
||||
*/
|
||||
public async selectCapabilityModel(): Promise<string> {
|
||||
return await UI.selectOneModelFile(UIConstants.SELECT_CAPABILITY_MODEL_LABEL, ModelType.CapabilityModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* download dependent interface of capability model
|
||||
* @param folder folder to download interface
|
||||
* @param capabilityModelFile capability model file path
|
||||
*/
|
||||
public async downloadDependentInterface(folder: string, capabilityModelFile: string): Promise<void> {
|
||||
await this.modelRepositoryManager.downloadDependentInterface(folder, capabilityModelFile);
|
||||
}
|
||||
}
|
|
@ -41,6 +41,7 @@ export class Constants {
|
|||
public static readonly PUBLIC_REPOSITORY_URL_NOT_FOUND_MSG = "Public repository url is not found";
|
||||
public static readonly CONNECTION_STRING_INVALID_FORMAT_MSG = "Invalid connection string format";
|
||||
public static readonly MODEL_TYPE_INVALID_MSG = "Invalid model type";
|
||||
public static readonly NEED_OPEN_COMPANY_REPOSITORY_MSG = "Please open company repository and try again";
|
||||
|
||||
public static readonly NSAT_SURVEY_URL = "https://aka.ms/vscode-azure-digital-twins-survey";
|
||||
public static readonly WEB_VIEW_PATH = "assets/modelRepository";
|
||||
|
|
|
@ -121,7 +121,7 @@ export class Utility {
|
|||
* @param filePath file path
|
||||
*/
|
||||
public static async getModelFileInfo(filePath: string): Promise<ModelFileInfo | undefined> {
|
||||
const content = await fs.readJson(filePath, { encoding: Constants.UTF8 });
|
||||
const content = await Utility.getJsonContent(filePath);
|
||||
const modelId: string = content[DigitalTwinConstants.ID];
|
||||
const context: string = content[DigitalTwinConstants.CONTEXT];
|
||||
const modelType: ModelType = DeviceModelManager.convertToModelType(content[DigitalTwinConstants.TYPE]);
|
||||
|
|
|
@ -7,8 +7,8 @@ import { ColorizedChannel } from "../common/colorizedChannel";
|
|||
import { Constants } from "../common/constants";
|
||||
import { ProcessError } from "../common/processError";
|
||||
import { Utility } from "../common/utility";
|
||||
import { MessageType, UI } from "../views/ui";
|
||||
import { UIConstants } from "../views/uiConstants";
|
||||
import { MessageType, UI } from "../view/ui";
|
||||
import { UIConstants } from "../view/uiConstants";
|
||||
|
||||
/**
|
||||
* DigitalTwin model type
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
import * as vscode from "vscode";
|
||||
import { ApiProvider } from "./api/apiProvider";
|
||||
import { ColorizedChannel } from "./common/colorizedChannel";
|
||||
import { Command } from "./common/command";
|
||||
import { Constants } from "./common/constants";
|
||||
|
@ -16,8 +17,7 @@ import { DigitalTwinHoverProvider } from "./intelliSense/digitalTwinHoverProvide
|
|||
import { IntelliSenseUtility } from "./intelliSense/intelliSenseUtility";
|
||||
import { SearchResult } from "./modelRepository/modelRepositoryInterface";
|
||||
import { ModelRepositoryManager } from "./modelRepository/modelRepositoryManager";
|
||||
import { MessageType, UI } from "./views/ui";
|
||||
import { UIConstants } from "./views/uiConstants";
|
||||
import { MessageType, UI } from "./view/ui";
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
const outputChannel = new ColorizedChannel(Constants.CHANNEL_NAME);
|
||||
|
@ -25,6 +25,7 @@ export function activate(context: vscode.ExtensionContext) {
|
|||
const nsat = new NSAT(Constants.NSAT_SURVEY_URL, telemetryClient);
|
||||
const deviceModelManager = new DeviceModelManager(context, outputChannel);
|
||||
const modelRepositoryManager = new ModelRepositoryManager(context, outputChannel, Constants.WEB_VIEW_PATH);
|
||||
const apiProvider = new ApiProvider(modelRepositoryManager);
|
||||
|
||||
telemetryClient.sendEvent(Constants.EXTENSION_ACTIVATED_MSG);
|
||||
context.subscriptions.push(outputChannel);
|
||||
|
@ -153,6 +154,8 @@ export function activate(context: vscode.ExtensionContext) {
|
|||
);
|
||||
},
|
||||
);
|
||||
// provide api integration
|
||||
return { apiProvider };
|
||||
}
|
||||
|
||||
export function deactivate() {}
|
||||
|
|
|
@ -130,7 +130,7 @@ export class DigitalTwinCompletionItemProvider implements vscode.CompletionItemP
|
|||
includeValue: boolean,
|
||||
separator: string,
|
||||
): vscode.CompletionItem[] {
|
||||
const result: vscode.CompletionItem[] = [];
|
||||
const completionItems: vscode.CompletionItem[] = [];
|
||||
const exist = new Set<string>();
|
||||
const classNode: ClassNode | undefined = DigitalTwinCompletionItemProvider.getObjectType(node, exist);
|
||||
if (!classNode) {
|
||||
|
@ -140,7 +140,7 @@ export class DigitalTwinCompletionItemProvider implements vscode.CompletionItemP
|
|||
if (!exist.has(DigitalTwinConstants.TYPE)) {
|
||||
// suggest @type property
|
||||
const dummyNode: PropertyNode = { id: DigitalTwinConstants.TYPE };
|
||||
result.push(
|
||||
completionItems.push(
|
||||
DigitalTwinCompletionItemProvider.createCompletionItem(
|
||||
`${dummyNode.id} ${DigitalTwinConstants.REQUIRED_PROPERTY_LABEL}`,
|
||||
true,
|
||||
|
@ -159,7 +159,7 @@ export class DigitalTwinCompletionItemProvider implements vscode.CompletionItemP
|
|||
if (!child.label || exist.has(child.label)) {
|
||||
continue;
|
||||
}
|
||||
result.push(
|
||||
completionItems.push(
|
||||
DigitalTwinCompletionItemProvider.createCompletionItem(
|
||||
DigitalTwinCompletionItemProvider.formatLabel(child.label, required),
|
||||
true,
|
||||
|
@ -177,9 +177,9 @@ export class DigitalTwinCompletionItemProvider implements vscode.CompletionItemP
|
|||
exist,
|
||||
required,
|
||||
);
|
||||
result.push(...suggestion);
|
||||
completionItems.push(...suggestion);
|
||||
}
|
||||
return result;
|
||||
return completionItems;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -275,7 +275,7 @@ export class DigitalTwinCompletionItemProvider implements vscode.CompletionItemP
|
|||
exist: Set<string>,
|
||||
required: Set<string>,
|
||||
): vscode.CompletionItem[] {
|
||||
const result: vscode.CompletionItem[] = [];
|
||||
const completionItems: vscode.CompletionItem[] = [];
|
||||
const properties: PropertyNode[] = [];
|
||||
const propertyNode: PropertyNode | undefined = IntelliSenseUtility.getPropertyNode(DigitalTwinConstants.ID);
|
||||
if (propertyNode) {
|
||||
|
@ -289,7 +289,7 @@ export class DigitalTwinCompletionItemProvider implements vscode.CompletionItemP
|
|||
if (exist.has(property.id)) {
|
||||
continue;
|
||||
}
|
||||
result.push(
|
||||
completionItems.push(
|
||||
DigitalTwinCompletionItemProvider.createCompletionItem(
|
||||
DigitalTwinCompletionItemProvider.formatLabel(property.id, required),
|
||||
true,
|
||||
|
@ -299,7 +299,7 @@ export class DigitalTwinCompletionItemProvider implements vscode.CompletionItemP
|
|||
),
|
||||
);
|
||||
}
|
||||
return result;
|
||||
return completionItems;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -356,16 +356,16 @@ export class DigitalTwinCompletionItemProvider implements vscode.CompletionItemP
|
|||
range: vscode.Range,
|
||||
separator: string,
|
||||
): vscode.CompletionItem[] {
|
||||
const result: vscode.CompletionItem[] = [];
|
||||
const completionItems: vscode.CompletionItem[] = [];
|
||||
const propertyPair: PropertyPair | undefined = IntelliSenseUtility.parseProperty(node);
|
||||
if (!propertyPair) {
|
||||
return result;
|
||||
return completionItems;
|
||||
}
|
||||
let propertyNode: PropertyNode | undefined;
|
||||
let propertyName: string = propertyPair.name.value as string;
|
||||
if (propertyName === DigitalTwinConstants.CONTEXT) {
|
||||
// suggest value of @context property
|
||||
result.push(
|
||||
completionItems.push(
|
||||
DigitalTwinCompletionItemProvider.createCompletionItem(
|
||||
DigitalTwinConstants.IOT_MODEL_LABEL,
|
||||
false,
|
||||
|
@ -384,7 +384,7 @@ export class DigitalTwinCompletionItemProvider implements vscode.CompletionItemP
|
|||
const classes: ClassNode[] = IntelliSenseUtility.getObjectClasses(propertyNode);
|
||||
for (const classNode of classes) {
|
||||
const value: string = DigitalTwinGraph.getClassType(classNode);
|
||||
result.push(
|
||||
completionItems.push(
|
||||
DigitalTwinCompletionItemProvider.createCompletionItem(
|
||||
value,
|
||||
false,
|
||||
|
@ -403,7 +403,7 @@ export class DigitalTwinCompletionItemProvider implements vscode.CompletionItemP
|
|||
if (propertyNode) {
|
||||
const enums = IntelliSenseUtility.getEnums(propertyNode);
|
||||
for (const value of enums) {
|
||||
result.push(
|
||||
completionItems.push(
|
||||
DigitalTwinCompletionItemProvider.createCompletionItem(
|
||||
value,
|
||||
false,
|
||||
|
@ -415,7 +415,7 @@ export class DigitalTwinCompletionItemProvider implements vscode.CompletionItemP
|
|||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return completionItems;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -13,8 +13,8 @@ import { UserCancelledError } from "../common/userCancelledError";
|
|||
import { Utility } from "../common/utility";
|
||||
import { ModelType } from "../deviceModel/deviceModelManager";
|
||||
import { DigitalTwinConstants } from "../intelliSense/digitalTwinConstants";
|
||||
import { ChoiceType, MessageType, UI } from "../views/ui";
|
||||
import { UIConstants } from "../views/uiConstants";
|
||||
import { ChoiceType, MessageType, UI } from "../view/ui";
|
||||
import { UIConstants } from "../view/uiConstants";
|
||||
import { ModelRepositoryClient } from "./modelRepositoryClient";
|
||||
import { ModelRepositoryConnection } from "./modelRepositoryConnection";
|
||||
import { GetResult, SearchResult } from "./modelRepositoryInterface";
|
||||
|
@ -78,16 +78,23 @@ export class ModelRepositoryManager {
|
|||
if (!connectionString) {
|
||||
throw new Error(Constants.CONNECTION_STRING_NOT_FOUND_MSG);
|
||||
}
|
||||
const connection: ModelRepositoryConnection = ModelRepositoryConnection.parse(connectionString);
|
||||
return {
|
||||
hostname: Utility.enforceHttps(connection.hostName),
|
||||
apiVersion: Constants.MODEL_REPOSITORY_API_VERSION,
|
||||
repositoryId: connection.repositoryId,
|
||||
accessToken: connection.generateAccessToken(),
|
||||
};
|
||||
return ModelRepositoryManager.getCompanyRepositoryInfo(connectionString);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get available repository info, company repository is prior to public repository
|
||||
*/
|
||||
private static async getAvailableRepositoryInfo(): Promise<RepositoryInfo[]> {
|
||||
const repoInfos: RepositoryInfo[] = [];
|
||||
const connectionString: string | null = await CredentialStore.get(Constants.MODEL_REPOSITORY_CONNECTION_KEY);
|
||||
if (connectionString) {
|
||||
repoInfos.push(ModelRepositoryManager.getCompanyRepositoryInfo(connectionString));
|
||||
}
|
||||
repoInfos.push(await ModelRepositoryManager.createRepositoryInfo(true));
|
||||
return repoInfos;
|
||||
}
|
||||
|
||||
/**
|
||||
* set up company model repository connection
|
||||
*/
|
||||
|
@ -98,13 +105,7 @@ export class ModelRepositoryManager {
|
|||
connectionString = await UI.inputConnectionString(UIConstants.INPUT_REPOSITORY_CONNECTION_STRING_LABEL);
|
||||
newConnection = true;
|
||||
}
|
||||
const connection: ModelRepositoryConnection = ModelRepositoryConnection.parse(connectionString);
|
||||
const repoInfo: RepositoryInfo = {
|
||||
hostname: Utility.enforceHttps(connection.hostName),
|
||||
apiVersion: Constants.MODEL_REPOSITORY_API_VERSION,
|
||||
repositoryId: connection.repositoryId,
|
||||
accessToken: connection.generateAccessToken(),
|
||||
};
|
||||
const repoInfo: RepositoryInfo = ModelRepositoryManager.getCompanyRepositoryInfo(connectionString);
|
||||
// test connection by calling searchModel
|
||||
await ModelRepositoryClient.searchModel(repoInfo, ModelType.Interface, Constants.EMPTY_STRING, 1, null);
|
||||
if (newConnection) {
|
||||
|
@ -112,6 +113,20 @@ export class ModelRepositoryManager {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get company repository info
|
||||
* @param connectionString connection string
|
||||
*/
|
||||
private static getCompanyRepositoryInfo(connectionString: string): RepositoryInfo {
|
||||
const connection: ModelRepositoryConnection = ModelRepositoryConnection.parse(connectionString);
|
||||
return {
|
||||
hostname: Utility.enforceHttps(connection.hostName),
|
||||
apiVersion: Constants.MODEL_REPOSITORY_API_VERSION,
|
||||
repositoryId: connection.repositoryId,
|
||||
accessToken: connection.generateAccessToken(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* validate model id list
|
||||
* @param modelIds model id list
|
||||
|
@ -258,7 +273,6 @@ export class ModelRepositoryManager {
|
|||
if (publicRepository) {
|
||||
throw new BadRequestError(`${RepositoryType.Public} not support delete operation`);
|
||||
}
|
||||
|
||||
ModelRepositoryManager.validateModelIds(modelIds);
|
||||
|
||||
try {
|
||||
|
@ -290,16 +304,42 @@ export class ModelRepositoryManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* download denpendent interface models from capability model
|
||||
* @param folder folder to download models
|
||||
* @param filePath capability model file path
|
||||
* download dependent interface of capability model, throw exception when interface not found
|
||||
* @param folder folder to download interface
|
||||
* @param capabilityModelFile capability model file path
|
||||
*/
|
||||
public async downloadDependentInterface(folder: string, filePath: string): Promise<void> {
|
||||
// TODO:(erichen): for code gen integration, used as api
|
||||
// get existing interface files
|
||||
// find dependent interface file list from dcm file
|
||||
// diff the files to download
|
||||
// download interface files
|
||||
public async downloadDependentInterface(folder: string, capabilityModelFile: string): Promise<void> {
|
||||
if (!folder || !capabilityModelFile) {
|
||||
throw new BadRequestError(`folder and capabilityModelFile ${Constants.NOT_EMPTY_MSG}`);
|
||||
}
|
||||
// get implemented interface of capability model
|
||||
const content = await Utility.getJsonContent(capabilityModelFile);
|
||||
const implementedInterface = content[DigitalTwinConstants.IMPLEMENTS];
|
||||
if (!implementedInterface || implementedInterface.length === 0) {
|
||||
throw new BadRequestError("no implemented interface found in capability model");
|
||||
}
|
||||
|
||||
// get existing interface file in workspace
|
||||
const repoInfos: RepositoryInfo[] = await ModelRepositoryManager.getAvailableRepositoryInfo();
|
||||
const fileInfos: ModelFileInfo[] = await UI.findModelFiles(ModelType.Interface);
|
||||
const exist = new Set<string>(fileInfos.map((f) => f.id));
|
||||
let schema: any;
|
||||
let found: boolean;
|
||||
let message: string;
|
||||
for (const item of implementedInterface) {
|
||||
schema = item[DigitalTwinConstants.SCHEMA];
|
||||
if (typeof schema !== "string" || exist.has(schema)) {
|
||||
continue;
|
||||
}
|
||||
found = await this.doDownloadModel(repoInfos, schema, folder);
|
||||
if (!found) {
|
||||
message = `interface ${schema} not found`;
|
||||
if (repoInfos.length === 1) {
|
||||
message = `${message}. ${Constants.NEED_OPEN_COMPANY_REPOSITORY_MSG}`;
|
||||
}
|
||||
throw new BadRequestError(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -323,12 +363,12 @@ export class ModelRepositoryManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* download model from repository
|
||||
* download model from repository, return false if model not found
|
||||
* @param repoInfos repository info list
|
||||
* @param modelId model id
|
||||
* @param folder folder to download model
|
||||
*/
|
||||
private async doDownloadModel(repoInfos: RepositoryInfo[], modelId: string, folder: string): Promise<void> {
|
||||
private async doDownloadModel(repoInfos: RepositoryInfo[], modelId: string, folder: string): Promise<boolean> {
|
||||
let result: GetResult | undefined;
|
||||
for (const repoInfo of repoInfos) {
|
||||
try {
|
||||
|
@ -346,7 +386,9 @@ export class ModelRepositoryManager {
|
|||
}
|
||||
if (result) {
|
||||
await Utility.createModelFile(folder, result.modelId, result.content);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
import * as path from "path";
|
||||
import * as vscode from "vscode";
|
||||
import { Constants } from "../common/constants";
|
||||
import { UserCancelledError } from "../common/userCancelledError";
|
||||
import { Utility } from "../common/utility";
|
||||
import { ModelType } from "../deviceModel/deviceModelManager";
|
||||
|
@ -90,7 +91,7 @@ export class UI {
|
|||
};
|
||||
});
|
||||
}
|
||||
items.push({ label: UIConstants.BROWSE_LABEL, description: "" });
|
||||
items.push({ label: UIConstants.BROWSE_LABEL, description: Constants.EMPTY_STRING });
|
||||
const selected: vscode.QuickPickItem = await UI.showQuickPick(label, items);
|
||||
return selected.description || (await UI.showOpenDialog(label));
|
||||
}
|
||||
|
@ -105,11 +106,11 @@ export class UI {
|
|||
placeHolder: label,
|
||||
ignoreFocusOut: true,
|
||||
};
|
||||
const result: vscode.QuickPickItem | undefined = await vscode.window.showQuickPick(items, options);
|
||||
if (!result) {
|
||||
const selected: vscode.QuickPickItem | undefined = await vscode.window.showQuickPick(items, options);
|
||||
if (!selected) {
|
||||
throw new UserCancelledError(label);
|
||||
}
|
||||
return result;
|
||||
return selected;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -125,11 +126,11 @@ export class UI {
|
|||
canSelectFolders: true,
|
||||
canSelectMany: false,
|
||||
};
|
||||
const results: vscode.Uri[] | undefined = await vscode.window.showOpenDialog(options);
|
||||
if (!results || results.length === 0) {
|
||||
const selected: vscode.Uri[] | undefined = await vscode.window.showOpenDialog(options);
|
||||
if (!selected || selected.length === 0) {
|
||||
throw new UserCancelledError(label);
|
||||
}
|
||||
return results[0].fsPath;
|
||||
return selected[0].fsPath;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -168,11 +169,11 @@ export class UI {
|
|||
value,
|
||||
ignoreFocusOut,
|
||||
};
|
||||
const result: string | undefined = await vscode.window.showInputBox(options);
|
||||
if (!result) {
|
||||
const input: string | undefined = await vscode.window.showInputBox(options);
|
||||
if (!input) {
|
||||
throw new UserCancelledError(label);
|
||||
}
|
||||
return result;
|
||||
return input;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -187,42 +188,23 @@ export class UI {
|
|||
}
|
||||
|
||||
/**
|
||||
* select model files
|
||||
* select model files by type
|
||||
* @param label label
|
||||
* @param type model type
|
||||
*/
|
||||
public static async selectModelFiles(label: string, type?: ModelType): Promise<string[] | undefined> {
|
||||
const files: vscode.Uri[] = await vscode.workspace.findFiles(UIConstants.MODEL_FILE_GLOB);
|
||||
if (files.length === 0) {
|
||||
UI.showNotification(MessageType.Warn, UIConstants.MODELS_NOT_FOUND_MSG);
|
||||
return undefined;
|
||||
}
|
||||
// process in parallel
|
||||
const items: Array<QuickPickItemWithData<string>> = [];
|
||||
await Promise.all(
|
||||
files.map(async (f) => {
|
||||
let fileInfo: ModelFileInfo | undefined;
|
||||
try {
|
||||
fileInfo = await Utility.getModelFileInfo(f.fsPath);
|
||||
} catch {
|
||||
// skip if file is not a valid json
|
||||
return;
|
||||
}
|
||||
if (fileInfo) {
|
||||
if (!type || type === fileInfo.type) {
|
||||
items.push({
|
||||
label: path.basename(fileInfo.filePath),
|
||||
description: fileInfo.id,
|
||||
data: fileInfo.filePath,
|
||||
});
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
if (items.length === 0) {
|
||||
const fileInfos: ModelFileInfo[] = await UI.findModelFiles(type);
|
||||
if (fileInfos.length === 0) {
|
||||
UI.showNotification(MessageType.Warn, UIConstants.MODELS_NOT_FOUND_MSG);
|
||||
return undefined;
|
||||
}
|
||||
const items: Array<QuickPickItemWithData<string>> = fileInfos.map((f) => {
|
||||
return {
|
||||
label: path.basename(f.filePath),
|
||||
description: f.id,
|
||||
data: f.filePath,
|
||||
};
|
||||
});
|
||||
const selected: Array<QuickPickItemWithData<string>> | undefined = await vscode.window.showQuickPick(items, {
|
||||
placeHolder: label,
|
||||
ignoreFocusOut: true,
|
||||
|
@ -235,6 +217,67 @@ export class UI {
|
|||
return selected.map((s) => s.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* select one model file
|
||||
* @param label label
|
||||
* @param type model type
|
||||
*/
|
||||
public static async selectOneModelFile(label: string, type?: ModelType): Promise<string> {
|
||||
const fileInfos: ModelFileInfo[] = await UI.findModelFiles(type);
|
||||
if (fileInfos.length === 0) {
|
||||
UI.showNotification(MessageType.Warn, UIConstants.MODELS_NOT_FOUND_MSG);
|
||||
return Constants.EMPTY_STRING;
|
||||
}
|
||||
const items: Array<QuickPickItemWithData<string>> = fileInfos.map((f) => {
|
||||
return {
|
||||
label: path.basename(f.filePath),
|
||||
description: f.id,
|
||||
data: f.filePath,
|
||||
};
|
||||
});
|
||||
const selected: QuickPickItemWithData<string> | undefined = await vscode.window.showQuickPick(items, {
|
||||
placeHolder: label,
|
||||
ignoreFocusOut: true,
|
||||
canPickMany: false,
|
||||
matchOnDescription: true,
|
||||
});
|
||||
if (!selected) {
|
||||
throw new UserCancelledError(label);
|
||||
}
|
||||
return selected.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* find model files by type
|
||||
* @param type model type
|
||||
*/
|
||||
public static async findModelFiles(type?: ModelType): Promise<ModelFileInfo[]> {
|
||||
const fileInfos: ModelFileInfo[] = [];
|
||||
const files: vscode.Uri[] = await vscode.workspace.findFiles(UIConstants.MODEL_FILE_GLOB);
|
||||
if (files.length === 0) {
|
||||
return fileInfos;
|
||||
}
|
||||
// process in parallel
|
||||
await Promise.all(
|
||||
files.map(async (f) => {
|
||||
let fileInfo: ModelFileInfo | undefined;
|
||||
try {
|
||||
fileInfo = await Utility.getModelFileInfo(f.fsPath);
|
||||
} catch {
|
||||
// skip if file is not a valid json
|
||||
return;
|
||||
}
|
||||
if (!fileInfo) {
|
||||
return;
|
||||
}
|
||||
if (!type || type === fileInfo.type) {
|
||||
fileInfos.push(fileInfo);
|
||||
}
|
||||
}),
|
||||
);
|
||||
return fileInfos;
|
||||
}
|
||||
|
||||
/**
|
||||
* ensure files saved
|
||||
* @param label label
|
|
@ -10,6 +10,7 @@ export class UIConstants {
|
|||
public static readonly BROWSE_LABEL = "Browse...";
|
||||
public static readonly SELECT_REPOSITORY_LABEL = "Select model repository";
|
||||
public static readonly SELECT_MODELS_LABEL = "Select device models";
|
||||
public static readonly SELECT_CAPABILITY_MODEL_LABEL = "Select a capability model";
|
||||
public static readonly INPUT_REPOSITORY_CONNECTION_STRING_LABEL = "Input company repository connection string";
|
||||
public static readonly SAVE_FILE_CHANGE_LABEL = "Save file change";
|
||||
public static readonly MODEL_REPOSITORY_TITLE = "IoT Plug and Play Model Repository";
|
Загрузка…
Ссылка в новой задаче