arduino-ide/arduino-ide-extension/src/node/core-client-provider-impl.ts
Akos Kitta 7494beca33 Switched to the official arduino-cli.
Signed-off-by: Akos Kitta <kittaakos@typefox.io>
2019-07-25 07:58:00 +02:00

155 lines
6.0 KiB
TypeScript

import { inject, injectable } from 'inversify';
import * as grpc from '@grpc/grpc-js';
import { ArduinoCoreClient } from './cli-protocol/commands/commands_grpc_pb';
import {
InitResp,
InitReq,
Configuration,
UpdateIndexReq,
UpdateIndexResp
} from './cli-protocol/commands/commands_pb';
import { WorkspaceServiceExt } from '../browser/workspace-service-ext';
import { FileSystem } from '@theia/filesystem/lib/common';
import URI from '@theia/core/lib/common/uri';
import { CoreClientProvider, Client } from './core-client-provider';
import * as PQueue from 'p-queue';
import { ToolOutputServiceServer } from '../common/protocol/tool-output-service';
import { Instance } from './cli-protocol/commands/common_pb';
import * as fs from 'fs-extra';
import * as path from 'path';
import * as os from 'os';
@injectable()
export class CoreClientProviderImpl implements CoreClientProvider {
protected clients = new Map<string, Client>();
protected readonly clientRequestQueue = new PQueue({ autoStart: true, concurrency: 1 });
@inject(FileSystem)
protected readonly fileSystem: FileSystem;
@inject(WorkspaceServiceExt)
protected readonly workspaceServiceExt: WorkspaceServiceExt;
@inject(ToolOutputServiceServer)
protected readonly toolOutputService: ToolOutputServiceServer;
async getClient(workspaceRootOrResourceUri?: string): Promise<Client | undefined> {
return this.clientRequestQueue.add(() => new Promise<Client | undefined>(async resolve => {
const roots = await this.workspaceServiceExt.roots();
if (!workspaceRootOrResourceUri) {
resolve(this.getOrCreateClient(roots[0]));
return;
}
const root = roots
.sort((left, right) => right.length - left.length) // Longest "paths" first
.map(uri => new URI(uri))
.find(uri => uri.isEqualOrParent(new URI(workspaceRootOrResourceUri)));
if (!root) {
console.warn(`Could not retrieve the container workspace root for URI: ${workspaceRootOrResourceUri}.`);
console.warn(`Falling back to ${roots[0]}`);
resolve(this.getOrCreateClient(roots[0]));
return;
}
resolve(this.getOrCreateClient(root.toString()));
}));
}
protected async getOrCreateClient(rootUri: string | undefined): Promise<Client | undefined> {
if (!rootUri) {
return undefined;
}
const existing = this.clients.get(rootUri);
if (existing) {
console.debug(`Reusing existing client for ${rootUri}.`);
return existing;
}
console.info(` >>> Creating and caching a new client for ${rootUri}...`);
const client = new ArduinoCoreClient('localhost:50051', grpc.credentials.createInsecure());
const config = new Configuration();
const rootPath = await this.fileSystem.getFsPath(rootUri);
if (!rootPath) {
throw new Error(`Could not resolve filesystem path of URI: ${rootUri}.`);
}
const defaultDownloadsDirPath = path.resolve(os.homedir(), 'Arduino-PoC', 'downloads');
if (!fs.existsSync(defaultDownloadsDirPath)) {
fs.mkdirpSync(defaultDownloadsDirPath);
}
const defaultDataDirPath = path.resolve(os.homedir(), 'Arduino-PoC', 'data')
if (!fs.existsSync(defaultDataDirPath)) {
fs.mkdirpSync(defaultDataDirPath);
}
config.setSketchbookdir(rootPath);
config.setDatadir(defaultDataDirPath);
config.setDownloadsdir(defaultDownloadsDirPath);
config.setBoardmanageradditionalurlsList(['https://downloads.arduino.cc/packages/package_index.json']);
const initReq = new InitReq();
initReq.setConfiguration(config);
initReq.setLibraryManagerOnly(false);
const initResp = await new Promise<InitResp>(resolve => {
let resp: InitResp | undefined = undefined;
const stream = client.init(initReq);
stream.on('data', (data: InitResp) => {
if (!resp) {
resp = data;
}
})
stream.on('end', () => {
resolve(resp);
})
});
const instance = initResp.getInstance();
if (!instance) {
throw new Error(`Could not retrieve instance from the initialize response.`);
}
// in a separate promise, try and update the index
let succeeded = true;
for (let i = 0; i < 10; i++) {
try {
await this.updateIndex(client, instance);
succeeded = true;
break;
} catch (e) {
this.toolOutputService.publishNewOutput("daemon", `Error while updating index in attempt ${i}: ${e}`);
}
}
if (!succeeded) {
this.toolOutputService.publishNewOutput("daemon", `Was unable to update the index. Please restart to try again.`);
}
const result = {
client,
instance
}
this.clients.set(rootUri, result);
console.info(` <<< New client has been successfully created and cached for ${rootUri}.`);
return result;
}
protected async updateIndex(client: ArduinoCoreClient, instance: Instance): Promise<void> {
const updateReq = new UpdateIndexReq();
updateReq.setInstance(instance);
const updateResp = client.updateIndex(updateReq);
updateResp.on('data', (o: UpdateIndexResp) => {
const progress = o.getDownloadProgress();
if (progress) {
if (progress.getCompleted()) {
this.toolOutputService.publishNewOutput("daemon", `Download${progress.getFile() ? ` of ${progress.getFile()}` : ''} completed.\n`);
}
}
});
await new Promise<void>((resolve, reject) => {
updateResp.on('error', reject);
updateResp.on('end', resolve);
});
}
}