Skip to content

Commit

Permalink
telemetry: implement logic for server resolution
Browse files Browse the repository at this point in the history
  • Loading branch information
Hweinstock committed Jan 24, 2025
1 parent f03bf8d commit 7d05c68
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 55 deletions.
109 changes: 67 additions & 42 deletions packages/core/src/shared/lsp/lspResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { TargetContent, logger, LspResult, LspVersion, Manifest } from './types'
import { getApplicationSupportFolder } from '../vscode/env'
import { createHash } from '../crypto'
import request from '../request'
import { lspSetupStage } from '../../amazonq/lsp/util'
import { lspSetupStage, tryFunctions } from '../../amazonq/lsp/util'

export class LanguageServerResolver {
constructor(
Expand All @@ -31,63 +31,88 @@ export class LanguageServerResolver {
* @throws ToolkitError if no compatible version can be found
*/
async resolve() {
const result: LspResult = {
location: 'unknown',
version: '',
assetDirectory: '',
}

const latestVersion = this.latestCompatibleLspVersion()
const targetContents = this.getLSPTargetContents(latestVersion)
const cacheDirectory = this.getDownloadDirectory(latestVersion.serverVersion)

if (await this.hasValidLocalCache(cacheDirectory, targetContents)) {
result.location = 'cache'
result.version = latestVersion.serverVersion
result.assetDirectory = cacheDirectory
return result
} else {
// Delete the cached directory since it's invalid
if (await fs.existsDir(cacheDirectory)) {
await fs.delete(cacheDirectory, {
recursive: true,
})
}
}

if (await this.downloadRemoteTargetContent(targetContents, latestVersion.serverVersion)) {
result.location = 'remote'
result.version = latestVersion.serverVersion
result.assetDirectory = cacheDirectory
return result
} else {
// clean up any leftover content that may have been downloaded
if (await fs.existsDir(cacheDirectory)) {
await fs.delete(cacheDirectory, {
recursive: true,
})
}
const serverResolvers = [
async () => await this.getLocalServer(cacheDirectory, latestVersion, targetContents),
async () => await this.fetchRemoteServer(cacheDirectory, latestVersion, targetContents),
async () => await this.getFallbackServer(latestVersion),
].map((resolver) => async () => await resolveServerWith(resolver))

return await tryFunctions(serverResolvers)

async function resolveServerWith(resolver: () => Promise<LspResult>) {
return await lspSetupStage(
'getServer',
async () => await resolver(),
(result) => {
return {
languageServerLocation: result.location,
languageServerVersion: result.version,
}
}
)
}
}

logger.info(
`Unable to download language server version ${latestVersion.serverVersion}. Attempting to fetch from fallback location`
)

async getFallbackServer(latestVersion: LspVersion): Promise<LspResult> {
const fallbackDirectory = await this.getFallbackDir(latestVersion.serverVersion)
if (!fallbackDirectory) {
throw new ToolkitError('Unable to find a compatible version of the Language Server')
throw new ToolkitError('Unable to find a compatible version of the Language Server', {
code: 'IncompatibleVersion',
})
}

const version = path.basename(fallbackDirectory)
logger.info(
`Unable to install ${this.lsName} language server v${latestVersion.serverVersion}. Launching a previous version from ${fallbackDirectory}`
)

result.location = 'fallback'
result.version = version
result.assetDirectory = fallbackDirectory
return {
location: 'fallback',
version: version,
assetDirectory: fallbackDirectory,
}
}

async fetchRemoteServer(
cacheDirectory: string,
latestVersion: LspVersion,
targetContents: TargetContent[]
): Promise<LspResult> {
if (await this.downloadRemoteTargetContent(targetContents, latestVersion.serverVersion)) {
return {
location: 'remote',
version: latestVersion.serverVersion,
assetDirectory: cacheDirectory,
}
} else {
throw new ToolkitError('Failed to download server from remote', { code: 'RemoteDownloadFailed' })
}
}

return result
async getLocalServer(
cacheDirectory: string,
latestVersion: LspVersion,
targetContents: TargetContent[]
): Promise<LspResult> {
if (await this.hasValidLocalCache(cacheDirectory, targetContents)) {
return {
location: 'cache',
version: latestVersion.serverVersion,
assetDirectory: cacheDirectory,
}
} else {
// Delete the cached directory since it's invalid
if (await fs.existsDir(cacheDirectory)) {
await fs.delete(cacheDirectory, {
recursive: true,
})
}
throw new ToolkitError('Failed to retrieve server from cache', { code: 'InvalidCache' })
}
}

/**
Expand Down
23 changes: 14 additions & 9 deletions packages/core/src/shared/lsp/manifestResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,22 @@ export class ManifestResolver {
* Fetches the latest manifest, falling back to local cache on failure
*/
async resolve(): Promise<Manifest> {
return await tryFunctions([
async () => await resolveManifestWith(async () => await this.fetchRemoteManifest()),
async () => await resolveManifestWith(async () => await this.getLocalManifest()),
])
const resolvers = [async () => await this.fetchRemoteManifest(), async () => await this.getLocalManifest()].map(
(resolver) => async () => await resolveManifestWith(resolver)
)
return await tryFunctions(resolvers)

async function resolveManifestWith(resolver: () => Promise<Manifest>) {
return await lspSetupStage('getManifest', async () => await resolver(), extractVersionFromResult)
}

function extractVersionFromResult(r: Manifest) {
return { manifestSchemaVersion: r.manifestSchemaVersion }
return await lspSetupStage(
'getManifest',
async () => await resolver(),
(result) => {
return {
manifestSchemaVersion: result.manifestSchemaVersion,
manifestLocation: result.location,
}
}
)
}
}

Expand Down
7 changes: 3 additions & 4 deletions packages/core/src/shared/lsp/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@
*/

import { getLogger } from '../logger/logger'
import { LanguageServerLocation, ManifestLocation } from '../telemetry'

export const logger = getLogger('lsp')

type Location = 'remote' | 'cache' | 'override' | 'fallback' | 'unknown'

export interface LspResult {
location: Location
location: LanguageServerLocation
version: string
assetDirectory: string
}
Expand Down Expand Up @@ -53,7 +52,7 @@ export interface Manifest {
artifactDescription: string
isManifestDeprecated: boolean
versions: LspVersion[]
location?: Location
location?: ManifestLocation
}

export interface VersionRange {
Expand Down

0 comments on commit 7d05c68

Please sign in to comment.