Path: blob/main/extensions/copilot/src/platform/networking/common/capiClientFetchedValue.ts
13401 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import type { MakeRequestOptions, RequestMetadata } from '@vscode/copilot-api';6import { createAdvancedFetch } from '../../../shared-fetch-utils/common/advancedFetcher';7import type { HttpResponse } from '../../../shared-fetch-utils/common/fetchTypes';8import { FetchedValue } from '../../../shared-fetch-utils/common/fetchedValue';9import { authBlockedMiddleware } from '../../../shared-fetch-utils/common/middleware/authBlockedMiddleware';10import { etagMiddleware } from '../../../shared-fetch-utils/common/middleware/etagMiddleware';11import { serverErrorBackoffMiddleware } from '../../../shared-fetch-utils/common/middleware/serverErrorBackoffMiddleware';12import type { ICAPIClientService } from '../../endpoint/common/capiClient';13import type { IEnvService } from '../../env/common/envService';1415export interface CapiClientFetchedValueOptions<T> {16/**17* The request options passed to {@link ICAPIClientService.makeRequest}.18*/19readonly request: (() => MakeRequestOptions | Promise<MakeRequestOptions>);2021/**22* Metadata for the CAPI request (e.g. {@link RequestType}).23*/24readonly requestMetadata: RequestMetadata;2526/**27* Extracts the domain value `T` from the HTTP response.28*29* `body` is the JSON-parsed object on success, the raw response text30* when the body is not valid JSON (e.g. error pages), or `undefined`31* when the response has no body (e.g. 204, 304 handled by etag cache).32*/33readonly parseResponse: (response: HttpResponse) => Promise<T>;3435/**36* Determines whether the current cached value is stale and should be37* re-fetched. Passed through to {@link FetchedValueOptions.isStale}.38*/39readonly isStale: (value: T) => boolean;4041/**42* When `true`, automatically resolves once per minute to keep the cache43* hot. Passed through to {@link FetchedValueOptions.keepCacheHot}.44*/45readonly keepCacheHot?: boolean;46}4748/**49* Creates a {@link FetchedValue} that fetches via50* {@link ICAPIClientService.makeRequest} with the full advanced-fetcher51* middleware stack applied.52*53* This is the recommended way to create periodically-refreshed cached54* values backed by CAPI endpoints.55*56* @example57* ```ts58* const config = createCapiClientFetchedValue(capiClientService, envService, {59* request: async () => ({60* headers: { Authorization: `Bearer ${await getToken()}` },61* method: 'POST',62* json: { key: 'value' },63* }),64* requestMetadata: { type: RequestType.CopilotToken },65* isStale: (c) => c.expiresAt < Date.now(),66* });67*68* const fresh = await config.resolve();69* ```70*/71export function createCapiClientFetchedValue<T>(72capiClientService: ICAPIClientService,73envService: IEnvService,74options: CapiClientFetchedValueOptions<T>,75): FetchedValue<T> {76const {77request,78requestMetadata,79parseResponse,80isStale,81keepCacheHot,82} = options;8384const fetch = createAdvancedFetch<T>({85request: async () => {86const currentRequestOpts = await request();87return {88url: `capi:${requestMetadata.type}`,89headers: currentRequestOpts.headers ?? {},90method: currentRequestOpts.method ?? 'GET',91state: currentRequestOpts92};93},94httpFetch: async (httpRequest) => {95const response = await capiClientService.makeRequest<Response>({96...(httpRequest.state ?? {}),97method: httpRequest.method,98// Use the headers from the middleware pipeline (may include99// If-None-Match, If-Modified-Since, etc.)100headers: httpRequest.headers,101}, requestMetadata);102103return response;104},105parseResponse,106middleware: [107etagMiddleware(),108authBlockedMiddleware(),109serverErrorBackoffMiddleware(),110],111});112113return new FetchedValue<T>({114fetch,115isStale,116keepCacheHot,117});118}119120121