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

ovsx-client: introduce async ovsx-client provider #10327

Merged
merged 6 commits into from
Oct 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
- [core] moved `NewWindowOptions` to `common/window.ts` [#10291](https://github.com/eclipse-theia/theia/pull/10291)
- [ovsx-client] removed `fetchJson` method from `OVSXClient` [#10325](https://github.com/eclipse-theia/theia/pull/10325)
- [callhierarchy, plugin-ext] Call hierarchy methods `getRootDefinition`, `$provideRootDefinition`, `provideRootDefinition`, and `prepareCallHierarchy` retyped to allow a return of an item or an array of items. [#10310](https://github.com/eclipse-theia/theia/pull/10310)
- [vsx-registry] removed `OVSXAsyncClient` [#10327](https://github.com/eclipse-theia/theia/pull/10327)
- [vsx-registry] updated `VSXEnvironment` from a class to an interface and symbol implemented in both `browser` and `node` [#10327](https://github.com/eclipse-theia/theia/pull/10327)

## v1.18.0 - 9/30/2021

Expand Down
1 change: 1 addition & 0 deletions dev-packages/ovsx-client/src/ovsx-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export interface OVSXClientOptions {
apiVersion: string
apiUrl: string
}

export class OVSXClient {

constructor(readonly options: OVSXClientOptions) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,25 @@ import { Argv, Arguments } from '@theia/core/shared/yargs';
import { CliContribution } from '@theia/core/lib/node/cli';
import { PluginHostEnvironmentVariable } from '@theia/plugin-ext/lib/common';
import { VSCODE_DEFAULT_API_VERSION } from '../common/plugin-vscode-types';
import { Deferred } from '@theia/core/lib/common/promise-util';

/**
* CLI Contribution allowing to override the VS Code API version which is returned by `vscode.version` API call.
*/
@injectable()
export class PluginVsCodeCliContribution implements CliContribution, PluginHostEnvironmentVariable {

/**
* CLI argument name to define the supported VS Code API version.
*/
static VSCODE_API_VERSION = 'vscode-api-version';

protected vsCodeApiVersion: string | undefined;
protected vsCodeApiVersion?: string;
protected vsCodeApiVersionDeferred = new Deferred<string>();

get vsCodeApiVersionPromise(): Promise<string> {
return this.vsCodeApiVersionDeferred.promise;
}

configure(conf: Argv): void {
conf.option(PluginVsCodeCliContribution.VSCODE_API_VERSION, {
Expand All @@ -40,11 +49,10 @@ export class PluginVsCodeCliContribution implements CliContribution, PluginHostE
}

setArguments(args: Arguments): void {
const arg = args[PluginVsCodeCliContribution.VSCODE_API_VERSION] as string;
if (arg) {
this.vsCodeApiVersion = arg;
this.process(process.env);
}
const arg = args[PluginVsCodeCliContribution.VSCODE_API_VERSION] as string | undefined;
this.vsCodeApiVersion = arg?.trim() || process.env['VSCODE_API_VERSION']?.trim() || VSCODE_DEFAULT_API_VERSION;
process.env['VSCODE_API_VERSION'] = this.vsCodeApiVersion;
this.vsCodeApiVersionDeferred.resolve(this.vsCodeApiVersion);
}

process(env: NodeJS.ProcessEnv): void {
Expand Down
2 changes: 1 addition & 1 deletion packages/vsx-registry/src/browser/vsx-extension.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ export class VSXExtension implements VSXExtensionData, TreeElement {
* @returns the registry link for the given extension at the path.
*/
async getRegistryLink(path = ''): Promise<URI> {
const uri = await this.environment.getRegistryUri();
const uri = new URI(await this.environment.getRegistryUri());
return uri.resolve('extension/' + this.id.replace('.', '/')).resolve(path);
}

Expand Down
18 changes: 10 additions & 8 deletions packages/vsx-registry/src/browser/vsx-extensions-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,16 @@ import { WorkspaceService } from '@theia/workspace/lib/browser';
import { RecommendedExtensions } from './recommended-extensions/recommended-extensions-preference-contribution';
import URI from '@theia/core/lib/common/uri';
import { VSXResponseError, VSXSearchParam } from '@theia/ovsx-client/lib/ovsx-types';
import { OVSXAsyncClient } from './ovsx-async-client';
import { OVSXClientProvider } from '../common/ovsx-client-provider';

@injectable()
export class VSXExtensionsModel {

protected readonly onDidChangeEmitter = new Emitter<void>();
readonly onDidChange = this.onDidChangeEmitter.event;

@inject(OVSXAsyncClient)
protected client: OVSXAsyncClient;
@inject(OVSXClientProvider)
protected clientProvider: OVSXClientProvider;

@inject(HostedPluginSupport)
protected readonly pluginSupport: HostedPluginSupport;
Expand All @@ -63,7 +63,6 @@ export class VSXExtensionsModel {

@postConstruct()
protected async init(): Promise<void> {
await this.client.ready;
await Promise.all([
this.initInstalled(),
this.initSearchResult(),
Expand Down Expand Up @@ -167,14 +166,15 @@ export class VSXExtensionsModel {
}, 150);
protected doUpdateSearchResult(param: VSXSearchParam, token: CancellationToken): Promise<void> {
return this.doChange(async () => {
const result = await this.client.search(param);
const client = await this.clientProvider();
const result = await client.search(param);
if (token.isCancellationRequested) {
return;
}
const searchResult = new Set<string>();
for (const data of result.extensions) {
const id = data.namespace.toLowerCase() + '.' + data.name.toLowerCase();
const extension = this.client.getLatestCompatibleVersion(data);
const extension = client.getLatestCompatibleVersion(data);
if (!extension) {
continue;
}
Expand Down Expand Up @@ -260,7 +260,8 @@ export class VSXExtensionsModel {
}
if (extension.readmeUrl) {
try {
const rawReadme = await this.client.fetchText(extension.readmeUrl);
const client = await this.clientProvider();
const rawReadme = await client.fetchText(extension.readmeUrl);
const readme = this.compileReadme(rawReadme);
extension.update({ readme });
} catch (e) {
Expand Down Expand Up @@ -292,7 +293,8 @@ export class VSXExtensionsModel {
if (!this.shouldRefresh(extension)) {
return extension;
}
const data = await this.client.getLatestCompatibleExtensionVersion(id);
const client = await this.clientProvider();
const data = await client.getLatestCompatibleExtensionVersion(id);
if (!data) {
return;
}
Expand Down
24 changes: 11 additions & 13 deletions packages/vsx-registry/src/browser/vsx-registry-frontend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
import '../../src/browser/style/index.css';

import { ContainerModule } from '@theia/core/shared/inversify';
import { WidgetFactory, bindViewContribution, FrontendApplicationContribution, ViewContainerIdentifier, OpenHandler, WidgetManager } from '@theia/core/lib/browser';
import {
WidgetFactory, bindViewContribution, FrontendApplicationContribution, ViewContainerIdentifier, OpenHandler, WidgetManager, WebSocketConnectionProvider
} from '@theia/core/lib/browser';
import { VSXExtensionsViewContainer } from './vsx-extensions-view-container';
import { VSXExtensionsContribution } from './vsx-extensions-contribution';
import { VSXExtensionsSearchBar } from './vsx-extensions-search-bar';
Expand All @@ -28,24 +30,20 @@ import { VSXExtensionFactory, VSXExtension, VSXExtensionOptions } from './vsx-ex
import { VSXExtensionEditor } from './vsx-extension-editor';
import { VSXExtensionEditorManager } from './vsx-extension-editor-manager';
import { VSXExtensionsSourceOptions } from './vsx-extensions-source';
import { VSXEnvironment } from '../common/vsx-environment';
import { VSXExtensionsSearchModel } from './vsx-extensions-search-model';
import { bindExtensionPreferences } from './recommended-extensions/recommended-extensions-preference-contribution';
import { bindPreferenceProviderOverrides } from './recommended-extensions/preference-provider-overrides';
import { OVSXAsyncClient } from './ovsx-async-client';
import { OVSXClientProvider, createOVSXClient } from '../common/ovsx-client-provider';
import { VSXEnvironment, VSX_ENVIRONMENT_PATH } from '../common/vsx-environment';

export default new ContainerModule((bind, unbind) => {
bind(VSXEnvironment).toSelf().inSingletonScope();
bind(OVSXAsyncClient).toDynamicValue(ctx => {
const vsxEnvironment = ctx.container.get(VSXEnvironment);
return new OVSXAsyncClient(Promise.all([
vsxEnvironment.getVscodeApiVersion(),
vsxEnvironment.getRegistryApiUri()
]).then(([apiVersion, apiUri]) => ({
apiVersion,
apiUrl: apiUri.toString()
})));
bind<OVSXClientProvider>(OVSXClientProvider).toDynamicValue(ctx => {
const clientPromise = createOVSXClient(ctx.container.get(VSXEnvironment));
return () => clientPromise;
}).inSingletonScope();
bind(VSXEnvironment).toDynamicValue(
ctx => WebSocketConnectionProvider.createProxy(ctx.container, VSX_ENVIRONMENT_PATH)
).inSingletonScope();

bind(VSXExtension).toSelf();
bind(VSXExtensionFactory).toFactory(ctx => (option: VSXExtensionOptions) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,16 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { OVSXClient, OVSXClientOptions } from '@theia/ovsx-client/lib';
import { OVSXClient } from '@theia/ovsx-client';
import { VSXEnvironment } from './vsx-environment';

/**
* In some instances, the OVSXClient must be created asynchronously. This class
* makes it possible to get an un-initialized instance and wait for it to be ready.
*/
export class OVSXAsyncClient extends OVSXClient {
export const OVSXClientProvider = Symbol('OVSXClientProvider');
export type OVSXClientProvider = () => Promise<OVSXClient>;

/**
* Resolves once the initial asynchronous options are resolved.
*
* Calling methods before this promise is resolved will throw errors.
*/
readonly ready: Promise<OVSXAsyncClient>;

constructor(asyncOptions: Promise<OVSXClientOptions>) {
super(undefined!); // hack: using methods at this point will fail.
this.ready = asyncOptions.then(options => {
(this.options as OVSXClientOptions) = options;
return this;
});
}
export async function createOVSXClient(vsxEnvironment: VSXEnvironment): Promise<OVSXClient> {
const [apiVersion, apiUrl] = await Promise.all([
vsxEnvironment.getVscodeApiVersion(),
vsxEnvironment.getRegistryApiUri()
]);
return new OVSXClient({ apiVersion, apiUrl: apiUrl.toString() });
}
24 changes: 24 additions & 0 deletions packages/vsx-registry/src/common/vsx-environment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/********************************************************************************
* Copyright (C) 2021 Ericsson and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

export const VSX_ENVIRONMENT_PATH = '/services/vsx-environment';

export const VSXEnvironment = Symbol('VSXEnvironment');
export interface VSXEnvironment {
getRegistryUri(): Promise<string>;
getRegistryApiUri(): Promise<string>;
getVscodeApiVersion(): Promise<string>;
}
51 changes: 0 additions & 51 deletions packages/vsx-registry/src/common/vsx-environment.tsx

This file was deleted.

41 changes: 41 additions & 0 deletions packages/vsx-registry/src/node/vsx-environment-impl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/********************************************************************************
* Copyright (C) 2020 TypeFox and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { injectable, inject } from '@theia/core/shared/inversify';
import URI from '@theia/core/lib/common/uri';
import { PluginVsCodeCliContribution } from '@theia/plugin-ext-vscode/lib/node/plugin-vscode-cli-contribution';
import { VSXEnvironment } from '../common/vsx-environment';

@injectable()
export class VSXEnvironmentImpl implements VSXEnvironment {

protected _registryUri = new URI(process.env['VSX_REGISTRY_URL']?.trim() || 'https://open-vsx.org');

@inject(PluginVsCodeCliContribution)
protected readonly pluginVscodeCli: PluginVsCodeCliContribution;

async getRegistryUri(): Promise<string> {
return this._registryUri.toString(true);
}

async getRegistryApiUri(): Promise<string> {
return this._registryUri.resolve('api').toString(true);
}

async getVscodeApiVersion(): Promise<string> {
return this.pluginVscodeCli.vsCodeApiVersionPromise;
}
}
9 changes: 5 additions & 4 deletions packages/vsx-registry/src/node/vsx-extension-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ import { injectable, inject } from '@theia/core/shared/inversify';
import URI from '@theia/core/lib/common/uri';
import { PluginDeployerResolver, PluginDeployerResolverContext } from '@theia/plugin-ext/lib/common/plugin-protocol';
import { VSXExtensionUri } from '../common/vsx-extension-uri';
import { OVSXClient } from '@theia/ovsx-client/lib/ovsx-client';
import { OVSXClientProvider } from '../common/ovsx-client-provider';

@injectable()
export class VSXExtensionResolver implements PluginDeployerResolver {

@inject(OVSXClient)
protected client: OVSXClient;
@inject(OVSXClientProvider)
protected clientProvider: OVSXClientProvider;

protected readonly downloadPath: string;

Expand All @@ -49,7 +49,8 @@ export class VSXExtensionResolver implements PluginDeployerResolver {
return;
}
console.log(`[${id}]: trying to resolve latest version...`);
const extension = await this.client.getLatestCompatibleExtensionVersion(id);
const client = await this.clientProvider();
const extension = await client.getLatestCompatibleExtensionVersion(id);
if (!extension) {
return;
}
Expand Down
22 changes: 12 additions & 10 deletions packages/vsx-registry/src/node/vsx-registry-backend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,20 @@
import { ContainerModule } from '@theia/core/shared/inversify';
import { VSXExtensionResolver } from './vsx-extension-resolver';
import { PluginDeployerResolver } from '@theia/plugin-ext/lib/common/plugin-protocol';
import { VSCODE_DEFAULT_API_VERSION, VSX_REGISTRY_URL_DEFAULT } from '@theia/plugin-ext-vscode/lib/common/plugin-vscode-types';
import { OVSXClient } from '@theia/ovsx-client/lib/ovsx-client';
import { OVSXClientProvider, createOVSXClient } from '../common/ovsx-client-provider';
import { VSXEnvironment, VSX_ENVIRONMENT_PATH } from '../common/vsx-environment';
import { VSXEnvironmentImpl } from './vsx-environment-impl';
import { ConnectionHandler, JsonRpcConnectionHandler } from '@theia/core';

export default new ContainerModule(bind => {
bind(OVSXClient).toConstantValue(new OVSXClient({
apiVersion: process.env['VSCODE_API_VERSION'] || VSCODE_DEFAULT_API_VERSION,
apiUrl: resolveRegistryUrl()
}));
bind<OVSXClientProvider>(OVSXClientProvider).toDynamicValue(ctx => {
const clientPromise = createOVSXClient(ctx.container.get(VSXEnvironment));
return () => clientPromise;
}).inSingletonScope();
bind(VSXEnvironment).to(VSXEnvironmentImpl).inSingletonScope();
bind(ConnectionHandler).toDynamicValue(
ctx => new JsonRpcConnectionHandler(VSX_ENVIRONMENT_PATH, () => ctx.container.get(VSXEnvironment))
).inSingletonScope();
bind(VSXExtensionResolver).toSelf().inSingletonScope();
bind(PluginDeployerResolver).toService(VSXExtensionResolver);
});

function resolveRegistryUrl(): string {
return process.env['VSX_REGISTRY_URL'] || VSX_REGISTRY_URL_DEFAULT;
}