From d28846c2dbeddd8a443d0ffec169388086e9fe9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A9ri=20Le=20Bouder?= Date: Mon, 5 Aug 2024 12:57:56 -0400 Subject: [PATCH 1/2] lightspeed: rely on electron's fetch - `fetch()` in the main process works well with the proxy - this is not the case from within the LSP server TODO: - Move away from the LSP server - Error handling --- packages/ansible-language-server/package.json | 1 - .../src/ansibleLanguageService.ts | 128 +++++++++++------ src/features/lightspeed/base.ts | 13 ++ .../lightspeed/lightSpeedOAuthProvider.ts | 92 ++++++------ src/features/lightspeed/lightspeedUser.ts | 132 +++++++++--------- .../lightspeed/playbookExplanation.ts | 1 + yarn.lock | 1 - 7 files changed, 211 insertions(+), 157 deletions(-) diff --git a/packages/ansible-language-server/package.json b/packages/ansible-language-server/package.json index 066738f08..9fb27f1cc 100644 --- a/packages/ansible-language-server/package.json +++ b/packages/ansible-language-server/package.json @@ -47,7 +47,6 @@ "dependencies": { "@flatten-js/interval-tree": "^1.1.3", "antsibull-docs": "^1.0.2", - "axios": "^1.7.2", "glob": "^10.4.5", "ini": "^4.1.3", "lodash": "^4.17.21", diff --git a/packages/ansible-language-server/src/ansibleLanguageService.ts b/packages/ansible-language-server/src/ansibleLanguageService.ts index f5dbb5909..93aa4f302 100644 --- a/packages/ansible-language-server/src/ansibleLanguageService.ts +++ b/packages/ansible-language-server/src/ansibleLanguageService.ts @@ -23,7 +23,6 @@ import { doValidate } from "./providers/validationProvider"; import { ValidationManager } from "./services/validationManager"; import { WorkspaceManager } from "./services/workspaceManager"; import { getAnsibleMetaData } from "./utils/getAnsibleMetaData"; -import axios from "axios"; import { AxiosError } from "axios"; import { getBaseUri } from "./utils/webUtils"; import { @@ -33,6 +32,17 @@ import { } from "./interfaces/lightspeedApi"; import { mapError } from "./utils/handleApiError"; + +export function loadFetch(): typeof fetch|undefined { + try { + return require('electron')?.net?.fetch; + } catch (err) { + // Not available. + } + return undefined; +} + + /** * Initializes the connection and registers all lifecycle event handlers. * @@ -374,28 +384,34 @@ export class AnsibleLanguageService { Authorization: `Bearer ${accessToken}`, }; - const axiosInstance = axios.create({ - baseURL: `${getBaseUri(URL)}/api/v0`, - headers: headers, + const body = JSON.stringify({ + content: content, + explanationId: explanationId, }); - const result: ExplanationResponse = await axiosInstance - .post( - "/ai/explanations/", - { - content: content, - explanationId: explanationId, - }, - { signal: AbortSignal.timeout(28000) }, - ) - .then((response) => { - return response.data; - }) - .catch((error) => { - const err = error as AxiosError; - const mappedError: IError = mapError(err); - return mappedError; - }); + const fetch = loadFetch(); + if (!fetch) { + return {"content": "Nothing"} as ExplanationResponse; // TODO + } + const result: ExplanationResponse = await fetch( + `${getBaseUri(URL)}/api/v0/ai/explanations/`, + { + method: "POST", + body, + headers, + }, + ).then((response) => {console.log(response); return response.json()}); + // .post( + // "/ai/explanations/", + // { signal: AbortSignal.timeout(28000) }, + // ) + //.then((response) => response.json()); + // .catch((error) => { + // /* TODO */ + // const err = error as AxiosError; + // const mappedError: IError = mapError(err); + // return mappedError; + // }); console.log(result); @@ -419,37 +435,59 @@ export class AnsibleLanguageService { Authorization: `Bearer ${accessToken}`, }; - const axiosInstance = axios.create({ - baseURL: `${getBaseUri(URL)}/api/v0`, - headers: headers, + const body = JSON.stringify({ + text, + createOutline, + outline, + generationId, + wizardId, }); - const result: GenerationResponse = await axiosInstance - .post( - "/ai/generations/", - { - text, - createOutline, - outline, - generationId, - wizardId, - }, - { signal: AbortSignal.timeout(28000) }, - ) - .then((response) => { - return response.data; - }) - .catch((error) => { - const err = error as AxiosError; - const mappedError: IError = mapError(err); - return mappedError; - }); + const fetch = loadFetch(); + if (!fetch) { + return {} as GenerationResponse; // TODO + } + + const result: GenerationResponse = await fetch( + `${getBaseUri(URL)}/api/v0/ai/generations/`, + { + method: "POST", + body, + headers, + }, + ).then((response) => { + return response.json(); + }); + + console.log(result); return result; + + // const result: GenerationResponse = await axiosInstance + // .post( + // "/ai/generations/", + // { + // text, + // createOutline, + // outline, + // generationId, + // wizardId, + // }, + // { signal: AbortSignal.timeout(28000) }, + // ) + // .then((response) => { + // return response.data; + // }) + // .catch((error) => { + // const err = error as AxiosError; + // const mappedError: IError = mapError(err); + // return mappedError; + // }); + + // return result; }, ); } - private handleError(error: unknown, contextName: string) { const leadMessage = `An error occurred in '${contextName}' handler: `; if (error instanceof Error) { diff --git a/src/features/lightspeed/base.ts b/src/features/lightspeed/base.ts index 6c5435ec0..d4408e5da 100644 --- a/src/features/lightspeed/base.ts +++ b/src/features/lightspeed/base.ts @@ -190,3 +190,16 @@ export class LightSpeedManager { ); } } + +// See: +// https://github.com/Microsoft/vscode/issues/12588#issuecomment-2111861237 +// https://github.com/microsoft/vscode/pull/198408 +// https://github.com/chrmarti/vscode-network-proxy-test/blob/main/src/extension.ts#L245-L252 +export function loadFetch(): typeof fetch|undefined { + try { + return require('electron')?.net?.fetch; + } catch (err) { + // Not available. + } + return undefined; +} diff --git a/src/features/lightspeed/lightSpeedOAuthProvider.ts b/src/features/lightspeed/lightSpeedOAuthProvider.ts index 02dedcb5a..7216e1d71 100644 --- a/src/features/lightspeed/lightSpeedOAuthProvider.ts +++ b/src/features/lightspeed/lightSpeedOAuthProvider.ts @@ -38,6 +38,9 @@ import { } from "../../definitions/lightspeed"; import { LightspeedAuthSession } from "../../interfaces/lightspeed"; import { lightSpeedManager } from "../../extension"; +import { loadFetch } from "../lightspeed/base"; + + const CODE_VERIFIER = generateCodeVerifier(); const CODE_CHALLENGE = generateCodeChallengeFromVerifier(CODE_VERIFIER); @@ -154,6 +157,8 @@ export class LightSpeedAuthenticationProvider try { lightSpeedManager.currentModelValue = undefined; const account = await this.login(scopes); + console.log("account"); + console.log(account); if (!account) { throw new Error(`Ansible Lightspeed login failure`); @@ -162,6 +167,8 @@ export class LightSpeedAuthenticationProvider const userinfo: LoggedInUserInfo = await this.getUserInfo( account.accessToken, ); + console.log("userinfo"); + console.log(userinfo); const identifier = uuid(); const userName = userinfo.external_username || userinfo.username || ""; @@ -194,6 +201,7 @@ export class LightSpeedAuthenticationProvider ? userinfo.rh_user_is_org_admin : false, }; + console.log(session); await this.context.secrets.store( SESSIONS_SECRET_KEY, JSON.stringify([session]), @@ -362,26 +370,36 @@ export class LightSpeedAuthenticationProvider "Content-Type": "application/x-www-form-urlencoded", }; - const postData = { + const body = new URLSearchParams({ client_id: LIGHTSPEED_CLIENT_ID, code: code, code_verifier: CODE_VERIFIER, redirect_uri: this._externalRedirectUri, grant_type: "authorization_code", - }; + }); console.log( "[ansible-lightspeed-oauth] Sending request for access token...", ); + console.log(body); + + const fetch = loadFetch(); + if (!fetch) { + return; + } try { - const { data } = await axios.post( + const response = await fetch( `${getBaseUri(this.settingsManager)}/o/token/`, - postData, { - headers: headers, + method: "POST", + body, + headers, }, ); + console.log(`requestOAuthAccountFromCode: ${response}`); + const data = await response.json(); + console.log(data); const account: OAuthAccount = { type: "oauth", @@ -395,6 +413,7 @@ export class LightSpeedAuthenticationProvider return account; } catch (error) { + /* TODO */ if (axios.isAxiosError(error)) { console.error( "[ansible-lightspeed-oauth] error message: ", @@ -422,28 +441,33 @@ export class LightSpeedAuthenticationProvider "Content-Type": "application/x-www-form-urlencoded", }; - const postData = { + const body = JSON.stringify({ client_id: LIGHTSPEED_CLIENT_ID, refresh_token: currentAccount.refreshToken, grant_type: "refresh_token", - }; + }); console.log( "[ansible-lightspeed-oauth] Sending request for a new access token...", ); + const fetch = loadFetch(); + if (!fetch) { + return; + } const account = await window.withProgress( { title: "Refreshing token", location: ProgressLocation.Notification, }, async () => { - return axios - .post(`${getBaseUri(this.settingsManager)}/o/token/`, postData, { - headers: headers, - }) - .then((response) => { - const data = response.data; + return fetch(`${getBaseUri(this.settingsManager)}/o/token/`, { + method: "POST", + body, + headers, + }) + .then((response) => response.json()) + .then((data) => { const account: OAuthAccount = { ...currentAccount, accessToken: data.access_token, @@ -559,36 +583,22 @@ export class LightSpeedAuthenticationProvider /* Get the user info from server */ private async getUserInfo(token: string) { console.log( - "[ansible-lightspeed-oauth] Sending request for logged-in user info...", + "[ansible-lightspeed-oauth] Sending request for logged-in user info1...", ); - - try { - const { data } = await axios.get( - `${getBaseUri(this.settingsManager)}${LIGHTSPEED_ME_AUTH_URL}`, - { - headers: { - Authorization: `Bearer ${token}`, - }, - }, - ); - - return data; - } catch (error) { - if (axios.isAxiosError(error)) { - console.error( - "[ansible-lightspeed-oauth] error message: ", - error.message, - ); - console.error( - "[ansible-lightspeed-oauth] error response data: ", - error.response?.data, - ); - throw new Error(error.message); - } else { - console.error("[ansible-lightspeed-oauth] unexpected error: ", error); - throw new Error("An unexpected error occurred"); - } + const headers = { + Authorization: `Bearer ${token}`, + }; + const fetch = loadFetch(); + if (!fetch) { + return; } + const response = await fetch( + `${getBaseUri(this.settingsManager)}${LIGHTSPEED_ME_AUTH_URL}`, + { + headers, + }, + ); + return response.json(); } /* Return session info if user is authenticated, else undefined */ diff --git a/src/features/lightspeed/lightspeedUser.ts b/src/features/lightspeed/lightspeedUser.ts index 8e2b962c2..0a5d5d5eb 100644 --- a/src/features/lightspeed/lightspeedUser.ts +++ b/src/features/lightspeed/lightspeedUser.ts @@ -20,6 +20,8 @@ import { } from "./lightSpeedOAuthProvider"; import { Log } from "../../utils/logger"; import * as marked from "marked"; +import { loadFetch } from "../lightspeed/base"; + export class LightspeedAccessDenied extends Error { constructor(message: string) { @@ -28,6 +30,8 @@ export class LightspeedAccessDenied extends Error { } } + + export enum AuthProviderType { rhsso = RHSSO_AUTH_ID, lightspeed = ANSIBLE_LIGHTSPEED_AUTH_ID, @@ -105,86 +109,76 @@ export class LightspeedUser { /* Get the user info from server */ public async getUserInfo(token: string) { this._logger.info( - "[ansible-lightspeed-user] Sending request for logged-in user info...", + `[ansible-lightspeed-user] Sending request for logged-in user info2...: ${getBaseUri(this._settingsManager)}${LIGHTSPEED_ME_AUTH_URL} Bearer ${token}`, ); + console.log(process.env); - try { - const { data } = await axios.get( - `${getBaseUri(this._settingsManager)}${LIGHTSPEED_ME_AUTH_URL}`, - { - headers: { - Authorization: `Bearer ${token}`, - }, - }, - ); - - return data; - } catch (error) { - if ( - axios.isAxiosError(error) && - error.response && - error.response.status === 401 - ) { - throw new LightspeedAccessDenied(error.message); - } else if (axios.isAxiosError(error)) { - this._logger.error( - `[ansible-lightspeed-user] error message: ${error.message}`, - ); - console.error( - "[ansible-lightspeed-user] error response data: ", - error.response?.data, - ); - throw new Error(error.message); - } else { - this._logger.error( - `[ansible-lightspeed-user] unexpected error: ${error}`, - ); - throw new Error("An unexpected error occurred"); - } + const headers = { + Authorization: `Bearer ${token}`, + }; + const fetch = loadFetch(); + if (!fetch) { + return; } + return fetch( + `${getBaseUri(this._settingsManager)}${LIGHTSPEED_ME_AUTH_URL}`, + { headers }, + ).then((response) => { + if (response.status === 401) { + throw new LightspeedAccessDenied(response.statusText); + } + return response.json(); + }); + // .catch((error) => { + // console.log(error); + // if ( + // axios.isAxiosError(error) && + // error.response && + // error.response.status === 401 + // ) { + // } else if (axios.isAxiosError(error)) { + // this._logger.error( + // `[ansible-lightspeed-user] error message: ${error.message}`, + // ); + // console.error( + // "[ansible-lightspeed-user] error response data: ", + // error.response?.data, + // ); + // throw new Error(error.message); + // } else { + // this._logger.error( + // `[ansible-lightspeed-user] unexpected error: ${error}`, + // ); + // throw new Error("An unexpected error occurred"); + // } + // }); } public async getUserInfoFromMarkdown(token: string) { this._logger.info( - "[ansible-lightspeed-user] Sending request for logged-in user info...", + "[ansible-lightspeed-user] Sending request for logged-in user info3...", ); - try { - const { data } = await axios.get( - `${getBaseUri(this._settingsManager)}${LIGHTSPEED_MARKDOWN_ME_AUTH_URL}`, - { - headers: { - Authorization: `Bearer ${token}`, - }, - }, - ); + const headers = { + Authorization: `Bearer ${token}`, + }; - const markdownData = marked.parseInline(data.content) as string; + const fetch = loadFetch(); + if (!fetch) { + return ""; + } + console.log(fetch); + const response = await fetch( + `${getBaseUri(this._settingsManager)}${LIGHTSPEED_MARKDOWN_ME_AUTH_URL}`, + { headers }, + ); - return markdownData; - } catch (error) { - if ( - axios.isAxiosError(error) && - error.response && - error.response.status === 401 - ) { - throw new LightspeedAccessDenied(error.message); - } else if (axios.isAxiosError(error)) { - this._logger.error( - `[ansible-lightspeed-user] error message: ${error.message}`, - ); - /* istanbul ignore next */ - console.error( - "[ansible-lightspeed-user] error response data: ", - error.response?.data, - ); - throw new Error(error.message); - } else { - this._logger.error( - `[ansible-lightspeed-user] unexpected error: ${error}`, - ); - throw new Error("An unexpected error occurred"); - } + const data = await response.json(); + + if (response.ok && data.content) { + return marked.parseInline(data.content); + } else { + return ""; } } diff --git a/src/features/lightspeed/playbookExplanation.ts b/src/features/lightspeed/playbookExplanation.ts index 61bfef338..563028cf4 100644 --- a/src/features/lightspeed/playbookExplanation.ts +++ b/src/features/lightspeed/playbookExplanation.ts @@ -114,6 +114,7 @@ export const playbookExplanation = async ( ); } } else { + console.log(response); markdown = response.content; const html_snippet = marked.parse(markdown) as string; currentPanel.setContent(html_snippet, true); diff --git a/yarn.lock b/yarn.lock index 657980162..1a9e82920 100644 --- a/yarn.lock +++ b/yarn.lock @@ -36,7 +36,6 @@ __metadata: "@types/uuid": "npm:^10.0.0" "@types/vscode": "npm:^1.85.0" antsibull-docs: "npm:^1.0.2" - axios: "npm:^1.7.2" chai: "npm:^4.5.0" fuse.js: "npm:^7.0.0" glob: "npm:^10.4.5" From 7557db93f7be7c2d79b55631b352f653c0ae9250 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 22:27:15 +0000 Subject: [PATCH 2/2] chore: auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../src/ansibleLanguageService.ts | 23 ++++++++++--------- src/features/lightspeed/base.ts | 14 +++++------ .../lightspeed/lightSpeedOAuthProvider.ts | 2 -- src/features/lightspeed/lightspeedUser.ts | 3 --- 4 files changed, 19 insertions(+), 23 deletions(-) diff --git a/packages/ansible-language-server/src/ansibleLanguageService.ts b/packages/ansible-language-server/src/ansibleLanguageService.ts index 93aa4f302..780438f2a 100644 --- a/packages/ansible-language-server/src/ansibleLanguageService.ts +++ b/packages/ansible-language-server/src/ansibleLanguageService.ts @@ -32,17 +32,15 @@ import { } from "./interfaces/lightspeedApi"; import { mapError } from "./utils/handleApiError"; - -export function loadFetch(): typeof fetch|undefined { - try { - return require('electron')?.net?.fetch; - } catch (err) { - // Not available. - } - return undefined; +export function loadFetch(): typeof fetch | undefined { + try { + return require("electron")?.net?.fetch; + } catch (err) { + // Not available. + } + return undefined; } - /** * Initializes the connection and registers all lifecycle event handlers. * @@ -391,7 +389,7 @@ export class AnsibleLanguageService { const fetch = loadFetch(); if (!fetch) { - return {"content": "Nothing"} as ExplanationResponse; // TODO + return { content: "Nothing" } as ExplanationResponse; // TODO } const result: ExplanationResponse = await fetch( `${getBaseUri(URL)}/api/v0/ai/explanations/`, @@ -400,7 +398,10 @@ export class AnsibleLanguageService { body, headers, }, - ).then((response) => {console.log(response); return response.json()}); + ).then((response) => { + console.log(response); + return response.json(); + }); // .post( // "/ai/explanations/", // { signal: AbortSignal.timeout(28000) }, diff --git a/src/features/lightspeed/base.ts b/src/features/lightspeed/base.ts index d4408e5da..7f9fc056a 100644 --- a/src/features/lightspeed/base.ts +++ b/src/features/lightspeed/base.ts @@ -195,11 +195,11 @@ export class LightSpeedManager { // https://github.com/Microsoft/vscode/issues/12588#issuecomment-2111861237 // https://github.com/microsoft/vscode/pull/198408 // https://github.com/chrmarti/vscode-network-proxy-test/blob/main/src/extension.ts#L245-L252 -export function loadFetch(): typeof fetch|undefined { - try { - return require('electron')?.net?.fetch; - } catch (err) { - // Not available. - } - return undefined; +export function loadFetch(): typeof fetch | undefined { + try { + return require("electron")?.net?.fetch; + } catch (err) { + // Not available. + } + return undefined; } diff --git a/src/features/lightspeed/lightSpeedOAuthProvider.ts b/src/features/lightspeed/lightSpeedOAuthProvider.ts index 7216e1d71..e23ba7829 100644 --- a/src/features/lightspeed/lightSpeedOAuthProvider.ts +++ b/src/features/lightspeed/lightSpeedOAuthProvider.ts @@ -40,8 +40,6 @@ import { LightspeedAuthSession } from "../../interfaces/lightspeed"; import { lightSpeedManager } from "../../extension"; import { loadFetch } from "../lightspeed/base"; - - const CODE_VERIFIER = generateCodeVerifier(); const CODE_CHALLENGE = generateCodeChallengeFromVerifier(CODE_VERIFIER); diff --git a/src/features/lightspeed/lightspeedUser.ts b/src/features/lightspeed/lightspeedUser.ts index 0a5d5d5eb..fe0df5333 100644 --- a/src/features/lightspeed/lightspeedUser.ts +++ b/src/features/lightspeed/lightspeedUser.ts @@ -22,7 +22,6 @@ import { Log } from "../../utils/logger"; import * as marked from "marked"; import { loadFetch } from "../lightspeed/base"; - export class LightspeedAccessDenied extends Error { constructor(message: string) { super(message); @@ -30,8 +29,6 @@ export class LightspeedAccessDenied extends Error { } } - - export enum AuthProviderType { rhsso = RHSSO_AUTH_ID, lightspeed = ANSIBLE_LIGHTSPEED_AUTH_ID,