From 09800a1941f83fe972959701ae0f8cfd2c51a454 Mon Sep 17 00:00:00 2001 From: Timo van Veenendaal Date: Mon, 4 Nov 2024 10:37:39 -0800 Subject: [PATCH 01/11] [ts-http-runtime] minimal API surface --- .../review/azure-core-comparison.diff | 1309 ++++++++++++++--- .../review/ts-http-runtime.api.md | 430 +----- .../src/auth/tokenCredential.ts | 10 - sdk/core/ts-http-runtime/src/client/common.ts | 11 - .../src/client/operationOptionHelpers.ts | 1 - .../ts-http-runtime/src/client/sendRequest.ts | 1 - .../src/createPipelineFromOptions.ts | 7 +- sdk/core/ts-http-runtime/src/index.ts | 186 +-- sdk/core/ts-http-runtime/src/interfaces.ts | 6 - .../ts-http-runtime/src/pipelineRequest.ts | 8 - .../bearerTokenAuthenticationPolicy.ts | 1 - .../src/policies/tracingPolicy.ts | 165 --- .../throttlingRetryStrategy.ts | 2 +- .../src/tracing/instrumenter.ts | 84 -- .../ts-http-runtime/src/tracing/interfaces.ts | 329 ----- .../src/tracing/state-browser.mts | 11 - .../ts-http-runtime/src/tracing/state-cjs.cts | 11 - sdk/core/ts-http-runtime/src/tracing/state.ts | 14 - .../src/tracing/tracingClient.ts | 121 -- .../src/tracing/tracingContext.ts | 67 - .../bearerTokenAuthenticationPolicy.spec.ts | 4 +- .../test/defaultLogPolicy.spec.ts | 3 +- .../test/defaultRetryPolicy.spec.ts | 3 +- .../test/formDataPolicy.spec.ts | 16 +- ...TokenAuthenticationPolicyChallenge.spec.ts | 12 +- .../test/node/checkEnvironment.spec.ts | 11 +- .../test/node/nodeHttpClient.spec.ts | 4 +- .../test/node/proxyPolicy.spec.ts | 6 +- .../ts-http-runtime/test/pipeline.spec.ts | 4 +- .../test/tracing/instrumenter.spec.ts | 92 -- .../test/tracing/interfaces.spec.ts | 21 - .../test/tracing/tracingClient.spec.ts | 203 --- .../test/tracing/tracingContext.spec.ts | 121 -- .../test/tracingPolicy.spec.ts | 272 ---- .../test/util/aborterUtils.spec.ts | 3 +- .../ts-http-runtime/test/util/delay.spec.ts | 2 +- .../ts-http-runtime/test/util/sha256.spec.ts | 2 +- .../test/util/typeGuards.spec.ts | 2 +- 38 files changed, 1178 insertions(+), 2377 deletions(-) delete mode 100644 sdk/core/ts-http-runtime/src/policies/tracingPolicy.ts delete mode 100644 sdk/core/ts-http-runtime/src/tracing/instrumenter.ts delete mode 100644 sdk/core/ts-http-runtime/src/tracing/interfaces.ts delete mode 100644 sdk/core/ts-http-runtime/src/tracing/state-browser.mts delete mode 100644 sdk/core/ts-http-runtime/src/tracing/state-cjs.cts delete mode 100644 sdk/core/ts-http-runtime/src/tracing/state.ts delete mode 100644 sdk/core/ts-http-runtime/src/tracing/tracingClient.ts delete mode 100644 sdk/core/ts-http-runtime/src/tracing/tracingContext.ts delete mode 100644 sdk/core/ts-http-runtime/test/tracing/instrumenter.spec.ts delete mode 100644 sdk/core/ts-http-runtime/test/tracing/interfaces.spec.ts delete mode 100644 sdk/core/ts-http-runtime/test/tracing/tracingClient.spec.ts delete mode 100644 sdk/core/ts-http-runtime/test/tracing/tracingContext.spec.ts delete mode 100644 sdk/core/ts-http-runtime/test/tracingPolicy.spec.ts diff --git a/sdk/core/ts-http-runtime/review/azure-core-comparison.diff b/sdk/core/ts-http-runtime/review/azure-core-comparison.diff index 4339d850a2ea..260d26c3c066 100644 --- a/sdk/core/ts-http-runtime/review/azure-core-comparison.diff +++ b/sdk/core/ts-http-runtime/review/azure-core-comparison.diff @@ -300,10 +300,10 @@ index 9db25cf..df8291c 100644 /** * Represents a credential defined by a static API key. diff --git a/src/auth/tokenCredential.ts b/src/auth/tokenCredential.ts -index 395b112..6a42777 100644 +index 395b112..19d080b 100644 --- a/src/auth/tokenCredential.ts +++ b/src/auth/tokenCredential.ts -@@ -1,9 +1,8 @@ +@@ -1,9 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. @@ -311,11 +311,26 @@ index 395b112..6a42777 100644 -import type { TracingContext } from "./tracing.js"; -import type { HttpMethods } from "@azure/core-util"; +import type { AbortSignalLike } from "../abort-controller/AbortSignalLike.js"; -+import type { TracingContext } from "../tracing/interfaces.js"; /** * Represents a credential capable of providing an authentication token. -@@ -60,28 +59,6 @@ export interface GetTokenOptions { +@@ -39,15 +37,6 @@ export interface GetTokenOptions { + */ + timeout?: number; + }; +- /** +- * Options used when tracing is enabled. +- */ +- tracingOptions?: { +- /** +- * Tracing Context for the current request. +- */ +- tracingContext?: TracingContext; +- }; + /** + * Claim details to perform the Continuous Access Evaluation authentication flow + */ +@@ -60,28 +49,6 @@ export interface GetTokenOptions { * Allows specifying a tenantId. Useful to handle challenges that provide tenant Id hints. */ tenantId?: string; @@ -344,7 +359,7 @@ index 395b112..6a42777 100644 } /** -@@ -103,26 +80,7 @@ export interface AccessToken { +@@ -103,26 +70,7 @@ export interface AccessToken { */ refreshAfterTimestamp?: number; @@ -464,10 +479,10 @@ index 30dac33..66ea99c 100644 if (!cachedHttpClient) { cachedHttpClient = createDefaultHttpClient(); diff --git a/src/client/common.ts b/src/client/common.ts -index 026f516..edda1c6 100644 +index 026f516..5d588ed 100644 --- a/src/client/common.ts +++ b/src/client/common.ts -@@ -3,19 +3,18 @@ +@@ -3,19 +3,17 @@ import type { HttpClient, @@ -488,13 +503,24 @@ index 026f516..edda1c6 100644 +} from "../interfaces.js"; +import type { Pipeline, PipelinePolicy } from "../pipeline.js"; +import type { AbortSignalLike } from "../abort-controller/AbortSignalLike.js"; -+import type { OperationTracingOptions } from "../tracing/interfaces.js"; +import type { PipelineOptions } from "../createPipelineFromOptions.js"; +import type { LogPolicyOptions } from "../policies/logPolicy.js"; /** * Shape of the default request parameters, this may be overridden by the specific -@@ -91,16 +90,9 @@ export type RequestParameters = { +@@ -74,11 +72,6 @@ export type RequestParameters = { + */ + abortSignal?: AbortSignalLike; + +- /** +- * Options used when tracing is enabled. +- */ +- tracingOptions?: OperationTracingOptions; +- + /** + * A function to be called each time a response is received from the server + * while performing the requested operation. +@@ -91,16 +84,9 @@ export type RequestParameters = { * A function to be called each time a response is received from the server * while performing the requested operation. * May be called multiple times. @@ -513,7 +539,19 @@ index 026f516..edda1c6 100644 /** * Wrapper object for http request and response. Deserialized object is stored in -@@ -202,7 +194,7 @@ export interface Client { +@@ -135,11 +121,6 @@ export interface OperationOptions { + * Options used when creating and sending HTTP requests for this operation. + */ + requestOptions?: OperationRequestOptions; +- /** +- * Options used when tracing is enabled. +- */ +- tracingOptions?: OperationTracingOptions; +- + /** + * A function to be called each time a response is received from the server + * while performing the requested operation. +@@ -202,7 +183,7 @@ export interface Client { * strong types. When used by the codegen this type gets overridden with the generated * types. For example: * ```typescript snippet:path_example @@ -522,7 +560,7 @@ index 026f516..edda1c6 100644 * * type MyClient = Client & { * path: Routes; -@@ -326,11 +318,9 @@ export type ClientOptions = PipelineOptions & { +@@ -326,11 +307,9 @@ export type ClientOptions = PipelineOptions & { */ apiKeyHeaderName?: string; }; @@ -722,6 +760,18 @@ index e34a065..2bc3df1 100644 /** * Describes a single part in a multipart body. +diff --git a/src/client/operationOptionHelpers.ts b/src/client/operationOptionHelpers.ts +index c7c1eb6..15bdc45 100644 +--- a/src/client/operationOptionHelpers.ts ++++ b/src/client/operationOptionHelpers.ts +@@ -16,7 +16,6 @@ export function operationOptionsToRequestParameters(options: OperationOptions): + abortSignal: options.abortSignal, + onUploadProgress: options.requestOptions?.onUploadProgress, + onDownloadProgress: options.requestOptions?.onDownloadProgress, +- tracingOptions: options.tracingOptions, + headers: { ...options.requestOptions?.headers }, + onResponse: options.onResponse, + }; diff --git a/src/client/restError.ts b/src/client/restError.ts index 1ecc969..9b98b21 100644 --- a/src/client/restError.ts @@ -739,7 +789,7 @@ index 1ecc969..9b98b21 100644 /** diff --git a/src/client/sendRequest.ts b/src/client/sendRequest.ts -index 9e947e6..2f30ba0 100644 +index 9e947e6..f2a1cac 100644 --- a/src/client/sendRequest.ts +++ b/src/client/sendRequest.ts @@ -5,19 +5,16 @@ import type { @@ -778,6 +828,14 @@ index 9e947e6..2f30ba0 100644 } throw e; +@@ -136,7 +134,6 @@ function buildPipelineRequest( + multipartBody, + headers, + allowInsecureConnection: options.allowInsecureConnection, +- tracingOptions: options.tracingOptions, + abortSignal: options.abortSignal, + onUploadProgress: options.onUploadProgress, + onDownloadProgress: options.onDownloadProgress, diff --git a/src/constants.ts b/src/constants.ts index 8e49c6b..60da499 100644 --- a/src/constants.ts @@ -791,19 +849,18 @@ index 8e49c6b..60da499 100644 export const DEFAULT_RETRY_POLICY_COUNT = 3; diff --git a/src/createPipelineFromOptions.ts b/src/createPipelineFromOptions.ts -index 2a2bd41..ede8855 100644 +index 2a2bd41..6066e07 100644 --- a/src/createPipelineFromOptions.ts +++ b/src/createPipelineFromOptions.ts -@@ -3,18 +3,18 @@ +@@ -3,18 +3,16 @@ import { type LogPolicyOptions, logPolicy } from "./policies/logPolicy.js"; import { type Pipeline, createEmptyPipeline } from "./pipeline.js"; -import type { PipelineRetryOptions, TlsSettings, ProxySettings } from "./interfaces.js"; -+import type { PipelineRetryOptions, TlsSettings } from "./interfaces.js"; ++import type { PipelineRetryOptions, ProxySettings, TlsSettings } from "./interfaces.js"; import { type RedirectPolicyOptions, redirectPolicy } from "./policies/redirectPolicy.js"; import { type UserAgentPolicyOptions, userAgentPolicy } from "./policies/userAgentPolicy.js"; -import { multipartPolicy, multipartPolicyName } from "./policies/multipartPolicy.js"; -+import type { ProxySettings } from "./index.js"; import { decompressResponsePolicy } from "./policies/decompressResponsePolicy.js"; import { defaultRetryPolicy } from "./policies/defaultRetryPolicy.js"; import { formDataPolicy } from "./policies/formDataPolicy.js"; @@ -812,12 +869,12 @@ index 2a2bd41..ede8855 100644 import { proxyPolicy } from "./policies/proxyPolicy.js"; -import { setClientRequestIdPolicy } from "./policies/setClientRequestIdPolicy.js"; import { tlsPolicy } from "./policies/tlsPolicy.js"; - import { tracingPolicy } from "./policies/tracingPolicy.js"; +-import { tracingPolicy } from "./policies/tracingPolicy.js"; +import { multipartPolicy, multipartPolicyName } from "./policies/multipartPolicy.js"; /** * Defines options that are used to configure the HTTP pipeline for -@@ -88,7 +88,6 @@ export function createPipelineFromOptions(options: InternalPipelineOptions): Pip +@@ -88,15 +86,11 @@ export function createPipelineFromOptions(options: InternalPipelineOptions): Pip pipeline.addPolicy(formDataPolicy(), { beforePolicies: [multipartPolicyName] }); pipeline.addPolicy(userAgentPolicy(options.userAgentOptions)); @@ -825,6 +882,14 @@ index 2a2bd41..ede8855 100644 // The multipart policy is added after policies with no phase, so that // policies can be added between it and formDataPolicy to modify // properties (e.g., making the boundary constant in recorded tests). + pipeline.addPolicy(multipartPolicy(), { afterPhase: "Deserialize" }); + pipeline.addPolicy(defaultRetryPolicy(options.retryOptions), { phase: "Retry" }); +- pipeline.addPolicy(tracingPolicy({ ...options.userAgentOptions, ...options.loggingOptions }), { +- afterPhase: "Retry", +- }); + if (isNodeLike) { + // Both XHR and Fetch expect to handle redirects automatically, + // so only include this policy when we're in Node. diff --git a/src/fetchHttpClient.ts b/src/fetchHttpClient.ts index e9751e2..e4f8769 100644 --- a/src/fetchHttpClient.ts @@ -839,81 +904,96 @@ index e9751e2..e4f8769 100644 HttpClient, HttpHeaders as PipelineHeaders, diff --git a/src/index.ts b/src/index.ts -index 688a7ea..76ca028 100644 +index 688a7ea..9cda6b3 100644 --- a/src/index.ts +++ b/src/index.ts -@@ -9,16 +9,16 @@ declare global { +@@ -9,117 +9,70 @@ declare global { interface ReadableStream {} interface TransformStream {} } - /* eslint-enable @typescript-eslint/no-unused-vars */ -export type { HttpMethods } from "@azure/core-util"; --export type { + ++export { AbortSignalLike } from "./abort-controller/AbortSignalLike.js"; +export { - Agent, ++ createClientLogger, ++ TypeSpecRuntimeLogger, ++ type TypeSpecRuntimeClientLogger, ++ type Debugger, ++} from "./logger/logger.js"; + export type { +- Agent, BodyPart, - FormDataMap, +- FormDataMap, FormDataValue, - HttpClient, - HttpHeaders, -+ HttpMethods, +- HttpClient, +- HttpHeaders, ++ RawHttpHeaders, KeyObject, - MultipartRequestBody, +- MultipartRequestBody, ++ PxfObject, ++ HttpClient, PipelineRequest, -@@ -34,92 +34,125 @@ export type { + PipelineResponse, +- PipelineRetryOptions, +- ProxySettings, +- PxfObject, +- RawHttpHeaders, +- RawHttpHeadersInput, +- RequestBodyType, + SendRequest, + TlsSettings, ++ Agent, ++ RequestBodyType, ++ FormDataMap, ++ HttpHeaders, ++ HttpMethods, ++ MultipartRequestBody, TransferProgressEvent, ++ ProxySettings, ++ RawHttpHeadersInput, ++ PipelineRetryOptions, } from "./interfaces.js"; - export { +-export { - type AddPolicyOptions as AddPipelineOptions, - type PipelinePhase, - type PipelinePolicy, - type Pipeline, -+ AddPolicyOptions as AddPipelineOptions, -+ PipelinePhase, -+ PipelinePolicy, -+ Pipeline, - createEmptyPipeline, - } from "./pipeline.js"; - export { - createPipelineFromOptions, +- createEmptyPipeline, +-} from "./pipeline.js"; +-export { +- createPipelineFromOptions, - type TelemetryOptions, - type InternalPipelineOptions, - type PipelineOptions, -+ TelemetryOptions, -+ InternalPipelineOptions, -+ PipelineOptions, - } from "./createPipelineFromOptions.js"; - export { createDefaultHttpClient } from "./defaultHttpClient.js"; +-} from "./createPipelineFromOptions.js"; +-export { createDefaultHttpClient } from "./defaultHttpClient.js"; export { createHttpHeaders } from "./httpHeaders.js"; -export { createPipelineRequest, type PipelineRequestOptions } from "./pipelineRequest.js"; -export { RestError, type RestErrorOptions, isRestError } from "./restError.js"; -+export { createPipelineRequest, PipelineRequestOptions } from "./pipelineRequest.js"; -+export { RestError, RestErrorOptions, isRestError } from "./restError.js"; - export { - decompressResponsePolicy, - decompressResponsePolicyName, - } from "./policies/decompressResponsePolicy.js"; +-export { +- decompressResponsePolicy, +- decompressResponsePolicyName, +-} from "./policies/decompressResponsePolicy.js"; -export { - exponentialRetryPolicy, - type ExponentialRetryPolicyOptions, - exponentialRetryPolicyName, -} from "./policies/exponentialRetryPolicy.js"; --export { ++export { isKeyCredential, type KeyCredential } from "./auth/keyCredential.js"; + export { - setClientRequestIdPolicy, - setClientRequestIdPolicyName, -} from "./policies/setClientRequestIdPolicy.js"; -export { logPolicy, logPolicyName, type LogPolicyOptions } from "./policies/logPolicy.js"; -+export { logPolicy, logPolicyName, LogPolicyOptions } from "./policies/logPolicy.js"; - export { multipartPolicy, multipartPolicyName } from "./policies/multipartPolicy.js"; - export { proxyPolicy, proxyPolicyName, getDefaultProxySettings } from "./policies/proxyPolicy.js"; - export { - redirectPolicy, - redirectPolicyName, +-export { multipartPolicy, multipartPolicyName } from "./policies/multipartPolicy.js"; +-export { proxyPolicy, proxyPolicyName, getDefaultProxySettings } from "./policies/proxyPolicy.js"; +-export { +- redirectPolicy, +- redirectPolicyName, - type RedirectPolicyOptions, -+ RedirectPolicyOptions, - } from "./policies/redirectPolicy.js"; +-} from "./policies/redirectPolicy.js"; -export { - systemErrorRetryPolicy, - type SystemErrorRetryPolicyOptions, @@ -925,127 +1005,84 @@ index 688a7ea..76ca028 100644 - type ThrottlingRetryPolicyOptions, -} from "./policies/throttlingRetryPolicy.js"; -export { retryPolicy, type RetryPolicyOptions } from "./policies/retryPolicy.js"; --export type { ++ isTokenCredential, ++ type TokenCredential, ++ type GetTokenOptions, ++ type AccessToken, ++} from "./auth/tokenCredential.js"; ++export { createPipelineRequest, type PipelineRequestOptions } from "./pipelineRequest.js"; ++export type { Pipeline, PipelinePolicy, AddPolicyOptions, PipelinePhase } from "./pipeline.js"; ++export { RestError, isRestError, type RestErrorOptions } from "./restError.js"; ++export { stringToUint8Array, uint8ArrayToString, type EncodingType } from "./util/bytesEncoding.js"; ++export { getClient } from "./client/getClient.js"; + export type { - RetryStrategy, - RetryInformation, - RetryModifiers, -} from "./retryStrategies/retryStrategy.js"; - export { - tracingPolicy, - tracingPolicyName, +-export { +- tracingPolicy, +- tracingPolicyName, - type TracingPolicyOptions, -+ TracingPolicyOptions, - } from "./policies/tracingPolicy.js"; +-} from "./policies/tracingPolicy.js"; -export { - defaultRetryPolicy, - type DefaultRetryPolicyOptions, -} from "./policies/defaultRetryPolicy.js"; -+export { defaultRetryPolicy, DefaultRetryPolicyOptions } from "./policies/defaultRetryPolicy.js"; - export { - userAgentPolicy, - userAgentPolicyName, +-export { +- userAgentPolicy, +- userAgentPolicyName, - type UserAgentPolicyOptions, -+ UserAgentPolicyOptions, - } from "./policies/userAgentPolicy.js"; - export { tlsPolicy, tlsPolicyName } from "./policies/tlsPolicy.js"; - export { formDataPolicy, formDataPolicyName } from "./policies/formDataPolicy.js"; - export { - bearerTokenAuthenticationPolicy, +-} from "./policies/userAgentPolicy.js"; +-export { tlsPolicy, tlsPolicyName } from "./policies/tlsPolicy.js"; +-export { formDataPolicy, formDataPolicyName } from "./policies/formDataPolicy.js"; +-export { +- bearerTokenAuthenticationPolicy, - type BearerTokenAuthenticationPolicyOptions, -+ BearerTokenAuthenticationPolicyOptions, - bearerTokenAuthenticationPolicyName, +- bearerTokenAuthenticationPolicyName, - type ChallengeCallbacks, - type AuthorizeRequestOptions, - type AuthorizeRequestOnChallengeOptions, -+ ChallengeCallbacks, -+ AuthorizeRequestOptions, -+ AuthorizeRequestOnChallengeOptions, - } from "./policies/bearerTokenAuthenticationPolicy.js"; +-} from "./policies/bearerTokenAuthenticationPolicy.js"; -export { ndJsonPolicy, ndJsonPolicyName } from "./policies/ndJsonPolicy.js"; -+export { AbortSignalLike } from "./abort-controller/AbortSignalLike.js"; -+export { AbortError } from "./abort-controller/AbortError.js"; -+export { AccessToken, GetTokenOptions, TokenCredential } from "./auth/tokenCredential.js"; -+export { KeyCredential, isKeyCredential } from "./auth/keyCredential.js"; -+export { -+ AddEventOptions, -+ Instrumenter, -+ InstrumenterSpanOptions, -+ OperationTracingOptions, -+ OptionsWithTracingContext, -+ Resolved, -+ SpanStatus, -+ SpanStatusError, -+ SpanStatusSuccess, -+ TracingClient, -+ TracingClientOptions, -+ TracingContext, -+ TracingSpan, -+ TracingSpanKind, -+ TracingSpanLink, -+ TracingSpanOptions, -+} from "./tracing/interfaces.js"; -+export { useInstrumenter } from "./tracing/instrumenter.js"; -+export { createTracingClient } from "./tracing/tracingClient.js"; -+// from core-util -+export { delay, DelayOptions, calculateRetryDelay } from "./util/delay.js"; -+export { -+ AbortOptions, -+ cancelablePromiseRace, -+ AbortablePromiseBuilder, -+} from "./util/aborterUtils.js"; - export { +-export { - auxiliaryAuthenticationHeaderPolicy, - type AuxiliaryAuthenticationHeaderPolicyOptions, - auxiliaryAuthenticationHeaderPolicyName, -} from "./policies/auxiliaryAuthenticationHeaderPolicy.js"; -+ createAbortablePromise, -+ CreateAbortablePromiseOptions, -+} from "./util/createAbortablePromise.js"; -+export { getRandomIntegerInclusive } from "./util/random.js"; -+export { isObject, UnknownObject } from "./util/object.js"; -+export { isError, getErrorMessage } from "./util/error.js"; - export { - createFile, - createFileFromStream, +-export { +- createFile, +- createFileFromStream, - type CreateFileOptions, - type CreateFileFromStreamOptions, -+ CreateFileOptions, -+ CreateFileFromStreamOptions, - } from "./util/file.js"; -+export { computeSha256Hash, computeSha256Hmac } from "./util/sha256.js"; -+export { isDefined, isObjectWithProperties, objectHasProperty } from "./util/typeGuards.js"; -+export { randomUUID } from "./util/uuidUtils.js"; -+export { -+ isBrowser, -+ isBun, -+ isNode, -+ isNodeLike, -+ isNodeRuntime, -+ isDeno, -+ isReactNative, -+ isWebWorker, -+} from "./util/checkEnvironment.js"; -+export { uint8ArrayToString, stringToUint8Array, EncodingType } from "./util/bytesEncoding.js"; -+export { -+ Debugger, -+ TypeSpecRuntimeLogger, -+ TypeSpecRuntimeLogLevel, -+ TypeSpecRuntimeClientLogger, -+} from "./logger/logger.js"; -+// client -+export { createRestError } from "./client/restError.js"; -+export { -+ addCredentialPipelinePolicy, -+ AddCredentialPipelinePolicyOptions, -+} from "./client/clientHelpers.js"; -+export { operationOptionsToRequestParameters } from "./client/operationOptionHelpers.js"; -+export * from "./client/getClient.js"; -+export * from "./client/common.js"; +-} from "./util/file.js"; ++ Client, ++ ClientOptions, ++ OperationOptions, ++ AdditionalPolicyConfig, ++ PathUnchecked, ++ PathUncheckedResponse, ++ HttpResponse, ++ RawResponseCallback, ++ OperationRequestOptions, ++ PathParameters, ++ ResourceMethods, ++ PathParameterWithOptions, ++ StreamableMethod, ++ RequestParameters, ++ HttpNodeStreamResponse, ++ HttpBrowserStreamResponse, ++ FullOperationResponse, ++} from "./client/common.js"; ++export type { PipelineOptions, TelemetryOptions } from "./createPipelineFromOptions.js"; ++export type { LogPolicyOptions as LogOptions } from "./policies/logPolicy.js"; ++export type { RedirectPolicyOptions as RedirectOptions } from "./policies/redirectPolicy.js"; ++export type { UserAgentPolicyOptions as UserAgentOptions } from "./policies/userAgentPolicy.js"; diff --git a/src/interfaces.ts b/src/interfaces.ts -index 26b806d..3e3ca58 100644 +index 26b806d..5d790ad 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts -@@ -1,9 +1,8 @@ +@@ -1,9 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. @@ -1053,11 +1090,22 @@ index 26b806d..3e3ca58 100644 -import type { OperationTracingOptions } from "@azure/core-tracing"; -import type { HttpMethods } from "@azure/core-util"; +import type { AbortSignalLike } from "./abort-controller/AbortSignalLike.js"; -+import type { OperationTracingOptions } from "./tracing/interfaces.js"; /** * A HttpHeaders collection represented as a simple JSON object. -@@ -315,6 +314,21 @@ export type TransferProgressEvent = { +@@ -204,11 +202,6 @@ export interface PipelineRequest { + */ + abortSignal?: AbortSignalLike; + +- /** +- * Tracing options to use for any created Spans. +- */ +- tracingOptions?: OperationTracingOptions; +- + /** + * Callback which fires upon upload progress. + */ +@@ -315,6 +308,21 @@ export type TransferProgressEvent = { loadedBytes: number; }; @@ -1290,7 +1338,7 @@ index 1ac007a..66abe50 100644 HttpClient, HttpHeaders, diff --git a/src/pipelineRequest.ts b/src/pipelineRequest.ts -index 8d1648d..50d7f83 100644 +index 8d1648d..5f67f7d 100644 --- a/src/pipelineRequest.ts +++ b/src/pipelineRequest.ts @@ -4,6 +4,7 @@ @@ -1301,7 +1349,7 @@ index 8d1648d..50d7f83 100644 MultipartRequestBody, PipelineRequest, ProxySettings, -@@ -11,10 +12,9 @@ import type { +@@ -11,10 +12,8 @@ import type { TransferProgressEvent, } from "./interfaces.js"; import { createHttpHeaders } from "./httpHeaders.js"; @@ -1311,10 +1359,37 @@ index 8d1648d..50d7f83 100644 -import type { HttpMethods } from "@azure/core-util"; +import type { AbortSignalLike } from "./abort-controller/AbortSignalLike.js"; +import { randomUUID } from "./util/uuidUtils.js"; -+import type { OperationTracingOptions } from "./tracing/interfaces.js"; /** * Settings to initialize a request. +@@ -100,11 +99,6 @@ export interface PipelineRequestOptions { + */ + abortSignal?: AbortSignalLike; + +- /** +- * Options used to create a span when tracing is enabled. +- */ +- tracingOptions?: OperationTracingOptions; +- + /** + * Callback which fires upon upload progress. + */ +@@ -133,7 +127,6 @@ class PipelineRequestImpl implements PipelineRequest { + public disableKeepAlive: boolean; + public abortSignal?: AbortSignalLike; + public requestId: string; +- public tracingOptions?: OperationTracingOptions; + public allowInsecureConnection?: boolean; + public onUploadProgress?: (progress: TransferProgressEvent) => void; + public onDownloadProgress?: (progress: TransferProgressEvent) => void; +@@ -151,7 +144,6 @@ class PipelineRequestImpl implements PipelineRequest { + this.streamResponseStatusCodes = options.streamResponseStatusCodes; + this.withCredentials = options.withCredentials ?? false; + this.abortSignal = options.abortSignal; +- this.tracingOptions = options.tracingOptions; + this.onUploadProgress = options.onUploadProgress; + this.onDownloadProgress = options.onDownloadProgress; + this.requestId = options.requestId || randomUUID(); diff --git a/src/policies/auxiliaryAuthenticationHeaderPolicy.ts b/src/policies/auxiliaryAuthenticationHeaderPolicy.ts deleted file mode 100644 index 55110a0..0000000 @@ -1428,7 +1503,7 @@ index 55110a0..0000000 - }; -} diff --git a/src/policies/bearerTokenAuthenticationPolicy.ts b/src/policies/bearerTokenAuthenticationPolicy.ts -index 898e5a6..87e8f5e 100644 +index 898e5a6..1cd3978 100644 --- a/src/policies/bearerTokenAuthenticationPolicy.ts +++ b/src/policies/bearerTokenAuthenticationPolicy.ts @@ -1,13 +1,12 @@ @@ -1465,7 +1540,7 @@ index 898e5a6..87e8f5e 100644 } /** -@@ -100,43 +99,18 @@ export interface BearerTokenAuthenticationPolicyOptions { +@@ -100,43 +99,17 @@ export interface BearerTokenAuthenticationPolicyOptions { /** * A logger can be sent for debugging purposes. */ @@ -1504,14 +1579,14 @@ index 898e5a6..87e8f5e 100644 - // Enable CAE true by default const getTokenOptions: GetTokenOptions = { abortSignal: request.abortSignal, - tracingOptions: request.tracingOptions, +- tracingOptions: request.tracingOptions, - enableCae: true, }; - const accessToken = await getAccessToken(scopes, getTokenOptions); if (accessToken) { -@@ -148,34 +122,12 @@ async function defaultAuthorizeRequest(options: AuthorizeRequestOptions): Promis +@@ -148,34 +121,12 @@ async function defaultAuthorizeRequest(options: AuthorizeRequestOptions): Promis * We will retrieve the challenge only if the response status code was 401, * and if the response contained the header "WWW-Authenticate" with a non-empty value. */ @@ -1551,7 +1626,7 @@ index 898e5a6..87e8f5e 100644 } /** -@@ -190,6 +142,8 @@ export function bearerTokenAuthenticationPolicy( +@@ -190,6 +141,8 @@ export function bearerTokenAuthenticationPolicy( const callbacks = { authorizeRequest: challengeCallbacks?.authorizeRequest ?? defaultAuthorizeRequest, authorizeRequestOnChallenge: challengeCallbacks?.authorizeRequestOnChallenge, @@ -1560,7 +1635,7 @@ index 898e5a6..87e8f5e 100644 }; // This function encapsulates the entire process of reliably retrieving the token -@@ -231,40 +185,20 @@ export function bearerTokenAuthenticationPolicy( +@@ -231,40 +184,20 @@ export function bearerTokenAuthenticationPolicy( let response: PipelineResponse; let error: Error | undefined; @@ -1614,7 +1689,7 @@ index 898e5a6..87e8f5e 100644 scopes: Array.isArray(scopes) ? scopes : [scopes], request, response, -@@ -272,41 +206,8 @@ export function bearerTokenAuthenticationPolicy( +@@ -272,41 +205,8 @@ export function bearerTokenAuthenticationPolicy( logger, }); @@ -1657,7 +1732,7 @@ index 898e5a6..87e8f5e 100644 } } -@@ -318,64 +219,3 @@ export function bearerTokenAuthenticationPolicy( +@@ -318,64 +218,3 @@ export function bearerTokenAuthenticationPolicy( }, }; } @@ -1867,40 +1942,180 @@ index a40d4ab..0000000 - }; -} diff --git a/src/policies/tracingPolicy.ts b/src/policies/tracingPolicy.ts -index 5e69548..34fad89 100644 +deleted file mode 100644 +index 5e69548..0000000 --- a/src/policies/tracingPolicy.ts -+++ b/src/policies/tracingPolicy.ts -@@ -1,18 +1,14 @@ - // Copyright (c) Microsoft Corporation. - // Licensed under the MIT License. - ++++ /dev/null +@@ -1,169 +0,0 @@ +-// Copyright (c) Microsoft Corporation. +-// Licensed under the MIT License. +- -import { - type TracingClient, - type TracingContext, - type TracingSpan, - createTracingClient, -} from "@azure/core-tracing"; -+import type { TracingClient, TracingContext, TracingSpan } from "../tracing/interfaces.js"; -+import { createTracingClient } from "../tracing/tracingClient.js"; - import { SDK_VERSION } from "../constants.js"; - import type { PipelineRequest, PipelineResponse, SendRequest } from "../interfaces.js"; - import type { PipelinePolicy } from "../pipeline.js"; - import { getUserAgentValue } from "../util/userAgent.js"; - import { logger } from "../log.js"; +-import { SDK_VERSION } from "../constants.js"; +-import type { PipelineRequest, PipelineResponse, SendRequest } from "../interfaces.js"; +-import type { PipelinePolicy } from "../pipeline.js"; +-import { getUserAgentValue } from "../util/userAgent.js"; +-import { logger } from "../log.js"; -import { getErrorMessage, isError } from "@azure/core-util"; -+import { getErrorMessage, isError } from "../util/error.js"; - import { isRestError } from "../restError.js"; - import { Sanitizer } from "../util/sanitizer.js"; - -@@ -92,7 +88,7 @@ function tryCreateTracingClient(): TracingClient | undefined { - try { - return createTracingClient({ - namespace: "", +-import { isRestError } from "../restError.js"; +-import { Sanitizer } from "../util/sanitizer.js"; +- +-/** +- * The programmatic identifier of the tracingPolicy. +- */ +-export const tracingPolicyName = "tracingPolicy"; +- +-/** +- * Options to configure the tracing policy. +- */ +-export interface TracingPolicyOptions { +- /** +- * String prefix to add to the user agent logged as metadata +- * on the generated Span. +- * Defaults to an empty string. +- */ +- userAgentPrefix?: string; +- /** +- * Query string names whose values will be logged when logging is enabled. By default no +- * query string values are logged. +- */ +- additionalAllowedQueryParameters?: string[]; +-} +- +-/** +- * A simple policy to create OpenTelemetry Spans for each request made by the pipeline +- * that has SpanOptions with a parent. +- * Requests made without a parent Span will not be recorded. +- * @param options - Options to configure the telemetry logged by the tracing policy. +- */ +-export function tracingPolicy(options: TracingPolicyOptions = {}): PipelinePolicy { +- const userAgentPromise = getUserAgentValue(options.userAgentPrefix); +- const sanitizer = new Sanitizer({ +- additionalAllowedQueryParameters: options.additionalAllowedQueryParameters, +- }); +- const tracingClient = tryCreateTracingClient(); +- +- return { +- name: tracingPolicyName, +- async sendRequest(request: PipelineRequest, next: SendRequest): Promise { +- if (!tracingClient) { +- return next(request); +- } +- +- const userAgent = await userAgentPromise; +- +- const spanAttributes = { +- "http.url": sanitizer.sanitizeUrl(request.url), +- "http.method": request.method, +- "http.user_agent": userAgent, +- requestId: request.requestId, +- }; +- if (userAgent) { +- spanAttributes["http.user_agent"] = userAgent; +- } +- +- const { span, tracingContext } = tryCreateSpan(tracingClient, request, spanAttributes) ?? {}; +- +- if (!span || !tracingContext) { +- return next(request); +- } +- +- try { +- const response = await tracingClient.withContext(tracingContext, next, request); +- tryProcessResponse(span, response); +- return response; +- } catch (err: any) { +- tryProcessError(span, err); +- throw err; +- } +- }, +- }; +-} +- +-function tryCreateTracingClient(): TracingClient | undefined { +- try { +- return createTracingClient({ +- namespace: "", - packageName: "@azure/core-rest-pipeline", -+ packageName: "@typespec/ts-http-runtime", - packageVersion: SDK_VERSION, - }); - } catch (e: unknown) { +- packageVersion: SDK_VERSION, +- }); +- } catch (e: unknown) { +- logger.warning(`Error when creating the TracingClient: ${getErrorMessage(e)}`); +- return undefined; +- } +-} +- +-function tryCreateSpan( +- tracingClient: TracingClient, +- request: PipelineRequest, +- spanAttributes: Record, +-): { span: TracingSpan; tracingContext: TracingContext } | undefined { +- try { +- // As per spec, we do not need to differentiate between HTTP and HTTPS in span name. +- const { span, updatedOptions } = tracingClient.startSpan( +- `HTTP ${request.method}`, +- { tracingOptions: request.tracingOptions }, +- { +- spanKind: "client", +- spanAttributes, +- }, +- ); +- +- // If the span is not recording, don't do any more work. +- if (!span.isRecording()) { +- span.end(); +- return undefined; +- } +- +- // set headers +- const headers = tracingClient.createRequestHeaders( +- updatedOptions.tracingOptions.tracingContext, +- ); +- for (const [key, value] of Object.entries(headers)) { +- request.headers.set(key, value); +- } +- return { span, tracingContext: updatedOptions.tracingOptions.tracingContext }; +- } catch (e: any) { +- logger.warning(`Skipping creating a tracing span due to an error: ${getErrorMessage(e)}`); +- return undefined; +- } +-} +- +-function tryProcessError(span: TracingSpan, error: unknown): void { +- try { +- span.setStatus({ +- status: "error", +- error: isError(error) ? error : undefined, +- }); +- if (isRestError(error) && error.statusCode) { +- span.setAttribute("http.status_code", error.statusCode); +- } +- span.end(); +- } catch (e: any) { +- logger.warning(`Skipping tracing span processing due to an error: ${getErrorMessage(e)}`); +- } +-} +- +-function tryProcessResponse(span: TracingSpan, response: PipelineResponse): void { +- try { +- span.setAttribute("http.status_code", response.status); +- const serviceRequestId = response.headers.get("x-ms-request-id"); +- if (serviceRequestId) { +- span.setAttribute("serviceRequestId", serviceRequestId); +- } +- span.setStatus({ +- status: "success", +- }); +- span.end(); +- } catch (e: any) { +- logger.warning(`Skipping tracing span processing due to an error: ${getErrorMessage(e)}`); +- } +-} diff --git a/src/restError.ts b/src/restError.ts index 0b2e69f..bc09e78 100644 --- a/src/restError.ts @@ -1950,10 +2165,16 @@ index a8b94fc..bc11b52 100644 * Function that determines how to proceed with the subsequent requests. * @param state - Retry state diff --git a/src/retryStrategies/throttlingRetryStrategy.ts b/src/retryStrategies/throttlingRetryStrategy.ts -index 2d4f87f..5c8d4ed 100644 +index 2d4f87f..d0f7298 100644 --- a/src/retryStrategies/throttlingRetryStrategy.ts +++ b/src/retryStrategies/throttlingRetryStrategy.ts -@@ -6,12 +6,12 @@ import { parseHeaderValueAsNumber } from "../util/helpers.js"; +@@ -1,17 +1,17 @@ + // Copyright (c) Microsoft Corporation. + // Licensed under the MIT License. + +-import type { PipelineResponse } from "../index.js"; ++import type { PipelineResponse } from "../interfaces.js"; + import { parseHeaderValueAsNumber } from "../util/helpers.js"; import type { RetryStrategy } from "./retryStrategy.js"; /** @@ -1997,47 +2218,685 @@ index 8992d7a..0000000 -} from "./interfaces.js"; -export { useInstrumenter } from "./instrumenter.js"; -export { createTracingClient } from "./tracingClient.js"; +diff --git a/src/tracing/instrumenter.ts b/src/tracing/instrumenter.ts +deleted file mode 100644 +index 28f5729..0000000 +--- a/src/tracing/instrumenter.ts ++++ /dev/null +@@ -1,84 +0,0 @@ +-// Copyright (c) Microsoft Corporation. +-// Licensed under the MIT License. +- +-import type { +- Instrumenter, +- InstrumenterSpanOptions, +- TracingContext, +- TracingSpan, +-} from "./interfaces.js"; +- +-import { createTracingContext } from "./tracingContext.js"; +-import { state } from "./state.js"; +- +-export function createDefaultTracingSpan(): TracingSpan { +- return { +- end: () => { +- // noop +- }, +- isRecording: () => false, +- recordException: () => { +- // noop +- }, +- setAttribute: () => { +- // noop +- }, +- setStatus: () => { +- // noop +- }, +- addEvent: () => { +- // noop +- }, +- }; +-} +- +-export function createDefaultInstrumenter(): Instrumenter { +- return { +- createRequestHeaders: (): Record => { +- return {}; +- }, +- parseTraceparentHeader: (): TracingContext | undefined => { +- return undefined; +- }, +- startSpan: ( +- _name: string, +- spanOptions: InstrumenterSpanOptions, +- ): { span: TracingSpan; tracingContext: TracingContext } => { +- return { +- span: createDefaultTracingSpan(), +- tracingContext: createTracingContext({ parentContext: spanOptions.tracingContext }), +- }; +- }, +- withContext< +- CallbackArgs extends unknown[], +- Callback extends (...args: CallbackArgs) => ReturnType, +- >( +- _context: TracingContext, +- callback: Callback, +- ...callbackArgs: CallbackArgs +- ): ReturnType { +- return callback(...callbackArgs); +- }, +- }; +-} +- +-/** +- * Extends the Azure SDK with support for a given instrumenter implementation. +- * +- * @param instrumenter - The instrumenter implementation to use. +- */ +-export function useInstrumenter(instrumenter: Instrumenter): void { +- state.instrumenterImplementation = instrumenter; +-} +- +-/** +- * Gets the currently set instrumenter, a No-Op instrumenter by default. +- * +- * @returns The currently set instrumenter +- */ +-export function getInstrumenter(): Instrumenter { +- if (!state.instrumenterImplementation) { +- state.instrumenterImplementation = createDefaultInstrumenter(); +- } +- return state.instrumenterImplementation; +-} diff --git a/src/tracing/interfaces.ts b/src/tracing/interfaces.ts -index 04d0785..fe8f87b 100644 +deleted file mode 100644 +index 04d0785..0000000 --- a/src/tracing/interfaces.ts -+++ b/src/tracing/interfaces.ts -@@ -27,7 +27,7 @@ export interface TracingClient { - * Example: - * - * ```ts snippet:with_span_example ++++ /dev/null +@@ -1,329 +0,0 @@ +-// Copyright (c) Microsoft Corporation. +-// Licensed under the MIT License. +- +-/** +- * A narrower version of TypeScript 4.5's Awaited type which Recursively +- * unwraps the "awaited type", emulating the behavior of `await`. +- */ +-export type Resolved = T extends { then(onfulfilled: infer F): any } // `await` only unwraps object types with a callable `then`. Non-object types are not unwrapped +- ? F extends (value: infer V) => any // if the argument to `then` is callable, extracts the first argument +- ? Resolved // recursively unwrap the value +- : never // the argument to `then` was not callable +- : T; // non-object or non-thenable +- +-/** +- * Represents a client that can integrate with the currently configured {@link Instrumenter}. +- * +- * Create an instance using {@link createTracingClient}. +- */ +-export interface TracingClient { +- /** +- * Wraps a callback in a tracing span, calls the callback, and closes the span. +- * +- * This is the primary interface for using Tracing and will handle error recording as well as setting the status on the span. +- * +- * Both synchronous and asynchronous functions will be awaited in order to reflect the result of the callback on the span. +- * +- * Example: +- * +- * ```ts snippet:with_span_example - * import { createTracingClient } from "@azure/core-tracing"; -+ * import { createTracingClient } from "@typespec/ts-http-runtime"; - * - * const tracingClient = createTracingClient({ - * namespace: "test.namespace", +- * +- * const tracingClient = createTracingClient({ +- * namespace: "test.namespace", +- * packageName: "test-package", +- * packageVersion: "1.0.0", +- * }); +- * const options = {}; +- * const myOperationResult = await tracingClient.withSpan( +- * "myClassName.myOperationName", +- * options, +- * (updatedOptions) => { +- * // Do something with the updated options. +- * return "myOperationResult"; +- * }, +- * ); +- * ``` +- * @param name - The name of the span. By convention this should be `${className}.${methodName}`. +- * @param operationOptions - The original options passed to the method. The callback will receive these options with the newly created {@link TracingContext}. +- * @param callback - The callback to be invoked with the updated options and newly created {@link TracingSpan}. +- */ +- withSpan< +- Options extends { tracingOptions?: OperationTracingOptions }, +- Callback extends ( +- updatedOptions: Options, +- span: Omit, +- ) => ReturnType, +- >( +- name: string, +- operationOptions: Options, +- callback: Callback, +- spanOptions?: TracingSpanOptions, +- ): Promise>>; +- /** +- * Starts a given span but does not set it as the active span. +- * +- * You must end the span using {@link TracingSpan.end}. +- * +- * Most of the time you will want to use {@link withSpan} instead. +- * +- * @param name - The name of the span. By convention this should be `${className}.${methodName}`. +- * @param operationOptions - The original operation options. +- * @param spanOptions - The options to use when creating the span. +- * +- * @returns A {@link TracingSpan} and the updated operation options. +- */ +- startSpan( +- name: string, +- operationOptions?: Options, +- spanOptions?: TracingSpanOptions, +- ): { +- span: TracingSpan; +- updatedOptions: OptionsWithTracingContext; +- }; +- /** +- * Wraps a callback with an active context and calls the callback. +- * Depending on the implementation, this may set the globally available active context. +- * +- * Useful when you want to leave the boundaries of the SDK (make a request or callback to user code) and are unable to use the {@link withSpan} API. +- * +- * @param context - The {@link TracingContext} to use as the active context in the scope of the callback. +- * @param callback - The callback to be invoked with the given context set as the globally active context. +- * @param callbackArgs - The callback arguments. +- */ +- withContext< +- CallbackArgs extends unknown[], +- Callback extends (...args: CallbackArgs) => ReturnType, +- >( +- context: TracingContext, +- callback: Callback, +- ...callbackArgs: CallbackArgs +- ): ReturnType; +- +- /** +- * Parses a traceparent header value into a {@link TracingSpanContext}. +- * +- * @param traceparentHeader - The traceparent header to parse. +- * @returns An implementation-specific identifier for the span. +- */ +- parseTraceparentHeader(traceparentHeader: string): TracingContext | undefined; +- +- /** +- * Creates a set of request headers to propagate tracing information to a backend. +- * +- * @param tracingContext - The context containing the span to propagate. +- * @returns The set of headers to add to a request. +- */ +- createRequestHeaders(tracingContext?: TracingContext): Record; +-} +- +-/** +- * Options that can be passed to {@link createTracingClient} +- */ +-export interface TracingClientOptions { +- /** The value of the az.namespace tracing attribute on newly created spans. */ +- namespace: string; +- /** The name of the package invoking this trace. */ +- packageName: string; +- /** An optional version of the package invoking this trace. */ +- packageVersion?: string; +-} +- +-/** The kind of span. */ +-export type TracingSpanKind = "client" | "server" | "producer" | "consumer" | "internal"; +- +-/** Options used to configure the newly created span. */ +-export interface TracingSpanOptions { +- /** The kind of span. Implementations should default this to "client". */ +- spanKind?: TracingSpanKind; +- /** A collection of {@link TracingSpanLink} to link to this span. */ +- spanLinks?: TracingSpanLink[]; +- /** Initial set of attributes to set on a span. */ +- spanAttributes?: { [key: string]: unknown }; +-} +- +-/** A pointer from the current {@link TracingSpan} to another span in the same or a different trace. */ +-export interface TracingSpanLink { +- /** The {@link TracingContext} containing the span context to link to. */ +- tracingContext: TracingContext; +- /** A set of attributes on the link. */ +- attributes?: { [key: string]: unknown }; +-} +- +-/** +- * Represents an implementation agnostic instrumenter. +- */ +-export interface Instrumenter { +- /** +- * Creates a new {@link TracingSpan} with the given name and options and sets it on a new context. +- * @param name - The name of the span. By convention this should be `${className}.${methodName}`. +- * @param spanOptions - The options to use when creating the span. +- * +- * @returns A {@link TracingSpan} that can be used to end the span, and the context this span has been set on. +- */ +- startSpan( +- name: string, +- spanOptions: InstrumenterSpanOptions, +- ): { span: TracingSpan; tracingContext: TracingContext }; +- /** +- * Wraps a callback with an active context and calls the callback. +- * Depending on the implementation, this may set the globally available active context. +- * +- * @param context - The {@link TracingContext} to use as the active context in the scope of the callback. +- * @param callback - The callback to be invoked with the given context set as the globally active context. +- * @param callbackArgs - The callback arguments. +- */ +- withContext< +- CallbackArgs extends unknown[], +- Callback extends (...args: CallbackArgs) => ReturnType, +- >( +- context: TracingContext, +- callback: Callback, +- ...callbackArgs: CallbackArgs +- ): ReturnType; +- +- /** +- * Provides an implementation-specific method to parse a {@link https://www.w3.org/TR/trace-context/#traceparent-header} +- * into a {@link TracingSpanContext} which can be used to link non-parented spans together. +- */ +- parseTraceparentHeader(traceparentHeader: string): TracingContext | undefined; +- /** +- * Provides an implementation-specific method to serialize a {@link TracingSpan} to a set of headers. +- * @param tracingContext - The context containing the span to serialize. +- */ +- createRequestHeaders(tracingContext?: TracingContext): Record; +-} +- +-/** +- * Options passed to {@link Instrumenter.startSpan} as a superset of {@link TracingSpanOptions}. +- */ +-export interface InstrumenterSpanOptions extends TracingSpanOptions { +- /** The name of the package invoking this trace. */ +- packageName: string; +- /** The version of the package invoking this trace. */ +- packageVersion?: string; +- /** The current tracing context. Defaults to an implementation-specific "active" context. */ +- tracingContext?: TracingContext; +-} +- +-/** +- * Status representing a successful operation that can be sent to {@link TracingSpan.setStatus} +- */ +-export type SpanStatusSuccess = { status: "success" }; +- +-/** +- * Status representing an error that can be sent to {@link TracingSpan.setStatus} +- */ +-export type SpanStatusError = { status: "error"; error?: Error | string }; +- +-/** +- * Represents the statuses that can be passed to {@link TracingSpan.setStatus}. +- * +- * By default, all spans will be created with status "unset". +- */ +-export type SpanStatus = SpanStatusSuccess | SpanStatusError; +- +-/** +- * Represents options you can pass to {@link TracingSpan.addEvent}. +- */ +-export interface AddEventOptions { +- /** +- * A set of attributes to attach to the event. +- */ +- attributes?: Record; +- /** +- * The start time of the event. +- */ +- startTime?: Date; +-} +- +-/** +- * Represents an implementation agnostic tracing span. +- */ +-export interface TracingSpan { +- /** +- * Sets the status of the span. When an error is provided, it will be recorded on the span as well. +- * +- * @param status - The {@link SpanStatus} to set on the span. +- */ +- setStatus(status: SpanStatus): void; +- +- /** +- * Sets a given attribute on a span. +- * +- * @param name - The attribute's name. +- * @param value - The attribute's value to set. May be any non-nullish value. +- */ +- setAttribute(name: string, value: unknown): void; +- +- /** +- * Ends the span. +- */ +- end(): void; +- +- /** +- * Records an exception on a {@link TracingSpan} without modifying its status. +- * +- * When recording an unhandled exception that should fail the span, please use {@link TracingSpan.setStatus} instead. +- * +- * @param exception - The exception to record on the span. +- * +- */ +- recordException(exception: Error | string): void; +- +- /** +- * Returns true if this {@link TracingSpan} is recording information. +- * +- * Depending on the span implementation, this may return false if the span is not being sampled. +- */ +- isRecording(): boolean; +- +- /** +- * Adds an event to the span. +- */ +- addEvent?(name: string, options?: AddEventOptions): void; +-} +- +-/** An immutable context bag of tracing values for the current operation. */ +-export interface TracingContext { +- /** +- * Sets a given object on a context. +- * @param key - The key of the given context value. +- * @param value - The value to set on the context. +- * +- * @returns - A new context with the given value set. +- */ +- setValue(key: symbol, value: unknown): TracingContext; +- /** +- * Gets an object from the context if it exists. +- * @param key - The key of the given context value. +- * +- * @returns - The value of the given context value if it exists, otherwise `undefined`. +- */ +- getValue(key: symbol): unknown; +- /** +- * Deletes an object from the context if it exists. +- * @param key - The key of the given context value to delete. +- */ +- deleteValue(key: symbol): TracingContext; +-} +- +-/** +- * Tracing options to set on an operation. +- */ +-export interface OperationTracingOptions { +- /** The context to use for created Tracing Spans. */ +- tracingContext?: TracingContext; +-} +- +-/** +- * A utility type for when we know a TracingContext has been set +- * as part of an operation's options. +- */ +-export type OptionsWithTracingContext< +- Options extends { tracingOptions?: OperationTracingOptions }, +-> = Options & { +- tracingOptions: { +- tracingContext: TracingContext; +- }; +-}; +diff --git a/src/tracing/state-browser.mts b/src/tracing/state-browser.mts +deleted file mode 100644 +index 6e56271..0000000 +--- a/src/tracing/state-browser.mts ++++ /dev/null +@@ -1,11 +0,0 @@ +-// Copyright (c) Microsoft Corporation. +-// Licensed under the MIT License. +- +-import type { Instrumenter } from "./interfaces.js"; +- +-/** +- * Browser-only implementation of the module's state. The browser esm variant will not load the commonjs state, so we do not need to share state between the two. +- */ +-export const state = { +- instrumenterImplementation: undefined as Instrumenter | undefined, +-}; +diff --git a/src/tracing/state-cjs.cts b/src/tracing/state-cjs.cts +deleted file mode 100644 +index 181bfa0..0000000 +--- a/src/tracing/state-cjs.cts ++++ /dev/null +@@ -1,11 +0,0 @@ +-// Copyright (c) Microsoft Corporation. +-// Licensed under the MIT License. +- +-/** +- * @internal +- * +- * Holds the singleton instrumenter, to be shared across CJS and ESM imports. +- */ +-export const state = { +- instrumenterImplementation: undefined, +-}; diff --git a/src/tracing/state.ts b/src/tracing/state.ts -index c7e7a4a..ccb13f4 100644 +deleted file mode 100644 +index c7e7a4a..0000000 --- a/src/tracing/state.ts -+++ b/src/tracing/state.ts -@@ -4,7 +4,7 @@ - import type { Instrumenter } from "./interfaces.js"; - // @ts-expect-error The recommended approach to sharing module state between ESM and CJS. - // See https://github.com/isaacs/tshy/blob/main/README.md#module-local-state for additional information. ++++ /dev/null +@@ -1,14 +0,0 @@ +-// Copyright (c) Microsoft Corporation. +-// Licensed under the MIT License. +- +-import type { Instrumenter } from "./interfaces.js"; +-// @ts-expect-error The recommended approach to sharing module state between ESM and CJS. +-// See https://github.com/isaacs/tshy/blob/main/README.md#module-local-state for additional information. -import { state as cjsState } from "../commonjs/state.js"; -+import { state as cjsState } from "../commonjs/tracing/state.js"; - - /** - * Defines the shared state between CJS and ESM by re-exporting the CJS state. +- +-/** +- * Defines the shared state between CJS and ESM by re-exporting the CJS state. +- */ +-export const state = cjsState as { +- instrumenterImplementation: Instrumenter | undefined; +-}; +diff --git a/src/tracing/tracingClient.ts b/src/tracing/tracingClient.ts +deleted file mode 100644 +index 3e9603d..0000000 +--- a/src/tracing/tracingClient.ts ++++ /dev/null +@@ -1,121 +0,0 @@ +-// Copyright (c) Microsoft Corporation. +-// Licensed under the MIT License. +- +-import type { +- OperationTracingOptions, +- OptionsWithTracingContext, +- Resolved, +- TracingClient, +- TracingClientOptions, +- TracingContext, +- TracingSpan, +- TracingSpanOptions, +-} from "./interfaces.js"; +-import { getInstrumenter } from "./instrumenter.js"; +-import { knownContextKeys } from "./tracingContext.js"; +- +-/** +- * Creates a new tracing client. +- * +- * @param options - Options used to configure the tracing client. +- * @returns - An instance of {@link TracingClient}. +- */ +-export function createTracingClient(options: TracingClientOptions): TracingClient { +- const { namespace, packageName, packageVersion } = options; +- +- function startSpan( +- name: string, +- operationOptions?: Options, +- spanOptions?: TracingSpanOptions, +- ): { +- span: TracingSpan; +- updatedOptions: OptionsWithTracingContext; +- } { +- const startSpanResult = getInstrumenter().startSpan(name, { +- ...spanOptions, +- packageName: packageName, +- packageVersion: packageVersion, +- tracingContext: operationOptions?.tracingOptions?.tracingContext, +- }); +- let tracingContext = startSpanResult.tracingContext; +- const span = startSpanResult.span; +- if (!tracingContext.getValue(knownContextKeys.namespace)) { +- tracingContext = tracingContext.setValue(knownContextKeys.namespace, namespace); +- } +- span.setAttribute("az.namespace", tracingContext.getValue(knownContextKeys.namespace)); +- const updatedOptions: OptionsWithTracingContext = Object.assign({}, operationOptions, { +- tracingOptions: { ...operationOptions?.tracingOptions, tracingContext }, +- }); +- +- return { +- span, +- updatedOptions, +- }; +- } +- +- async function withSpan< +- Options extends { tracingOptions?: OperationTracingOptions }, +- Callback extends ( +- updatedOptions: Options, +- span: Omit, +- ) => ReturnType, +- >( +- name: string, +- operationOptions: Options, +- callback: Callback, +- spanOptions?: TracingSpanOptions, +- ): Promise>> { +- const { span, updatedOptions } = startSpan(name, operationOptions, spanOptions); +- try { +- const result = await withContext(updatedOptions.tracingOptions.tracingContext, () => +- Promise.resolve(callback(updatedOptions, span)), +- ); +- span.setStatus({ status: "success" }); +- return result as ReturnType; +- } catch (err: any) { +- span.setStatus({ status: "error", error: err }); +- throw err; +- } finally { +- span.end(); +- } +- } +- +- function withContext< +- CallbackArgs extends unknown[], +- Callback extends (...args: CallbackArgs) => ReturnType, +- >( +- context: TracingContext, +- callback: Callback, +- ...callbackArgs: CallbackArgs +- ): ReturnType { +- return getInstrumenter().withContext(context, callback, ...callbackArgs); +- } +- +- /** +- * Parses a traceparent header value into a span identifier. +- * +- * @param traceparentHeader - The traceparent header to parse. +- * @returns An implementation-specific identifier for the span. +- */ +- function parseTraceparentHeader(traceparentHeader: string): TracingContext | undefined { +- return getInstrumenter().parseTraceparentHeader(traceparentHeader); +- } +- +- /** +- * Creates a set of request headers to propagate tracing information to a backend. +- * +- * @param tracingContext - The context containing the span to serialize. +- * @returns The set of headers to add to a request. +- */ +- function createRequestHeaders(tracingContext?: TracingContext): Record { +- return getInstrumenter().createRequestHeaders(tracingContext); +- } +- +- return { +- startSpan, +- withSpan, +- withContext, +- parseTraceparentHeader, +- createRequestHeaders, +- }; +-} diff --git a/src/tracing/tracingContext.ts b/src/tracing/tracingContext.ts -index 84999ab..5528c7a 100644 +deleted file mode 100644 +index 84999ab..0000000 --- a/src/tracing/tracingContext.ts -+++ b/src/tracing/tracingContext.ts -@@ -5,8 +5,8 @@ import type { TracingContext, TracingSpan } from "./interfaces.js"; - - /** @internal */ - export const knownContextKeys = { ++++ /dev/null +@@ -1,67 +0,0 @@ +-// Copyright (c) Microsoft Corporation. +-// Licensed under the MIT License. +- +-import type { TracingContext, TracingSpan } from "./interfaces.js"; +- +-/** @internal */ +-export const knownContextKeys = { - span: Symbol.for("@azure/core-tracing span"), - namespace: Symbol.for("@azure/core-tracing namespace"), -+ span: Symbol.for("@typespec/ts-http-runtime span"), -+ namespace: Symbol.for("@typespec/ts-http-runtime namespace"), - }; - - /** +-}; +- +-/** +- * Creates a new {@link TracingContext} with the given options. +- * @param options - A set of known keys that may be set on the context. +- * @returns A new {@link TracingContext} with the given options. +- * +- * @internal +- */ +-export function createTracingContext(options: CreateTracingContextOptions = {}): TracingContext { +- let context: TracingContext = new TracingContextImpl(options.parentContext); +- if (options.span) { +- context = context.setValue(knownContextKeys.span, options.span); +- } +- if (options.namespace) { +- context = context.setValue(knownContextKeys.namespace, options.namespace); +- } +- return context; +-} +- +-/** @internal */ +-export class TracingContextImpl implements TracingContext { +- private _contextMap: Map; +- constructor(initialContext?: TracingContext) { +- this._contextMap = +- initialContext instanceof TracingContextImpl +- ? new Map(initialContext._contextMap) +- : new Map(); +- } +- +- setValue(key: symbol, value: unknown): TracingContext { +- const newContext = new TracingContextImpl(this); +- newContext._contextMap.set(key, value); +- return newContext; +- } +- +- getValue(key: symbol): unknown { +- return this._contextMap.get(key); +- } +- +- deleteValue(key: symbol): TracingContext { +- const newContext = new TracingContextImpl(this); +- newContext._contextMap.delete(key); +- return newContext; +- } +-} +- +-/** +- * Represents a set of items that can be set when creating a new {@link TracingContext}. +- */ +-export interface CreateTracingContextOptions { +- /** The {@link parentContext} - the newly created context will contain all the values of the parent context unless overridden. */ +- parentContext?: TracingContext; +- /** An initial span to set on the context. */ +- span?: TracingSpan; +- /** The namespace to set on any child spans. */ +- namespace?: string; +-} diff --git a/src/util/aborterUtils.ts b/src/util/aborterUtils.ts index ce29be9..cde2d52 100644 --- a/src/util/aborterUtils.ts diff --git a/sdk/core/ts-http-runtime/review/ts-http-runtime.api.md b/sdk/core/ts-http-runtime/review/ts-http-runtime.api.md index 491e8ec062ac..1503c4bf6291 100644 --- a/sdk/core/ts-http-runtime/review/ts-http-runtime.api.md +++ b/sdk/core/ts-http-runtime/review/ts-http-runtime.api.md @@ -4,22 +4,6 @@ ```ts -// @public -export type AbortablePromiseBuilder = (abortOptions: { - abortSignal?: AbortSignalLike; -}) => Promise; - -// @public -export class AbortError extends Error { - constructor(message?: string); -} - -// @public -export interface AbortOptions { - abortErrorMsg?: string; - abortSignal?: AbortSignalLike; -} - // @public export interface AbortSignalLike { readonly aborted: boolean; @@ -34,21 +18,6 @@ export interface AccessToken { token: string; } -// @public -export function addCredentialPipelinePolicy(pipeline: Pipeline, endpoint: string, options?: AddCredentialPipelinePolicyOptions): void; - -// @public -export interface AddCredentialPipelinePolicyOptions { - clientOptions?: ClientOptions; - credential?: TokenCredential | KeyCredential; -} - -// @public -export interface AddEventOptions { - attributes?: Record; - startTime?: Date; -} - // @public export interface AdditionalPolicyConfig { policy: PipelinePolicy; @@ -56,7 +25,7 @@ export interface AdditionalPolicyConfig { } // @public -export interface AddPipelineOptions { +export interface AddPolicyOptions { afterPhase?: PipelinePhase; afterPolicies?: string[]; beforePolicies?: string[]; @@ -72,62 +41,12 @@ export interface Agent { sockets: unknown; } -// @public -export interface AuthorizeRequestOnChallengeOptions { - getAccessToken: (scopes: string[], options: GetTokenOptions) => Promise; - logger?: TypeSpecRuntimeLogger; - request: PipelineRequest; - response: PipelineResponse; - scopes: string[]; -} - -// @public -export interface AuthorizeRequestOptions { - getAccessToken: (scopes: string[], options: GetTokenOptions) => Promise; - logger?: TypeSpecRuntimeLogger; - request: PipelineRequest; - scopes: string[]; -} - -// @public -export function bearerTokenAuthenticationPolicy(options: BearerTokenAuthenticationPolicyOptions): PipelinePolicy; - -// @public -export const bearerTokenAuthenticationPolicyName = "bearerTokenAuthenticationPolicy"; - -// @public -export interface BearerTokenAuthenticationPolicyOptions { - challengeCallbacks?: ChallengeCallbacks; - credential?: TokenCredential; - logger?: TypeSpecRuntimeLogger; - scopes: string | string[]; -} - // @public export interface BodyPart { body: ((() => ReadableStream) | (() => NodeJS.ReadableStream)) | ReadableStream | NodeJS.ReadableStream | Uint8Array | Blob; headers: HttpHeaders; } -// @public -export function calculateRetryDelay(retryAttempt: number, config: { - retryDelayInMs: number; - maxRetryDelayInMs: number; -}): { - retryAfterInMs: number; -}; - -// @public -export function cancelablePromiseRace(abortablePromiseBuilders: AbortablePromiseBuilder[], options?: { - abortSignal?: AbortSignalLike; -}): Promise; - -// @public -export interface ChallengeCallbacks { - authorizeRequest?(options: AuthorizeRequestOptions): Promise; - authorizeRequestOnChallenge?(options: AuthorizeRequestOnChallengeOptions): Promise; -} - // @public export interface Client { path: Function; @@ -146,65 +65,18 @@ export type ClientOptions = PipelineOptions & { allowInsecureConnection?: boolean; additionalPolicies?: AdditionalPolicyConfig[]; httpClient?: HttpClient; - loggingOptions?: LogPolicyOptions; + loggingOptions?: LogOptions; }; // @public -export function computeSha256Hash(content: string, encoding: "base64" | "hex"): Promise; - -// @public -export function computeSha256Hmac(key: string, stringToSign: string, encoding: "base64" | "hex"): Promise; - -// @public -export function createAbortablePromise(buildPromise: (resolve: (value: T | PromiseLike) => void, reject: (reason?: any) => void) => void, options?: CreateAbortablePromiseOptions): Promise; - -// @public -export interface CreateAbortablePromiseOptions extends AbortOptions { - cleanupBeforeAbort?: () => void; -} - -// @public -export function createDefaultHttpClient(): HttpClient; - -// @public -export function createEmptyPipeline(): Pipeline; - -// @public -export function createFile(content: Uint8Array, name: string, options?: CreateFileOptions): File; - -// @public -export function createFileFromStream(stream: () => ReadableStream | NodeJS.ReadableStream, name: string, options?: CreateFileFromStreamOptions): File; - -// @public -export interface CreateFileFromStreamOptions extends CreateFileOptions { - size?: number; -} - -// @public -export interface CreateFileOptions { - lastModified?: number; - type?: string; - webkitRelativePath?: string; -} +export function createClientLogger(namespace: string): TypeSpecRuntimeLogger; // @public export function createHttpHeaders(rawHeaders?: RawHttpHeadersInput): HttpHeaders; -// @public -export function createPipelineFromOptions(options: InternalPipelineOptions): Pipeline; - // @public export function createPipelineRequest(options: PipelineRequestOptions): PipelineRequest; -// @public -export function createRestError(response: PathUncheckedResponse): RestError; - -// @public -export function createRestError(message: string, response: PathUncheckedResponse): RestError; - -// @public -export function createTracingClient(options: TracingClientOptions): TracingClient; - // @public export interface Debugger { (...args: any[]): void; @@ -215,55 +87,14 @@ export interface Debugger { namespace: string; } -// @public -export function decompressResponsePolicy(): PipelinePolicy; - -// @public -export const decompressResponsePolicyName = "decompressResponsePolicy"; - -// @public -export function defaultRetryPolicy(options?: DefaultRetryPolicyOptions): PipelinePolicy; - -// @public -export interface DefaultRetryPolicyOptions extends PipelineRetryOptions { -} - -// @public -export function delay(timeInMs: number, options?: DelayOptions_2): Promise; - -// @public -interface DelayOptions_2 extends AbortOptions { -} -export { DelayOptions_2 as DelayOptions } - // @public export type EncodingType = "utf-8" | "base64" | "base64url" | "hex"; -// @public -export interface ErrorModel { - code: string; - details: Array; - innererror?: InnerError; - message: string; - target?: string; -} - -// @public -export interface ErrorResponse { - error: ErrorModel; -} - // @public export type FormDataMap = { [key: string]: FormDataValue | FormDataValue[]; }; -// @public -export function formDataPolicy(): PipelinePolicy; - -// @public -export const formDataPolicyName = "formDataPolicy"; - // @public export type FormDataValue = string | Blob | File; @@ -280,15 +111,6 @@ export function getClient(endpoint: string, options?: ClientOptions): Client; // @public export function getClient(endpoint: string, credentials?: TokenCredential | KeyCredential, options?: ClientOptions): Client; -// @public @deprecated -export function getDefaultProxySettings(proxyUrl?: string): ProxySettings | undefined; - -// @public -export function getErrorMessage(e: unknown): string; - -// @public -export function getRandomIntegerInclusive(min: number, max: number): number; - // @public export interface GetTokenOptions { abortSignal?: AbortSignalLike; @@ -298,9 +120,6 @@ export interface GetTokenOptions { timeout?: number; }; tenantId?: string; - tracingOptions?: { - tracingContext?: TracingContext; - }; } // @public @@ -340,76 +159,14 @@ export type HttpResponse = { status: string; }; -// @public -export interface InnerError { - code: string; - innererror?: InnerError; -} - -// @public -export interface Instrumenter { - createRequestHeaders(tracingContext?: TracingContext): Record; - parseTraceparentHeader(traceparentHeader: string): TracingContext | undefined; - startSpan(name: string, spanOptions: InstrumenterSpanOptions): { - span: TracingSpan; - tracingContext: TracingContext; - }; - withContext ReturnType>(context: TracingContext, callback: Callback, ...callbackArgs: CallbackArgs): ReturnType; -} - -// @public -export interface InstrumenterSpanOptions extends TracingSpanOptions { - packageName: string; - packageVersion?: string; - tracingContext?: TracingContext; -} - -// @public -export interface InternalPipelineOptions extends PipelineOptions { - loggingOptions?: LogPolicyOptions; -} - -// @public -export const isBrowser: boolean; - -// @public -export const isBun: boolean; - -// @public -export function isDefined(thing: T | undefined | null): thing is T; - -// @public -export const isDeno: boolean; - -// @public -export function isError(e: unknown): e is Error; - // @public export function isKeyCredential(credential: unknown): credential is KeyCredential; -// @public @deprecated -export const isNode: boolean; - -// @public -export const isNodeLike: boolean; - -// @public -export const isNodeRuntime: boolean; - -// @public -export function isObject(input: unknown): input is UnknownObject; - -// @public -export function isObjectWithProperties(thing: Thing, properties: PropertyName[]): thing is Thing & Record; - -// @public -export const isReactNative: boolean; - // @public export function isRestError(e: unknown): e is RestError; // @public -export const isWebWorker: boolean; +export function isTokenCredential(credential: unknown): credential is TokenCredential; // @public export interface KeyCredential { @@ -423,44 +180,25 @@ export interface KeyObject { } // @public -export function logPolicy(options?: LogPolicyOptions): PipelinePolicy; - -// @public -export const logPolicyName = "logPolicy"; - -// @public -export interface LogPolicyOptions { +export interface LogOptions { additionalAllowedHeaderNames?: string[]; additionalAllowedQueryParameters?: string[]; logger?: Debugger; } -// @public -export function multipartPolicy(): PipelinePolicy; - -// @public -export const multipartPolicyName = "multipartPolicy"; - // @public export interface MultipartRequestBody { boundary?: string; parts: BodyPart[]; } -// @public -export function objectHasProperty(thing: Thing, property: PropertyName): thing is Thing & Record; - // @public export interface OperationOptions { abortSignal?: AbortSignalLike; onResponse?: RawResponseCallback; requestOptions?: OperationRequestOptions; - tracingOptions?: OperationTracingOptions; } -// @public -export function operationOptionsToRequestParameters(options: OperationOptions): RequestParameters; - // @public export interface OperationRequestOptions { allowInsecureConnection?: boolean; @@ -471,20 +209,6 @@ export interface OperationRequestOptions { timeout?: number; } -// @public -export interface OperationTracingOptions { - tracingContext?: TracingContext; -} - -// @public -export type OptionsWithTracingContext = Options & { - tracingOptions: { - tracingContext: TracingContext; - }; -}; - // @public export type PathParameters = TRoute extends `${infer _Head}/{${infer _Param}}${infer Tail}` ? [ pathParameter: string | number | PathParameterWithOptions, @@ -508,7 +232,7 @@ export type PathUncheckedResponse = HttpResponse & { // @public export interface Pipeline { - addPolicy(policy: PipelinePolicy, options?: AddPipelineOptions): void; + addPolicy(policy: PipelinePolicy, options?: AddPolicyOptions): void; clone(): Pipeline; getOrderedPolicies(): PipelinePolicy[]; removePolicy(options: { @@ -521,11 +245,11 @@ export interface Pipeline { // @public export interface PipelineOptions { proxyOptions?: ProxySettings; - redirectOptions?: RedirectPolicyOptions; + redirectOptions?: RedirectOptions; retryOptions?: PipelineRetryOptions; telemetryOptions?: TelemetryOptions; tlsOptions?: TlsSettings; - userAgentOptions?: UserAgentPolicyOptions; + userAgentOptions?: UserAgentOptions; } // @public @@ -556,7 +280,6 @@ export interface PipelineRequest { streamResponseStatusCodes?: Set; timeout: number; tlsSettings?: TlsSettings; - tracingOptions?: OperationTracingOptions; url: string; withCredentials: boolean; } @@ -578,7 +301,6 @@ export interface PipelineRequestOptions { requestId?: string; streamResponseStatusCodes?: Set; timeout?: number; - tracingOptions?: OperationTracingOptions; url: string; withCredentials?: boolean; } @@ -601,14 +323,6 @@ export interface PipelineRetryOptions { retryDelayInMs?: number; } -// @public -export function proxyPolicy(proxySettings?: ProxySettings, options?: { - customNoProxyList?: string[]; -}): PipelinePolicy; - -// @public -export const proxyPolicyName = "proxyPolicy"; - // @public export interface ProxySettings { host: string; @@ -623,9 +337,6 @@ export interface PxfObject { passphrase?: string | undefined; } -// @public -export function randomUUID(): string; - // @public export type RawHttpHeaders = { [headerName: string]: string; @@ -638,13 +349,7 @@ export type RawHttpHeadersInput = Record; export type RawResponseCallback = (rawResponse: FullOperationResponse, error?: unknown) => void; // @public -export function redirectPolicy(options?: RedirectPolicyOptions): PipelinePolicy; - -// @public -export const redirectPolicyName = "redirectPolicy"; - -// @public -export interface RedirectPolicyOptions { +export interface RedirectOptions { maxRetries?: number; } @@ -665,15 +370,9 @@ export type RequestParameters = { onUploadProgress?: (progress: TransferProgressEvent) => void; onDownloadProgress?: (progress: TransferProgressEvent) => void; abortSignal?: AbortSignalLike; - tracingOptions?: OperationTracingOptions; onResponse?: RawResponseCallback; }; -// @public -export type Resolved = T extends { - then(onfulfilled: infer F): any; -} ? F extends (value: infer V) => any ? Resolved : never : T; - // @public export interface ResourceMethods> { delete: (options?: RequestParameters) => TResponse; @@ -709,20 +408,6 @@ export interface RestErrorOptions { // @public export type SendRequest = (request: PipelineRequest) => Promise; -// @public -export type SpanStatus = SpanStatusSuccess | SpanStatusError; - -// @public -export type SpanStatusError = { - status: "error"; - error?: Error | string; -}; - -// @public -export type SpanStatusSuccess = { - status: "success"; -}; - // @public export type StreamableMethod = PromiseLike & { asNodeStream: () => Promise; @@ -737,12 +422,6 @@ export interface TelemetryOptions { clientRequestIdHeaderName?: string; } -// @public -export function tlsPolicy(tlsSettings?: TlsSettings): PipelinePolicy; - -// @public -export const tlsPolicyName = "tlsPolicy"; - // @public export interface TlsSettings { ca?: string | Buffer | Array | undefined; @@ -757,78 +436,6 @@ export interface TokenCredential { getToken(scopes: string | string[], options?: GetTokenOptions): Promise; } -// @public -export interface TracingClient { - createRequestHeaders(tracingContext?: TracingContext): Record; - parseTraceparentHeader(traceparentHeader: string): TracingContext | undefined; - startSpan(name: string, operationOptions?: Options, spanOptions?: TracingSpanOptions): { - span: TracingSpan; - updatedOptions: OptionsWithTracingContext; - }; - withContext ReturnType>(context: TracingContext, callback: Callback, ...callbackArgs: CallbackArgs): ReturnType; - withSpan) => ReturnType>(name: string, operationOptions: Options, callback: Callback, spanOptions?: TracingSpanOptions): Promise>>; -} - -// @public -export interface TracingClientOptions { - namespace: string; - packageName: string; - packageVersion?: string; -} - -// @public -export interface TracingContext { - deleteValue(key: symbol): TracingContext; - getValue(key: symbol): unknown; - setValue(key: symbol, value: unknown): TracingContext; -} - -// @public -export function tracingPolicy(options?: TracingPolicyOptions): PipelinePolicy; - -// @public -export const tracingPolicyName = "tracingPolicy"; - -// @public -export interface TracingPolicyOptions { - additionalAllowedQueryParameters?: string[]; - userAgentPrefix?: string; -} - -// @public -export interface TracingSpan { - addEvent?(name: string, options?: AddEventOptions): void; - end(): void; - isRecording(): boolean; - recordException(exception: Error | string): void; - setAttribute(name: string, value: unknown): void; - setStatus(status: SpanStatus): void; -} - -// @public -export type TracingSpanKind = "client" | "server" | "producer" | "consumer" | "internal"; - -// @public -export interface TracingSpanLink { - attributes?: { - [key: string]: unknown; - }; - tracingContext: TracingContext; -} - -// @public -export interface TracingSpanOptions { - spanAttributes?: { - [key: string]: unknown; - }; - spanKind?: TracingSpanKind; - spanLinks?: TracingSpanLink[]; -} - // @public export type TransferProgressEvent = { loadedBytes: number; @@ -848,28 +455,11 @@ export interface TypeSpecRuntimeLogger { warning: Debugger; } -// @public -export type TypeSpecRuntimeLogLevel = "verbose" | "info" | "warning" | "error"; - // @public export function uint8ArrayToString(bytes: Uint8Array, format: EncodingType): string; // @public -export type UnknownObject = { - [s: string]: unknown; -}; - -// @public -export function useInstrumenter(instrumenter: Instrumenter): void; - -// @public -export function userAgentPolicy(options?: UserAgentPolicyOptions): PipelinePolicy; - -// @public -export const userAgentPolicyName = "userAgentPolicy"; - -// @public -export interface UserAgentPolicyOptions { +export interface UserAgentOptions { userAgentPrefix?: string; } diff --git a/sdk/core/ts-http-runtime/src/auth/tokenCredential.ts b/sdk/core/ts-http-runtime/src/auth/tokenCredential.ts index 6a42777aeb4a..19d080b4e97f 100644 --- a/sdk/core/ts-http-runtime/src/auth/tokenCredential.ts +++ b/sdk/core/ts-http-runtime/src/auth/tokenCredential.ts @@ -2,7 +2,6 @@ // Licensed under the MIT License. import type { AbortSignalLike } from "../abort-controller/AbortSignalLike.js"; -import type { TracingContext } from "../tracing/interfaces.js"; /** * Represents a credential capable of providing an authentication token. @@ -38,15 +37,6 @@ export interface GetTokenOptions { */ timeout?: number; }; - /** - * Options used when tracing is enabled. - */ - tracingOptions?: { - /** - * Tracing Context for the current request. - */ - tracingContext?: TracingContext; - }; /** * Claim details to perform the Continuous Access Evaluation authentication flow */ diff --git a/sdk/core/ts-http-runtime/src/client/common.ts b/sdk/core/ts-http-runtime/src/client/common.ts index edda1c6998cc..5d588ed80982 100644 --- a/sdk/core/ts-http-runtime/src/client/common.ts +++ b/sdk/core/ts-http-runtime/src/client/common.ts @@ -12,7 +12,6 @@ import type { } from "../interfaces.js"; import type { Pipeline, PipelinePolicy } from "../pipeline.js"; import type { AbortSignalLike } from "../abort-controller/AbortSignalLike.js"; -import type { OperationTracingOptions } from "../tracing/interfaces.js"; import type { PipelineOptions } from "../createPipelineFromOptions.js"; import type { LogPolicyOptions } from "../policies/logPolicy.js"; @@ -73,11 +72,6 @@ export type RequestParameters = { */ abortSignal?: AbortSignalLike; - /** - * Options used when tracing is enabled. - */ - tracingOptions?: OperationTracingOptions; - /** * A function to be called each time a response is received from the server * while performing the requested operation. @@ -127,11 +121,6 @@ export interface OperationOptions { * Options used when creating and sending HTTP requests for this operation. */ requestOptions?: OperationRequestOptions; - /** - * Options used when tracing is enabled. - */ - tracingOptions?: OperationTracingOptions; - /** * A function to be called each time a response is received from the server * while performing the requested operation. diff --git a/sdk/core/ts-http-runtime/src/client/operationOptionHelpers.ts b/sdk/core/ts-http-runtime/src/client/operationOptionHelpers.ts index c7c1eb625ada..15bdc452b5f6 100644 --- a/sdk/core/ts-http-runtime/src/client/operationOptionHelpers.ts +++ b/sdk/core/ts-http-runtime/src/client/operationOptionHelpers.ts @@ -16,7 +16,6 @@ export function operationOptionsToRequestParameters(options: OperationOptions): abortSignal: options.abortSignal, onUploadProgress: options.requestOptions?.onUploadProgress, onDownloadProgress: options.requestOptions?.onDownloadProgress, - tracingOptions: options.tracingOptions, headers: { ...options.requestOptions?.headers }, onResponse: options.onResponse, }; diff --git a/sdk/core/ts-http-runtime/src/client/sendRequest.ts b/sdk/core/ts-http-runtime/src/client/sendRequest.ts index 2f30ba080626..f2a1cacc80bf 100644 --- a/sdk/core/ts-http-runtime/src/client/sendRequest.ts +++ b/sdk/core/ts-http-runtime/src/client/sendRequest.ts @@ -134,7 +134,6 @@ function buildPipelineRequest( multipartBody, headers, allowInsecureConnection: options.allowInsecureConnection, - tracingOptions: options.tracingOptions, abortSignal: options.abortSignal, onUploadProgress: options.onUploadProgress, onDownloadProgress: options.onDownloadProgress, diff --git a/sdk/core/ts-http-runtime/src/createPipelineFromOptions.ts b/sdk/core/ts-http-runtime/src/createPipelineFromOptions.ts index ede8855e0df5..6066e077119d 100644 --- a/sdk/core/ts-http-runtime/src/createPipelineFromOptions.ts +++ b/sdk/core/ts-http-runtime/src/createPipelineFromOptions.ts @@ -3,17 +3,15 @@ import { type LogPolicyOptions, logPolicy } from "./policies/logPolicy.js"; import { type Pipeline, createEmptyPipeline } from "./pipeline.js"; -import type { PipelineRetryOptions, TlsSettings } from "./interfaces.js"; +import type { PipelineRetryOptions, ProxySettings, TlsSettings } from "./interfaces.js"; import { type RedirectPolicyOptions, redirectPolicy } from "./policies/redirectPolicy.js"; import { type UserAgentPolicyOptions, userAgentPolicy } from "./policies/userAgentPolicy.js"; -import type { ProxySettings } from "./index.js"; import { decompressResponsePolicy } from "./policies/decompressResponsePolicy.js"; import { defaultRetryPolicy } from "./policies/defaultRetryPolicy.js"; import { formDataPolicy } from "./policies/formDataPolicy.js"; import { isNodeLike } from "./util/checkEnvironment.js"; import { proxyPolicy } from "./policies/proxyPolicy.js"; import { tlsPolicy } from "./policies/tlsPolicy.js"; -import { tracingPolicy } from "./policies/tracingPolicy.js"; import { multipartPolicy, multipartPolicyName } from "./policies/multipartPolicy.js"; /** @@ -93,9 +91,6 @@ export function createPipelineFromOptions(options: InternalPipelineOptions): Pip // properties (e.g., making the boundary constant in recorded tests). pipeline.addPolicy(multipartPolicy(), { afterPhase: "Deserialize" }); pipeline.addPolicy(defaultRetryPolicy(options.retryOptions), { phase: "Retry" }); - pipeline.addPolicy(tracingPolicy({ ...options.userAgentOptions, ...options.loggingOptions }), { - afterPhase: "Retry", - }); if (isNodeLike) { // Both XHR and Fetch expect to handle redirects automatically, // so only include this policy when we're in Node. diff --git a/sdk/core/ts-http-runtime/src/index.ts b/sdk/core/ts-http-runtime/src/index.ts index 76ca028579cf..9cda6b3c29fe 100644 --- a/sdk/core/ts-http-runtime/src/index.ts +++ b/sdk/core/ts-http-runtime/src/index.ts @@ -11,148 +11,68 @@ declare global { } /* eslint-enable @typescript-eslint/no-unused-vars */ +export { AbortSignalLike } from "./abort-controller/AbortSignalLike.js"; export { - Agent, + createClientLogger, + TypeSpecRuntimeLogger, + type TypeSpecRuntimeClientLogger, + type Debugger, +} from "./logger/logger.js"; +export type { BodyPart, - FormDataMap, FormDataValue, - HttpClient, - HttpHeaders, - HttpMethods, + RawHttpHeaders, KeyObject, - MultipartRequestBody, + PxfObject, + HttpClient, PipelineRequest, PipelineResponse, - PipelineRetryOptions, - ProxySettings, - PxfObject, - RawHttpHeaders, - RawHttpHeadersInput, - RequestBodyType, SendRequest, TlsSettings, + Agent, + RequestBodyType, + FormDataMap, + HttpHeaders, + HttpMethods, + MultipartRequestBody, TransferProgressEvent, + ProxySettings, + RawHttpHeadersInput, + PipelineRetryOptions, } from "./interfaces.js"; -export { - AddPolicyOptions as AddPipelineOptions, - PipelinePhase, - PipelinePolicy, - Pipeline, - createEmptyPipeline, -} from "./pipeline.js"; -export { - createPipelineFromOptions, - TelemetryOptions, - InternalPipelineOptions, - PipelineOptions, -} from "./createPipelineFromOptions.js"; -export { createDefaultHttpClient } from "./defaultHttpClient.js"; export { createHttpHeaders } from "./httpHeaders.js"; -export { createPipelineRequest, PipelineRequestOptions } from "./pipelineRequest.js"; -export { RestError, RestErrorOptions, isRestError } from "./restError.js"; -export { - decompressResponsePolicy, - decompressResponsePolicyName, -} from "./policies/decompressResponsePolicy.js"; -export { logPolicy, logPolicyName, LogPolicyOptions } from "./policies/logPolicy.js"; -export { multipartPolicy, multipartPolicyName } from "./policies/multipartPolicy.js"; -export { proxyPolicy, proxyPolicyName, getDefaultProxySettings } from "./policies/proxyPolicy.js"; -export { - redirectPolicy, - redirectPolicyName, - RedirectPolicyOptions, -} from "./policies/redirectPolicy.js"; -export { - tracingPolicy, - tracingPolicyName, - TracingPolicyOptions, -} from "./policies/tracingPolicy.js"; -export { defaultRetryPolicy, DefaultRetryPolicyOptions } from "./policies/defaultRetryPolicy.js"; -export { - userAgentPolicy, - userAgentPolicyName, - UserAgentPolicyOptions, -} from "./policies/userAgentPolicy.js"; -export { tlsPolicy, tlsPolicyName } from "./policies/tlsPolicy.js"; -export { formDataPolicy, formDataPolicyName } from "./policies/formDataPolicy.js"; -export { - bearerTokenAuthenticationPolicy, - BearerTokenAuthenticationPolicyOptions, - bearerTokenAuthenticationPolicyName, - ChallengeCallbacks, - AuthorizeRequestOptions, - AuthorizeRequestOnChallengeOptions, -} from "./policies/bearerTokenAuthenticationPolicy.js"; -export { AbortSignalLike } from "./abort-controller/AbortSignalLike.js"; -export { AbortError } from "./abort-controller/AbortError.js"; -export { AccessToken, GetTokenOptions, TokenCredential } from "./auth/tokenCredential.js"; -export { KeyCredential, isKeyCredential } from "./auth/keyCredential.js"; -export { - AddEventOptions, - Instrumenter, - InstrumenterSpanOptions, - OperationTracingOptions, - OptionsWithTracingContext, - Resolved, - SpanStatus, - SpanStatusError, - SpanStatusSuccess, - TracingClient, - TracingClientOptions, - TracingContext, - TracingSpan, - TracingSpanKind, - TracingSpanLink, - TracingSpanOptions, -} from "./tracing/interfaces.js"; -export { useInstrumenter } from "./tracing/instrumenter.js"; -export { createTracingClient } from "./tracing/tracingClient.js"; -// from core-util -export { delay, DelayOptions, calculateRetryDelay } from "./util/delay.js"; -export { - AbortOptions, - cancelablePromiseRace, - AbortablePromiseBuilder, -} from "./util/aborterUtils.js"; -export { - createAbortablePromise, - CreateAbortablePromiseOptions, -} from "./util/createAbortablePromise.js"; -export { getRandomIntegerInclusive } from "./util/random.js"; -export { isObject, UnknownObject } from "./util/object.js"; -export { isError, getErrorMessage } from "./util/error.js"; -export { - createFile, - createFileFromStream, - CreateFileOptions, - CreateFileFromStreamOptions, -} from "./util/file.js"; -export { computeSha256Hash, computeSha256Hmac } from "./util/sha256.js"; -export { isDefined, isObjectWithProperties, objectHasProperty } from "./util/typeGuards.js"; -export { randomUUID } from "./util/uuidUtils.js"; -export { - isBrowser, - isBun, - isNode, - isNodeLike, - isNodeRuntime, - isDeno, - isReactNative, - isWebWorker, -} from "./util/checkEnvironment.js"; -export { uint8ArrayToString, stringToUint8Array, EncodingType } from "./util/bytesEncoding.js"; -export { - Debugger, - TypeSpecRuntimeLogger, - TypeSpecRuntimeLogLevel, - TypeSpecRuntimeClientLogger, -} from "./logger/logger.js"; -// client -export { createRestError } from "./client/restError.js"; +export { isKeyCredential, type KeyCredential } from "./auth/keyCredential.js"; export { - addCredentialPipelinePolicy, - AddCredentialPipelinePolicyOptions, -} from "./client/clientHelpers.js"; -export { operationOptionsToRequestParameters } from "./client/operationOptionHelpers.js"; -export * from "./client/getClient.js"; -export * from "./client/common.js"; + isTokenCredential, + type TokenCredential, + type GetTokenOptions, + type AccessToken, +} from "./auth/tokenCredential.js"; +export { createPipelineRequest, type PipelineRequestOptions } from "./pipelineRequest.js"; +export type { Pipeline, PipelinePolicy, AddPolicyOptions, PipelinePhase } from "./pipeline.js"; +export { RestError, isRestError, type RestErrorOptions } from "./restError.js"; +export { stringToUint8Array, uint8ArrayToString, type EncodingType } from "./util/bytesEncoding.js"; +export { getClient } from "./client/getClient.js"; +export type { + Client, + ClientOptions, + OperationOptions, + AdditionalPolicyConfig, + PathUnchecked, + PathUncheckedResponse, + HttpResponse, + RawResponseCallback, + OperationRequestOptions, + PathParameters, + ResourceMethods, + PathParameterWithOptions, + StreamableMethod, + RequestParameters, + HttpNodeStreamResponse, + HttpBrowserStreamResponse, + FullOperationResponse, +} from "./client/common.js"; +export type { PipelineOptions, TelemetryOptions } from "./createPipelineFromOptions.js"; +export type { LogPolicyOptions as LogOptions } from "./policies/logPolicy.js"; +export type { RedirectPolicyOptions as RedirectOptions } from "./policies/redirectPolicy.js"; +export type { UserAgentPolicyOptions as UserAgentOptions } from "./policies/userAgentPolicy.js"; diff --git a/sdk/core/ts-http-runtime/src/interfaces.ts b/sdk/core/ts-http-runtime/src/interfaces.ts index 3e3ca58e9d6a..5d790adc5764 100644 --- a/sdk/core/ts-http-runtime/src/interfaces.ts +++ b/sdk/core/ts-http-runtime/src/interfaces.ts @@ -2,7 +2,6 @@ // Licensed under the MIT License. import type { AbortSignalLike } from "./abort-controller/AbortSignalLike.js"; -import type { OperationTracingOptions } from "./tracing/interfaces.js"; /** * A HttpHeaders collection represented as a simple JSON object. @@ -203,11 +202,6 @@ export interface PipelineRequest { */ abortSignal?: AbortSignalLike; - /** - * Tracing options to use for any created Spans. - */ - tracingOptions?: OperationTracingOptions; - /** * Callback which fires upon upload progress. */ diff --git a/sdk/core/ts-http-runtime/src/pipelineRequest.ts b/sdk/core/ts-http-runtime/src/pipelineRequest.ts index 50d7f83dc2bd..5f67f7dcd1b8 100644 --- a/sdk/core/ts-http-runtime/src/pipelineRequest.ts +++ b/sdk/core/ts-http-runtime/src/pipelineRequest.ts @@ -14,7 +14,6 @@ import type { import { createHttpHeaders } from "./httpHeaders.js"; import type { AbortSignalLike } from "./abort-controller/AbortSignalLike.js"; import { randomUUID } from "./util/uuidUtils.js"; -import type { OperationTracingOptions } from "./tracing/interfaces.js"; /** * Settings to initialize a request. @@ -100,11 +99,6 @@ export interface PipelineRequestOptions { */ abortSignal?: AbortSignalLike; - /** - * Options used to create a span when tracing is enabled. - */ - tracingOptions?: OperationTracingOptions; - /** * Callback which fires upon upload progress. */ @@ -133,7 +127,6 @@ class PipelineRequestImpl implements PipelineRequest { public disableKeepAlive: boolean; public abortSignal?: AbortSignalLike; public requestId: string; - public tracingOptions?: OperationTracingOptions; public allowInsecureConnection?: boolean; public onUploadProgress?: (progress: TransferProgressEvent) => void; public onDownloadProgress?: (progress: TransferProgressEvent) => void; @@ -151,7 +144,6 @@ class PipelineRequestImpl implements PipelineRequest { this.streamResponseStatusCodes = options.streamResponseStatusCodes; this.withCredentials = options.withCredentials ?? false; this.abortSignal = options.abortSignal; - this.tracingOptions = options.tracingOptions; this.onUploadProgress = options.onUploadProgress; this.onDownloadProgress = options.onDownloadProgress; this.requestId = options.requestId || randomUUID(); diff --git a/sdk/core/ts-http-runtime/src/policies/bearerTokenAuthenticationPolicy.ts b/sdk/core/ts-http-runtime/src/policies/bearerTokenAuthenticationPolicy.ts index 87e8f5e705d3..1cd3978b3a3a 100644 --- a/sdk/core/ts-http-runtime/src/policies/bearerTokenAuthenticationPolicy.ts +++ b/sdk/core/ts-http-runtime/src/policies/bearerTokenAuthenticationPolicy.ts @@ -109,7 +109,6 @@ async function defaultAuthorizeRequest(options: AuthorizeRequestOptions): Promis const { scopes, getAccessToken, request } = options; const getTokenOptions: GetTokenOptions = { abortSignal: request.abortSignal, - tracingOptions: request.tracingOptions, }; const accessToken = await getAccessToken(scopes, getTokenOptions); diff --git a/sdk/core/ts-http-runtime/src/policies/tracingPolicy.ts b/sdk/core/ts-http-runtime/src/policies/tracingPolicy.ts deleted file mode 100644 index 34fad8948451..000000000000 --- a/sdk/core/ts-http-runtime/src/policies/tracingPolicy.ts +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -import type { TracingClient, TracingContext, TracingSpan } from "../tracing/interfaces.js"; -import { createTracingClient } from "../tracing/tracingClient.js"; -import { SDK_VERSION } from "../constants.js"; -import type { PipelineRequest, PipelineResponse, SendRequest } from "../interfaces.js"; -import type { PipelinePolicy } from "../pipeline.js"; -import { getUserAgentValue } from "../util/userAgent.js"; -import { logger } from "../log.js"; -import { getErrorMessage, isError } from "../util/error.js"; -import { isRestError } from "../restError.js"; -import { Sanitizer } from "../util/sanitizer.js"; - -/** - * The programmatic identifier of the tracingPolicy. - */ -export const tracingPolicyName = "tracingPolicy"; - -/** - * Options to configure the tracing policy. - */ -export interface TracingPolicyOptions { - /** - * String prefix to add to the user agent logged as metadata - * on the generated Span. - * Defaults to an empty string. - */ - userAgentPrefix?: string; - /** - * Query string names whose values will be logged when logging is enabled. By default no - * query string values are logged. - */ - additionalAllowedQueryParameters?: string[]; -} - -/** - * A simple policy to create OpenTelemetry Spans for each request made by the pipeline - * that has SpanOptions with a parent. - * Requests made without a parent Span will not be recorded. - * @param options - Options to configure the telemetry logged by the tracing policy. - */ -export function tracingPolicy(options: TracingPolicyOptions = {}): PipelinePolicy { - const userAgentPromise = getUserAgentValue(options.userAgentPrefix); - const sanitizer = new Sanitizer({ - additionalAllowedQueryParameters: options.additionalAllowedQueryParameters, - }); - const tracingClient = tryCreateTracingClient(); - - return { - name: tracingPolicyName, - async sendRequest(request: PipelineRequest, next: SendRequest): Promise { - if (!tracingClient) { - return next(request); - } - - const userAgent = await userAgentPromise; - - const spanAttributes = { - "http.url": sanitizer.sanitizeUrl(request.url), - "http.method": request.method, - "http.user_agent": userAgent, - requestId: request.requestId, - }; - if (userAgent) { - spanAttributes["http.user_agent"] = userAgent; - } - - const { span, tracingContext } = tryCreateSpan(tracingClient, request, spanAttributes) ?? {}; - - if (!span || !tracingContext) { - return next(request); - } - - try { - const response = await tracingClient.withContext(tracingContext, next, request); - tryProcessResponse(span, response); - return response; - } catch (err: any) { - tryProcessError(span, err); - throw err; - } - }, - }; -} - -function tryCreateTracingClient(): TracingClient | undefined { - try { - return createTracingClient({ - namespace: "", - packageName: "@typespec/ts-http-runtime", - packageVersion: SDK_VERSION, - }); - } catch (e: unknown) { - logger.warning(`Error when creating the TracingClient: ${getErrorMessage(e)}`); - return undefined; - } -} - -function tryCreateSpan( - tracingClient: TracingClient, - request: PipelineRequest, - spanAttributes: Record, -): { span: TracingSpan; tracingContext: TracingContext } | undefined { - try { - // As per spec, we do not need to differentiate between HTTP and HTTPS in span name. - const { span, updatedOptions } = tracingClient.startSpan( - `HTTP ${request.method}`, - { tracingOptions: request.tracingOptions }, - { - spanKind: "client", - spanAttributes, - }, - ); - - // If the span is not recording, don't do any more work. - if (!span.isRecording()) { - span.end(); - return undefined; - } - - // set headers - const headers = tracingClient.createRequestHeaders( - updatedOptions.tracingOptions.tracingContext, - ); - for (const [key, value] of Object.entries(headers)) { - request.headers.set(key, value); - } - return { span, tracingContext: updatedOptions.tracingOptions.tracingContext }; - } catch (e: any) { - logger.warning(`Skipping creating a tracing span due to an error: ${getErrorMessage(e)}`); - return undefined; - } -} - -function tryProcessError(span: TracingSpan, error: unknown): void { - try { - span.setStatus({ - status: "error", - error: isError(error) ? error : undefined, - }); - if (isRestError(error) && error.statusCode) { - span.setAttribute("http.status_code", error.statusCode); - } - span.end(); - } catch (e: any) { - logger.warning(`Skipping tracing span processing due to an error: ${getErrorMessage(e)}`); - } -} - -function tryProcessResponse(span: TracingSpan, response: PipelineResponse): void { - try { - span.setAttribute("http.status_code", response.status); - const serviceRequestId = response.headers.get("x-ms-request-id"); - if (serviceRequestId) { - span.setAttribute("serviceRequestId", serviceRequestId); - } - span.setStatus({ - status: "success", - }); - span.end(); - } catch (e: any) { - logger.warning(`Skipping tracing span processing due to an error: ${getErrorMessage(e)}`); - } -} diff --git a/sdk/core/ts-http-runtime/src/retryStrategies/throttlingRetryStrategy.ts b/sdk/core/ts-http-runtime/src/retryStrategies/throttlingRetryStrategy.ts index 5c8d4ed7232b..d0f729855056 100644 --- a/sdk/core/ts-http-runtime/src/retryStrategies/throttlingRetryStrategy.ts +++ b/sdk/core/ts-http-runtime/src/retryStrategies/throttlingRetryStrategy.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import type { PipelineResponse } from "../index.js"; +import type { PipelineResponse } from "../interfaces.js"; import { parseHeaderValueAsNumber } from "../util/helpers.js"; import type { RetryStrategy } from "./retryStrategy.js"; diff --git a/sdk/core/ts-http-runtime/src/tracing/instrumenter.ts b/sdk/core/ts-http-runtime/src/tracing/instrumenter.ts deleted file mode 100644 index 28f5729dc540..000000000000 --- a/sdk/core/ts-http-runtime/src/tracing/instrumenter.ts +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -import type { - Instrumenter, - InstrumenterSpanOptions, - TracingContext, - TracingSpan, -} from "./interfaces.js"; - -import { createTracingContext } from "./tracingContext.js"; -import { state } from "./state.js"; - -export function createDefaultTracingSpan(): TracingSpan { - return { - end: () => { - // noop - }, - isRecording: () => false, - recordException: () => { - // noop - }, - setAttribute: () => { - // noop - }, - setStatus: () => { - // noop - }, - addEvent: () => { - // noop - }, - }; -} - -export function createDefaultInstrumenter(): Instrumenter { - return { - createRequestHeaders: (): Record => { - return {}; - }, - parseTraceparentHeader: (): TracingContext | undefined => { - return undefined; - }, - startSpan: ( - _name: string, - spanOptions: InstrumenterSpanOptions, - ): { span: TracingSpan; tracingContext: TracingContext } => { - return { - span: createDefaultTracingSpan(), - tracingContext: createTracingContext({ parentContext: spanOptions.tracingContext }), - }; - }, - withContext< - CallbackArgs extends unknown[], - Callback extends (...args: CallbackArgs) => ReturnType, - >( - _context: TracingContext, - callback: Callback, - ...callbackArgs: CallbackArgs - ): ReturnType { - return callback(...callbackArgs); - }, - }; -} - -/** - * Extends the Azure SDK with support for a given instrumenter implementation. - * - * @param instrumenter - The instrumenter implementation to use. - */ -export function useInstrumenter(instrumenter: Instrumenter): void { - state.instrumenterImplementation = instrumenter; -} - -/** - * Gets the currently set instrumenter, a No-Op instrumenter by default. - * - * @returns The currently set instrumenter - */ -export function getInstrumenter(): Instrumenter { - if (!state.instrumenterImplementation) { - state.instrumenterImplementation = createDefaultInstrumenter(); - } - return state.instrumenterImplementation; -} diff --git a/sdk/core/ts-http-runtime/src/tracing/interfaces.ts b/sdk/core/ts-http-runtime/src/tracing/interfaces.ts deleted file mode 100644 index fe8f87b54c01..000000000000 --- a/sdk/core/ts-http-runtime/src/tracing/interfaces.ts +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -/** - * A narrower version of TypeScript 4.5's Awaited type which Recursively - * unwraps the "awaited type", emulating the behavior of `await`. - */ -export type Resolved = T extends { then(onfulfilled: infer F): any } // `await` only unwraps object types with a callable `then`. Non-object types are not unwrapped - ? F extends (value: infer V) => any // if the argument to `then` is callable, extracts the first argument - ? Resolved // recursively unwrap the value - : never // the argument to `then` was not callable - : T; // non-object or non-thenable - -/** - * Represents a client that can integrate with the currently configured {@link Instrumenter}. - * - * Create an instance using {@link createTracingClient}. - */ -export interface TracingClient { - /** - * Wraps a callback in a tracing span, calls the callback, and closes the span. - * - * This is the primary interface for using Tracing and will handle error recording as well as setting the status on the span. - * - * Both synchronous and asynchronous functions will be awaited in order to reflect the result of the callback on the span. - * - * Example: - * - * ```ts snippet:with_span_example - * import { createTracingClient } from "@typespec/ts-http-runtime"; - * - * const tracingClient = createTracingClient({ - * namespace: "test.namespace", - * packageName: "test-package", - * packageVersion: "1.0.0", - * }); - * const options = {}; - * const myOperationResult = await tracingClient.withSpan( - * "myClassName.myOperationName", - * options, - * (updatedOptions) => { - * // Do something with the updated options. - * return "myOperationResult"; - * }, - * ); - * ``` - * @param name - The name of the span. By convention this should be `${className}.${methodName}`. - * @param operationOptions - The original options passed to the method. The callback will receive these options with the newly created {@link TracingContext}. - * @param callback - The callback to be invoked with the updated options and newly created {@link TracingSpan}. - */ - withSpan< - Options extends { tracingOptions?: OperationTracingOptions }, - Callback extends ( - updatedOptions: Options, - span: Omit, - ) => ReturnType, - >( - name: string, - operationOptions: Options, - callback: Callback, - spanOptions?: TracingSpanOptions, - ): Promise>>; - /** - * Starts a given span but does not set it as the active span. - * - * You must end the span using {@link TracingSpan.end}. - * - * Most of the time you will want to use {@link withSpan} instead. - * - * @param name - The name of the span. By convention this should be `${className}.${methodName}`. - * @param operationOptions - The original operation options. - * @param spanOptions - The options to use when creating the span. - * - * @returns A {@link TracingSpan} and the updated operation options. - */ - startSpan( - name: string, - operationOptions?: Options, - spanOptions?: TracingSpanOptions, - ): { - span: TracingSpan; - updatedOptions: OptionsWithTracingContext; - }; - /** - * Wraps a callback with an active context and calls the callback. - * Depending on the implementation, this may set the globally available active context. - * - * Useful when you want to leave the boundaries of the SDK (make a request or callback to user code) and are unable to use the {@link withSpan} API. - * - * @param context - The {@link TracingContext} to use as the active context in the scope of the callback. - * @param callback - The callback to be invoked with the given context set as the globally active context. - * @param callbackArgs - The callback arguments. - */ - withContext< - CallbackArgs extends unknown[], - Callback extends (...args: CallbackArgs) => ReturnType, - >( - context: TracingContext, - callback: Callback, - ...callbackArgs: CallbackArgs - ): ReturnType; - - /** - * Parses a traceparent header value into a {@link TracingSpanContext}. - * - * @param traceparentHeader - The traceparent header to parse. - * @returns An implementation-specific identifier for the span. - */ - parseTraceparentHeader(traceparentHeader: string): TracingContext | undefined; - - /** - * Creates a set of request headers to propagate tracing information to a backend. - * - * @param tracingContext - The context containing the span to propagate. - * @returns The set of headers to add to a request. - */ - createRequestHeaders(tracingContext?: TracingContext): Record; -} - -/** - * Options that can be passed to {@link createTracingClient} - */ -export interface TracingClientOptions { - /** The value of the az.namespace tracing attribute on newly created spans. */ - namespace: string; - /** The name of the package invoking this trace. */ - packageName: string; - /** An optional version of the package invoking this trace. */ - packageVersion?: string; -} - -/** The kind of span. */ -export type TracingSpanKind = "client" | "server" | "producer" | "consumer" | "internal"; - -/** Options used to configure the newly created span. */ -export interface TracingSpanOptions { - /** The kind of span. Implementations should default this to "client". */ - spanKind?: TracingSpanKind; - /** A collection of {@link TracingSpanLink} to link to this span. */ - spanLinks?: TracingSpanLink[]; - /** Initial set of attributes to set on a span. */ - spanAttributes?: { [key: string]: unknown }; -} - -/** A pointer from the current {@link TracingSpan} to another span in the same or a different trace. */ -export interface TracingSpanLink { - /** The {@link TracingContext} containing the span context to link to. */ - tracingContext: TracingContext; - /** A set of attributes on the link. */ - attributes?: { [key: string]: unknown }; -} - -/** - * Represents an implementation agnostic instrumenter. - */ -export interface Instrumenter { - /** - * Creates a new {@link TracingSpan} with the given name and options and sets it on a new context. - * @param name - The name of the span. By convention this should be `${className}.${methodName}`. - * @param spanOptions - The options to use when creating the span. - * - * @returns A {@link TracingSpan} that can be used to end the span, and the context this span has been set on. - */ - startSpan( - name: string, - spanOptions: InstrumenterSpanOptions, - ): { span: TracingSpan; tracingContext: TracingContext }; - /** - * Wraps a callback with an active context and calls the callback. - * Depending on the implementation, this may set the globally available active context. - * - * @param context - The {@link TracingContext} to use as the active context in the scope of the callback. - * @param callback - The callback to be invoked with the given context set as the globally active context. - * @param callbackArgs - The callback arguments. - */ - withContext< - CallbackArgs extends unknown[], - Callback extends (...args: CallbackArgs) => ReturnType, - >( - context: TracingContext, - callback: Callback, - ...callbackArgs: CallbackArgs - ): ReturnType; - - /** - * Provides an implementation-specific method to parse a {@link https://www.w3.org/TR/trace-context/#traceparent-header} - * into a {@link TracingSpanContext} which can be used to link non-parented spans together. - */ - parseTraceparentHeader(traceparentHeader: string): TracingContext | undefined; - /** - * Provides an implementation-specific method to serialize a {@link TracingSpan} to a set of headers. - * @param tracingContext - The context containing the span to serialize. - */ - createRequestHeaders(tracingContext?: TracingContext): Record; -} - -/** - * Options passed to {@link Instrumenter.startSpan} as a superset of {@link TracingSpanOptions}. - */ -export interface InstrumenterSpanOptions extends TracingSpanOptions { - /** The name of the package invoking this trace. */ - packageName: string; - /** The version of the package invoking this trace. */ - packageVersion?: string; - /** The current tracing context. Defaults to an implementation-specific "active" context. */ - tracingContext?: TracingContext; -} - -/** - * Status representing a successful operation that can be sent to {@link TracingSpan.setStatus} - */ -export type SpanStatusSuccess = { status: "success" }; - -/** - * Status representing an error that can be sent to {@link TracingSpan.setStatus} - */ -export type SpanStatusError = { status: "error"; error?: Error | string }; - -/** - * Represents the statuses that can be passed to {@link TracingSpan.setStatus}. - * - * By default, all spans will be created with status "unset". - */ -export type SpanStatus = SpanStatusSuccess | SpanStatusError; - -/** - * Represents options you can pass to {@link TracingSpan.addEvent}. - */ -export interface AddEventOptions { - /** - * A set of attributes to attach to the event. - */ - attributes?: Record; - /** - * The start time of the event. - */ - startTime?: Date; -} - -/** - * Represents an implementation agnostic tracing span. - */ -export interface TracingSpan { - /** - * Sets the status of the span. When an error is provided, it will be recorded on the span as well. - * - * @param status - The {@link SpanStatus} to set on the span. - */ - setStatus(status: SpanStatus): void; - - /** - * Sets a given attribute on a span. - * - * @param name - The attribute's name. - * @param value - The attribute's value to set. May be any non-nullish value. - */ - setAttribute(name: string, value: unknown): void; - - /** - * Ends the span. - */ - end(): void; - - /** - * Records an exception on a {@link TracingSpan} without modifying its status. - * - * When recording an unhandled exception that should fail the span, please use {@link TracingSpan.setStatus} instead. - * - * @param exception - The exception to record on the span. - * - */ - recordException(exception: Error | string): void; - - /** - * Returns true if this {@link TracingSpan} is recording information. - * - * Depending on the span implementation, this may return false if the span is not being sampled. - */ - isRecording(): boolean; - - /** - * Adds an event to the span. - */ - addEvent?(name: string, options?: AddEventOptions): void; -} - -/** An immutable context bag of tracing values for the current operation. */ -export interface TracingContext { - /** - * Sets a given object on a context. - * @param key - The key of the given context value. - * @param value - The value to set on the context. - * - * @returns - A new context with the given value set. - */ - setValue(key: symbol, value: unknown): TracingContext; - /** - * Gets an object from the context if it exists. - * @param key - The key of the given context value. - * - * @returns - The value of the given context value if it exists, otherwise `undefined`. - */ - getValue(key: symbol): unknown; - /** - * Deletes an object from the context if it exists. - * @param key - The key of the given context value to delete. - */ - deleteValue(key: symbol): TracingContext; -} - -/** - * Tracing options to set on an operation. - */ -export interface OperationTracingOptions { - /** The context to use for created Tracing Spans. */ - tracingContext?: TracingContext; -} - -/** - * A utility type for when we know a TracingContext has been set - * as part of an operation's options. - */ -export type OptionsWithTracingContext< - Options extends { tracingOptions?: OperationTracingOptions }, -> = Options & { - tracingOptions: { - tracingContext: TracingContext; - }; -}; diff --git a/sdk/core/ts-http-runtime/src/tracing/state-browser.mts b/sdk/core/ts-http-runtime/src/tracing/state-browser.mts deleted file mode 100644 index 6e5627125e50..000000000000 --- a/sdk/core/ts-http-runtime/src/tracing/state-browser.mts +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -import type { Instrumenter } from "./interfaces.js"; - -/** - * Browser-only implementation of the module's state. The browser esm variant will not load the commonjs state, so we do not need to share state between the two. - */ -export const state = { - instrumenterImplementation: undefined as Instrumenter | undefined, -}; diff --git a/sdk/core/ts-http-runtime/src/tracing/state-cjs.cts b/sdk/core/ts-http-runtime/src/tracing/state-cjs.cts deleted file mode 100644 index 181bfa0ba427..000000000000 --- a/sdk/core/ts-http-runtime/src/tracing/state-cjs.cts +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -/** - * @internal - * - * Holds the singleton instrumenter, to be shared across CJS and ESM imports. - */ -export const state = { - instrumenterImplementation: undefined, -}; diff --git a/sdk/core/ts-http-runtime/src/tracing/state.ts b/sdk/core/ts-http-runtime/src/tracing/state.ts deleted file mode 100644 index ccb13f4c8d79..000000000000 --- a/sdk/core/ts-http-runtime/src/tracing/state.ts +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -import type { Instrumenter } from "./interfaces.js"; -// @ts-expect-error The recommended approach to sharing module state between ESM and CJS. -// See https://github.com/isaacs/tshy/blob/main/README.md#module-local-state for additional information. -import { state as cjsState } from "../commonjs/tracing/state.js"; - -/** - * Defines the shared state between CJS and ESM by re-exporting the CJS state. - */ -export const state = cjsState as { - instrumenterImplementation: Instrumenter | undefined; -}; diff --git a/sdk/core/ts-http-runtime/src/tracing/tracingClient.ts b/sdk/core/ts-http-runtime/src/tracing/tracingClient.ts deleted file mode 100644 index 3e9603dbd6f7..000000000000 --- a/sdk/core/ts-http-runtime/src/tracing/tracingClient.ts +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -import type { - OperationTracingOptions, - OptionsWithTracingContext, - Resolved, - TracingClient, - TracingClientOptions, - TracingContext, - TracingSpan, - TracingSpanOptions, -} from "./interfaces.js"; -import { getInstrumenter } from "./instrumenter.js"; -import { knownContextKeys } from "./tracingContext.js"; - -/** - * Creates a new tracing client. - * - * @param options - Options used to configure the tracing client. - * @returns - An instance of {@link TracingClient}. - */ -export function createTracingClient(options: TracingClientOptions): TracingClient { - const { namespace, packageName, packageVersion } = options; - - function startSpan( - name: string, - operationOptions?: Options, - spanOptions?: TracingSpanOptions, - ): { - span: TracingSpan; - updatedOptions: OptionsWithTracingContext; - } { - const startSpanResult = getInstrumenter().startSpan(name, { - ...spanOptions, - packageName: packageName, - packageVersion: packageVersion, - tracingContext: operationOptions?.tracingOptions?.tracingContext, - }); - let tracingContext = startSpanResult.tracingContext; - const span = startSpanResult.span; - if (!tracingContext.getValue(knownContextKeys.namespace)) { - tracingContext = tracingContext.setValue(knownContextKeys.namespace, namespace); - } - span.setAttribute("az.namespace", tracingContext.getValue(knownContextKeys.namespace)); - const updatedOptions: OptionsWithTracingContext = Object.assign({}, operationOptions, { - tracingOptions: { ...operationOptions?.tracingOptions, tracingContext }, - }); - - return { - span, - updatedOptions, - }; - } - - async function withSpan< - Options extends { tracingOptions?: OperationTracingOptions }, - Callback extends ( - updatedOptions: Options, - span: Omit, - ) => ReturnType, - >( - name: string, - operationOptions: Options, - callback: Callback, - spanOptions?: TracingSpanOptions, - ): Promise>> { - const { span, updatedOptions } = startSpan(name, operationOptions, spanOptions); - try { - const result = await withContext(updatedOptions.tracingOptions.tracingContext, () => - Promise.resolve(callback(updatedOptions, span)), - ); - span.setStatus({ status: "success" }); - return result as ReturnType; - } catch (err: any) { - span.setStatus({ status: "error", error: err }); - throw err; - } finally { - span.end(); - } - } - - function withContext< - CallbackArgs extends unknown[], - Callback extends (...args: CallbackArgs) => ReturnType, - >( - context: TracingContext, - callback: Callback, - ...callbackArgs: CallbackArgs - ): ReturnType { - return getInstrumenter().withContext(context, callback, ...callbackArgs); - } - - /** - * Parses a traceparent header value into a span identifier. - * - * @param traceparentHeader - The traceparent header to parse. - * @returns An implementation-specific identifier for the span. - */ - function parseTraceparentHeader(traceparentHeader: string): TracingContext | undefined { - return getInstrumenter().parseTraceparentHeader(traceparentHeader); - } - - /** - * Creates a set of request headers to propagate tracing information to a backend. - * - * @param tracingContext - The context containing the span to serialize. - * @returns The set of headers to add to a request. - */ - function createRequestHeaders(tracingContext?: TracingContext): Record { - return getInstrumenter().createRequestHeaders(tracingContext); - } - - return { - startSpan, - withSpan, - withContext, - parseTraceparentHeader, - createRequestHeaders, - }; -} diff --git a/sdk/core/ts-http-runtime/src/tracing/tracingContext.ts b/sdk/core/ts-http-runtime/src/tracing/tracingContext.ts deleted file mode 100644 index 5528c7a8f466..000000000000 --- a/sdk/core/ts-http-runtime/src/tracing/tracingContext.ts +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -import type { TracingContext, TracingSpan } from "./interfaces.js"; - -/** @internal */ -export const knownContextKeys = { - span: Symbol.for("@typespec/ts-http-runtime span"), - namespace: Symbol.for("@typespec/ts-http-runtime namespace"), -}; - -/** - * Creates a new {@link TracingContext} with the given options. - * @param options - A set of known keys that may be set on the context. - * @returns A new {@link TracingContext} with the given options. - * - * @internal - */ -export function createTracingContext(options: CreateTracingContextOptions = {}): TracingContext { - let context: TracingContext = new TracingContextImpl(options.parentContext); - if (options.span) { - context = context.setValue(knownContextKeys.span, options.span); - } - if (options.namespace) { - context = context.setValue(knownContextKeys.namespace, options.namespace); - } - return context; -} - -/** @internal */ -export class TracingContextImpl implements TracingContext { - private _contextMap: Map; - constructor(initialContext?: TracingContext) { - this._contextMap = - initialContext instanceof TracingContextImpl - ? new Map(initialContext._contextMap) - : new Map(); - } - - setValue(key: symbol, value: unknown): TracingContext { - const newContext = new TracingContextImpl(this); - newContext._contextMap.set(key, value); - return newContext; - } - - getValue(key: symbol): unknown { - return this._contextMap.get(key); - } - - deleteValue(key: symbol): TracingContext { - const newContext = new TracingContextImpl(this); - newContext._contextMap.delete(key); - return newContext; - } -} - -/** - * Represents a set of items that can be set when creating a new {@link TracingContext}. - */ -export interface CreateTracingContextOptions { - /** The {@link parentContext} - the newly created context will contain all the values of the parent context unless overridden. */ - parentContext?: TracingContext; - /** An initial span to set on the context. */ - span?: TracingSpan; - /** The namespace to set on any child spans. */ - namespace?: string; -} diff --git a/sdk/core/ts-http-runtime/test/bearerTokenAuthenticationPolicy.spec.ts b/sdk/core/ts-http-runtime/test/bearerTokenAuthenticationPolicy.spec.ts index 46722fcde5fb..44c557784c4a 100644 --- a/sdk/core/ts-http-runtime/test/bearerTokenAuthenticationPolicy.spec.ts +++ b/sdk/core/ts-http-runtime/test/bearerTokenAuthenticationPolicy.spec.ts @@ -4,17 +4,17 @@ import { describe, it, assert, expect, vi, beforeEach, afterEach } from "vitest"; import type { AccessToken, TokenCredential } from "../src/auth/tokenCredential.js"; import type { - AuthorizeRequestOnChallengeOptions, PipelinePolicy, PipelineResponse, SendRequest, } from "../src/index.js"; import { - bearerTokenAuthenticationPolicy, createHttpHeaders, createPipelineRequest, } from "../src/index.js"; import { DEFAULT_CYCLER_OPTIONS } from "../src/util/tokenCycler.js"; +import type { AuthorizeRequestOnChallengeOptions} from "../src/policies/bearerTokenAuthenticationPolicy.js"; +import { bearerTokenAuthenticationPolicy } from "../src/policies/bearerTokenAuthenticationPolicy.js"; const { refreshWindowInMs: defaultRefreshWindow } = DEFAULT_CYCLER_OPTIONS; diff --git a/sdk/core/ts-http-runtime/test/defaultLogPolicy.spec.ts b/sdk/core/ts-http-runtime/test/defaultLogPolicy.spec.ts index 8e9590456755..7a4827495cce 100644 --- a/sdk/core/ts-http-runtime/test/defaultLogPolicy.spec.ts +++ b/sdk/core/ts-http-runtime/test/defaultLogPolicy.spec.ts @@ -36,7 +36,6 @@ describe("defaultLogPolicy", function () { "userAgentPolicy", "multipartPolicy", "defaultRetryPolicy", - "tracingPolicy", ); if (isNodeLike) { expectedOrderedPolicies.push("redirectPolicy"); @@ -66,7 +65,7 @@ describe("defaultLogPolicy", function () { ); const expectedOrder: string[] = orderedPolicies.map((policy) => policy.name); - const repeatedPolicies = expectedOrder.slice(expectedOrder.indexOf("tracingPolicy")); + const repeatedPolicies = ["redirectPolicy", "testSignPolicy", "logPolicy"]; for (let i = 0; i < DEFAULT_RETRY_POLICY_COUNT; i++) { expectedOrder.push(...repeatedPolicies); } diff --git a/sdk/core/ts-http-runtime/test/defaultRetryPolicy.spec.ts b/sdk/core/ts-http-runtime/test/defaultRetryPolicy.spec.ts index bfe72d3e72d9..7a8587e58ba9 100644 --- a/sdk/core/ts-http-runtime/test/defaultRetryPolicy.spec.ts +++ b/sdk/core/ts-http-runtime/test/defaultRetryPolicy.spec.ts @@ -3,8 +3,9 @@ import { describe, it, assert, expect, vi, afterEach } from "vitest"; import type { SendRequest } from "../src/index.js"; -import { RestError, createPipelineRequest, defaultRetryPolicy } from "../src/index.js"; +import { RestError, createPipelineRequest } from "../src/index.js"; import { DEFAULT_RETRY_POLICY_COUNT } from "../src/constants.js"; +import { defaultRetryPolicy } from "../src/policies/defaultRetryPolicy.js"; describe("defaultRetryPolicy", function () { afterEach(function () { diff --git a/sdk/core/ts-http-runtime/test/formDataPolicy.spec.ts b/sdk/core/ts-http-runtime/test/formDataPolicy.spec.ts index 216e22198cbd..5e27e173bf4b 100644 --- a/sdk/core/ts-http-runtime/test/formDataPolicy.spec.ts +++ b/sdk/core/ts-http-runtime/test/formDataPolicy.spec.ts @@ -3,17 +3,13 @@ import { describe, it, assert, vi } from "vitest"; import type { PipelineResponse, SendRequest } from "../src/index.js"; -import { - createFile, - createFileFromStream, - createHttpHeaders, - createPipelineRequest, - formDataPolicy, - isBrowser, - isNodeLike, - stringToUint8Array, -} from "../src/index.js"; +import { stringToUint8Array } from "../src/index.js"; import type { BodyPart, FormDataMap, MultipartRequestBody } from "../src/interfaces.js"; +import { createPipelineRequest } from "../src/pipelineRequest.js"; +import { createHttpHeaders } from "../src/httpHeaders.js"; +import { formDataPolicy } from "../src/policies/formDataPolicy.js"; +import { createFile, createFileFromStream } from "../src/util/file.js"; +import { isBrowser, isNodeLike } from "../src/util/checkEnvironment.js"; export async function performRequest(formData: FormDataMap): Promise { const request = createPipelineRequest({ diff --git a/sdk/core/ts-http-runtime/test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts b/sdk/core/ts-http-runtime/test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts index 8394c29f0be4..71572a5b00b5 100644 --- a/sdk/core/ts-http-runtime/test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts +++ b/sdk/core/ts-http-runtime/test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts @@ -8,17 +8,15 @@ import type { TokenCredential, } from "../../src/auth/tokenCredential.js"; import type { - AuthorizeRequestOnChallengeOptions, HttpClient, PipelineResponse, } from "../../src/index.js"; -import { - bearerTokenAuthenticationPolicy, - createEmptyPipeline, - createHttpHeaders, - createPipelineRequest, -} from "../../src/index.js"; import { TextDecoder } from "node:util"; +import { createPipelineRequest } from "../../src/pipelineRequest.js"; +import { createHttpHeaders } from "../../src/httpHeaders.js"; +import { createEmptyPipeline } from "../../src/pipeline.js"; +import type { AuthorizeRequestOnChallengeOptions} from "../../src/policies/bearerTokenAuthenticationPolicy.js"; +import { bearerTokenAuthenticationPolicy } from "../../src/policies/bearerTokenAuthenticationPolicy.js"; export interface TestChallenge { scope: string; diff --git a/sdk/core/ts-http-runtime/test/node/checkEnvironment.spec.ts b/sdk/core/ts-http-runtime/test/node/checkEnvironment.spec.ts index 326c5dc29669..72a1b5ebac54 100644 --- a/sdk/core/ts-http-runtime/test/node/checkEnvironment.spec.ts +++ b/sdk/core/ts-http-runtime/test/node/checkEnvironment.spec.ts @@ -1,17 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import { - isBrowser, - isBun, - isDeno, - isNode, - isNodeLike, - isNodeRuntime, - isReactNative, - isWebWorker, -} from "../../src/index.js"; import { describe, it, assert } from "vitest"; +import { isBrowser, isBun, isDeno, isNode, isNodeLike, isNodeRuntime, isReactNative, isWebWorker } from "../../src/util/checkEnvironment.js"; describe("checkEnvironment (node)", function () { describe("isBun (node)", function () { diff --git a/sdk/core/ts-http-runtime/test/node/nodeHttpClient.spec.ts b/sdk/core/ts-http-runtime/test/node/nodeHttpClient.spec.ts index aae1e1662c35..787e29a7a1a7 100644 --- a/sdk/core/ts-http-runtime/test/node/nodeHttpClient.spec.ts +++ b/sdk/core/ts-http-runtime/test/node/nodeHttpClient.spec.ts @@ -5,7 +5,7 @@ import { describe, it, assert, vi, beforeEach, afterEach } from "vitest"; import { PassThrough, Writable } from "node:stream"; import type { ClientRequest, IncomingHttpHeaders, IncomingMessage } from "http"; import type { AbortSignalLike } from "../../src/abort-controller/AbortSignalLike.js"; -import { createDefaultHttpClient, createPipelineRequest, delay } from "../../src/index.js"; +import { createPipelineRequest } from "../../src/index.js"; vi.mock("https", async () => { const actual = await vi.importActual("https"); @@ -25,6 +25,8 @@ vi.mock("http", async () => { import * as https from "https"; import * as http from "http"; +import { createDefaultHttpClient } from "../../src/defaultHttpClient.js"; +import { delay } from "../../src/util/delay.js"; class FakeResponse extends PassThrough { public statusCode?: number; diff --git a/sdk/core/ts-http-runtime/test/node/proxyPolicy.spec.ts b/sdk/core/ts-http-runtime/test/node/proxyPolicy.spec.ts index 31cad1bcb75c..ce12d3ee82db 100644 --- a/sdk/core/ts-http-runtime/test/node/proxyPolicy.spec.ts +++ b/sdk/core/ts-http-runtime/test/node/proxyPolicy.spec.ts @@ -7,11 +7,9 @@ import type { Agent, PipelineRequest } from "../../src/index.js"; import { type ProxySettings, type SendRequest, - createPipelineRequest, - getDefaultProxySettings, - proxyPolicy, + createPipelineRequest } from "../../src/index.js"; -import { globalNoProxyList, loadNoProxy } from "../../src/policies/proxyPolicy.js"; +import { getDefaultProxySettings, globalNoProxyList, loadNoProxy, proxyPolicy } from "../../src/policies/proxyPolicy.js"; interface ProxyAgent extends Agent { proxy: URL; diff --git a/sdk/core/ts-http-runtime/test/pipeline.spec.ts b/sdk/core/ts-http-runtime/test/pipeline.spec.ts index d2e1e97080df..7655a82795e2 100644 --- a/sdk/core/ts-http-runtime/test/pipeline.spec.ts +++ b/sdk/core/ts-http-runtime/test/pipeline.spec.ts @@ -4,11 +4,11 @@ import { describe, it, assert } from "vitest"; import type { HttpClient, PipelinePolicy } from "../src/index.js"; import { - createEmptyPipeline, createHttpHeaders, - createPipelineFromOptions, createPipelineRequest, } from "../src/index.js"; +import { createEmptyPipeline } from "../src/pipeline.js"; +import { createPipelineFromOptions } from "../src/createPipelineFromOptions.js"; describe("HttpsPipeline", function () { it("Newly created pipeline has no policies", function () { diff --git a/sdk/core/ts-http-runtime/test/tracing/instrumenter.spec.ts b/sdk/core/ts-http-runtime/test/tracing/instrumenter.spec.ts deleted file mode 100644 index 1ed8ed50a384..000000000000 --- a/sdk/core/ts-http-runtime/test/tracing/instrumenter.spec.ts +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -import type { Instrumenter, TracingSpan } from "../../src/tracing/interfaces.js"; -import { - createDefaultInstrumenter, - createDefaultTracingSpan, - getInstrumenter, - useInstrumenter, -} from "../../src/tracing/instrumenter.js"; -import { createTracingContext, knownContextKeys } from "../../src/tracing/tracingContext.js"; -import { describe, it, assert, beforeEach } from "vitest"; - -describe("Instrumenter", () => { - describe("NoOpInstrumenter", () => { - let instrumenter: Instrumenter; - const name = "test-operation"; - - beforeEach(() => { - instrumenter = createDefaultInstrumenter(); - }); - - describe("#startSpan", () => { - const packageName = "test-package"; - - it("returns a new context", () => { - const { tracingContext } = instrumenter.startSpan(name, { packageName }); - assert.exists(tracingContext); - }); - - it("returns context with all existing properties", () => { - const [key, value] = [Symbol.for("key"), "value"]; - const context = createTracingContext().setValue(key, value); - - const { tracingContext } = instrumenter.startSpan(name, { - tracingContext: context, - packageName, - }); - assert.strictEqual(tracingContext.getValue(key), value); - }); - }); - - describe("#withContext", () => { - it("applies the callback", () => { - const expectedText = "expected"; - const result = instrumenter.withContext(createTracingContext(), () => expectedText); - assert.equal(result, expectedText); - }); - }); - - describe("#parseTraceparentHeader", () => { - it("returns undefined", () => { - assert.isUndefined(instrumenter.parseTraceparentHeader("")); - }); - }); - - describe("#createRequestHeaders", () => { - it("returns an empty object", () => { - assert.isEmpty(instrumenter.createRequestHeaders(createTracingContext())); - assert.isEmpty( - instrumenter.createRequestHeaders( - createTracingContext().setValue(knownContextKeys.span, createDefaultTracingSpan()), - ), - ); - }); - }); - }); - - describe("NoOpSpan", () => { - it("supports all TracingSpan methods", () => { - const span: TracingSpan = createDefaultTracingSpan(); - span.setStatus({ status: "success" }); - span.setAttribute("foo", "bar"); - span.recordException(new Error("test")); - assert.exists(span.addEvent); - span.addEvent?.("I said Span not Spren!", { attributes: { foo: "Bar" } }); - span.end(); - assert.isFalse(span.isRecording()); - }); - }); - - describe("useInstrumenter", () => { - it("allows setting and getting a global instrumenter", () => { - const instrumenter = getInstrumenter(); - assert.exists(instrumenter); - - const newInstrumenter = createDefaultInstrumenter(); - useInstrumenter(newInstrumenter); - assert.strictEqual(getInstrumenter(), newInstrumenter); - }); - }); -}); diff --git a/sdk/core/ts-http-runtime/test/tracing/interfaces.spec.ts b/sdk/core/ts-http-runtime/test/tracing/interfaces.spec.ts deleted file mode 100644 index dca18ae2990b..000000000000 --- a/sdk/core/ts-http-runtime/test/tracing/interfaces.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -import { describe, it, assert } from "vitest"; -import type { GetTokenOptions } from "../../src/auth/tokenCredential.js"; -import type { OperationTracingOptions } from "../../src/tracing/interfaces.js"; -import { createTracingContext } from "../../src/tracing/tracingContext.js"; - -describe("Interface compatibility", () => { - describe("OperationTracingOptions", () => { - it("is compatible with core-auth", () => { - const tracingOptions: OperationTracingOptions = { - tracingContext: createTracingContext({}), - }; - const authOptions: GetTokenOptions = { - tracingOptions, - }; - assert.ok(authOptions.tracingOptions); - }); - }); -}); diff --git a/sdk/core/ts-http-runtime/test/tracing/tracingClient.spec.ts b/sdk/core/ts-http-runtime/test/tracing/tracingClient.spec.ts deleted file mode 100644 index 37943c777109..000000000000 --- a/sdk/core/ts-http-runtime/test/tracing/tracingClient.spec.ts +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -import { describe, it, assert, expect, vi, beforeEach, afterEach } from "vitest"; -import type { - Instrumenter, - TracingClient, - TracingContext, - TracingSpan, -} from "../../src/tracing/interfaces.js"; -import { - createDefaultInstrumenter, - createDefaultTracingSpan, - useInstrumenter, -} from "../../src/tracing/instrumenter.js"; -import { createTracingContext, knownContextKeys } from "../../src/tracing/tracingContext.js"; -import { createTracingClient } from "../../src/tracing/tracingClient.js"; - -describe("TracingClient", () => { - let instrumenter: Instrumenter; - let span: TracingSpan; - let context: TracingContext; - let client: TracingClient; - const expectedNamespace = "Microsoft.Test"; - - beforeEach(() => { - instrumenter = createDefaultInstrumenter(); - span = createDefaultTracingSpan(); - context = createTracingContext(); - - useInstrumenter(instrumenter); - client = createTracingClient({ - namespace: expectedNamespace, - packageName: "test-package", - packageVersion: "1.0.0", - }); - }); - - afterEach(() => { - vi.restoreAllMocks(); - }); - - describe("#startSpan", () => { - it("sets namespace on span", () => { - // Set our instrumenter to always return the same span and context so we - // can inspect them. - instrumenter.startSpan = () => { - return { - span, - tracingContext: context, - }; - }; - const setAttributeSpy = vi.spyOn(span, "setAttribute"); - client.startSpan("test", {}); - expect(setAttributeSpy).toBeCalledWith("az.namespace", expectedNamespace); - }); - - it("passes package information to instrumenter", () => { - const instrumenterStartSpanSpy = vi.spyOn(instrumenter, "startSpan"); - client.startSpan("test", {}); - expect(instrumenterStartSpanSpy).toHaveBeenCalledOnce(); - expect(instrumenterStartSpanSpy).toHaveBeenCalledWith("test", { - packageName: "test-package", - packageVersion: "1.0.0", - tracingContext: undefined, - }); - }); - - it("sets namespace on context", () => { - const { updatedOptions } = client.startSpan("test"); - assert.equal( - updatedOptions.tracingOptions?.tracingContext?.getValue(knownContextKeys.namespace), - expectedNamespace, - ); - }); - - it("does not override existing namespace on context", () => { - context = createTracingContext().setValue(knownContextKeys.namespace, "Existing.Namespace"); - const { updatedOptions } = client.startSpan("test", { - tracingOptions: { tracingContext: context }, - }); - assert.equal( - updatedOptions.tracingOptions?.tracingContext?.getValue(knownContextKeys.namespace), - "Existing.Namespace", - ); - }); - - it("Returns tracingContext in updatedOptions", () => { - let { updatedOptions } = client.startSpan>("test"); - assert.exists(updatedOptions.tracingOptions.tracingContext); - updatedOptions = client.startSpan("test", updatedOptions).updatedOptions; - assert.exists(updatedOptions.tracingOptions.tracingContext); - }); - - it("Does not erase unknown tracingOptions", () => { - // this test is to future-proof any tracingOptions we might add - const { updatedOptions } = client.startSpan>("test", { - tracingOptions: { unknownProp: true } as any, - }); - assert.exists((updatedOptions.tracingOptions as any).unknownProp); - }); - }); - - describe("#withSpan", () => { - const spanName = "test-span"; - - it("sets namespace on span", async () => { - // Set our instrumenter to always return the same span and context so we - // can inspect them. - instrumenter.startSpan = () => { - return { - span, - tracingContext: context, - }; - }; - const setAttributeSpy = vi.spyOn(span, "setAttribute"); - await client.withSpan(spanName, {}, async () => { - // no op - }); - expect(setAttributeSpy).toBeCalledWith("az.namespace", expectedNamespace); - }); - - it("passes options and span to callback", async () => { - await client.withSpan(spanName, { foo: "foo", bar: "bar" } as any, (options, currentSpan) => { - assert.exists(currentSpan); - assert.exists(options); - assert.equal(options.foo, "foo"); - assert.equal(options.bar, "bar"); - return true; - }); - }); - - it("promisifies synchronous functions", async () => { - const result = await client.withSpan(spanName, {}, () => { - return 5; - }); - assert.equal(result, 5); - }); - - it("supports asynchronous functions", async () => { - const result = await client.withSpan(spanName, {}, () => { - return Promise.resolve(5); - }); - assert.equal(result, 5); - }); - - it("returns context with all existing properties", async () => { - const [key, value] = [Symbol.for("key"), "value"]; - const parentContext = createTracingContext().setValue(key, value); - await client.withSpan( - spanName, - { - tracingOptions: { - tracingContext: parentContext, - }, - }, - (updatedOptions) => { - assert.strictEqual(updatedOptions.tracingOptions.tracingContext.getValue(key), value); - }, - ); - }); - - describe("with a successful callback", () => { - it("sets status on the span", async () => { - // Set our instrumenter to always return the same span and context so we - // can inspect them. - instrumenter.startSpan = () => { - return { - span, - tracingContext: context, - }; - }; - const setStatusSpy = vi.spyOn(span, "setStatus"); - await client.withSpan(spanName, {}, () => Promise.resolve(42)); - - expect(setStatusSpy).toHaveBeenCalledWith({ status: "success" }); - }); - }); - - describe("with an error", () => { - it("sets status on the span", async () => { - // Set our instrumenter to always return the same span and context so we - // can inspect them. - instrumenter.startSpan = () => { - return { - span, - tracingContext: context, - }; - }; - const setStatusSpy = vi.spyOn(span, "setStatus"); - let errorThrown = false; - try { - await client.withSpan(spanName, {}, () => Promise.reject(new Error("test"))); - } catch (err: any) { - errorThrown = true; - expect(setStatusSpy).toHaveBeenCalledWith({ status: "error", error: err }); - } - - assert.isTrue(errorThrown); - }); - }); - }); -}); diff --git a/sdk/core/ts-http-runtime/test/tracing/tracingContext.spec.ts b/sdk/core/ts-http-runtime/test/tracing/tracingContext.spec.ts deleted file mode 100644 index 49742b8121e6..000000000000 --- a/sdk/core/ts-http-runtime/test/tracing/tracingContext.spec.ts +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -import { describe, it, assert, beforeEach } from "vitest"; -import { - TracingContextImpl, - createTracingContext, - knownContextKeys, -} from "../../src/tracing/tracingContext.js"; -import { createDefaultTracingSpan } from "../../src/tracing/instrumenter.js"; - -describe("TracingContext", () => { - describe("TracingContextImpl", () => { - let context: TracingContextImpl; - - beforeEach(() => { - context = new TracingContextImpl(); - }); - - it("can be created from an existing context map", () => { - const existingContext = createTracingContext() - .setValue(Symbol.for("key1"), "value1") - .setValue(Symbol.for("key2"), "value2"); - const newContext = new TracingContextImpl(existingContext); - assert.equal(newContext.getValue(Symbol.for("key1")), "value1"); - assert.equal(newContext.getValue(Symbol.for("key2")), "value2"); - }); - - describe("getValue and setValue", () => { - it("returns new context with new value", () => { - const newContext = context.setValue(Symbol.for("newKey"), "newVal"); - assert.equal(newContext.getValue(Symbol.for("newKey")), "newVal"); - }); - - it("returns new context with all existing values", () => { - const newContext = context - .setValue(Symbol.for("newKey"), "newVal") - .setValue(Symbol.for("someOtherKey"), "someOtherVal") - .setValue(Symbol.for("lastKey"), "lastVal"); - - // inherited context data should remain - assert.equal(newContext.getValue(Symbol.for("newKey")), "newVal"); - }); - - it("does not modify existing context", () => { - context.setValue(Symbol.for("newKey"), "newVal"); - assert.notExists(context.getValue(Symbol.for("newKey"))); - }); - - it("can fetch parent chain data", () => { - const newContext = context - .setValue(Symbol.for("ancestry"), "grandparent") - .setValue(Symbol.for("ancestry"), "parent") - .setValue(Symbol.for("self"), "self"); // use a different key for current context - - assert.equal(newContext.getValue(Symbol.for("ancestry")), "parent"); - assert.equal(newContext.getValue(Symbol.for("self")), "self"); - }); - }); - - describe("#deleteValue", () => { - it("returns new context without deleted value", () => { - const newContext = context - .setValue(Symbol.for("newKey"), "newVal") - .deleteValue(Symbol.for("newKey")); - assert.notExists(newContext.getValue(Symbol.for("newKey"))); - }); - - it("does not modify existing context", () => { - const newContext = context.setValue(Symbol.for("newKey"), "newVal"); - newContext.deleteValue(Symbol.for("newKey")); - assert.equal(newContext.getValue(Symbol.for("newKey")), "newVal"); - }); - - it("deletes parent chain data", () => { - const newContext = context - .setValue(Symbol.for("ancestry"), "grandparent") - .setValue(Symbol.for("ancestry"), "parent") - .setValue(Symbol.for("self"), "self"); - - assert.isDefined(newContext.getValue(Symbol.for("ancestry"))); - assert.isDefined(newContext.getValue(Symbol.for("self"))); - - const updatedContext = newContext - .deleteValue(Symbol.for("ancestry")) - .deleteValue(Symbol.for("self")); - - assert.isUndefined(updatedContext.getValue(Symbol.for("ancestry"))); - assert.isUndefined(updatedContext.getValue(Symbol.for("self"))); - }); - }); - }); - - describe("#createTracingContext", () => { - it("returns a new context", () => { - const context = createTracingContext(); - assert.exists(context); - assert.instanceOf(context, TracingContextImpl); - }); - - it("can add known attributes", () => { - const span = createDefaultTracingSpan(); - const namespace = "test-namespace"; - const newContext = createTracingContext({ - span, - namespace, - }); - assert.strictEqual(newContext.getValue(knownContextKeys.namespace), namespace); - assert.strictEqual(newContext.getValue(knownContextKeys.span), span); - }); - - it("can be initialized from an existing context", () => { - const parentContext = createTracingContext().setValue( - knownContextKeys.namespace, - "test-namespace", - ); - const newContext = createTracingContext({ parentContext: parentContext }); - assert.equal(newContext.getValue(knownContextKeys.namespace), "test-namespace"); - }); - }); -}); diff --git a/sdk/core/ts-http-runtime/test/tracingPolicy.spec.ts b/sdk/core/ts-http-runtime/test/tracingPolicy.spec.ts deleted file mode 100644 index 2da62afd9dfc..000000000000 --- a/sdk/core/ts-http-runtime/test/tracingPolicy.spec.ts +++ /dev/null @@ -1,272 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -import { describe, it, assert, expect, vi, beforeEach, afterEach, type Mock } from "vitest"; -import type { PipelineRequest, PipelineResponse, SendRequest } from "../src/index.js"; -import { - RestError, - createHttpHeaders, - createPipelineRequest, - tracingPolicy, -} from "../src/index.js"; -import type { - Instrumenter, - InstrumenterSpanOptions, - SpanStatus, - TracingContext, - TracingSpan, - TracingSpanOptions, -} from "../src/tracing/interfaces.js"; -import { useInstrumenter } from "../src/tracing/instrumenter.js"; - -class MockSpan implements TracingSpan { - spanAttributes: Record = {}; - endCalled: boolean = false; - status?: SpanStatus; - exceptions: Array = []; - - constructor( - public name: string, - spanOptions: TracingSpanOptions = {}, - ) { - this.spanAttributes = spanOptions.spanAttributes ?? {}; - } - - isRecording(): boolean { - return true; - } - - recordException(exception: Error | string): void { - this.exceptions.push(exception); - } - - end(): void { - this.endCalled = true; - } - - setStatus(status: SpanStatus): void { - this.status = status; - } - - setAttribute(name: string, value: unknown): void { - this.spanAttributes[name] = value; - } - - getAttribute(name: string): unknown { - return this.spanAttributes[name]; - } -} - -const noopTracingContext: TracingContext = { - deleteValue() { - return this; - }, - getValue() { - return undefined; - }, - setValue() { - return this; - }, -}; - -class MockInstrumenter implements Instrumenter { - lastSpanCreated: MockSpan | undefined; - staticSpan: MockSpan | undefined; - - setStaticSpan(span: MockSpan): void { - this.staticSpan = span; - } - startSpan( - name: string, - spanOptions: InstrumenterSpanOptions, - ): { - span: TracingSpan; - tracingContext: TracingContext; - } { - const tracingContext = spanOptions.tracingContext ?? noopTracingContext; - if (this.staticSpan) { - return { span: this.staticSpan, tracingContext }; - } - const span = new MockSpan(name, spanOptions); - this.lastSpanCreated = span; - return { - span, - tracingContext, - }; - } - withContext< - CallbackArgs extends unknown[], - Callback extends (...args: CallbackArgs) => ReturnType, - >( - _context: TracingContext, - callback: Callback, - ...callbackArgs: CallbackArgs - ): ReturnType { - return callback(...callbackArgs); - } - - parseTraceparentHeader(_traceparentHeader: string): TracingContext | undefined { - return undefined; - } - createRequestHeaders(_tracingContext?: TracingContext): Record { - return {}; - } -} - -describe("tracingPolicy", function () { - let activeInstrumenter: MockInstrumenter; - - function createTestRequest({ noContext = false } = {}): { - request: PipelineRequest; - next: Mock; - } { - const request = createPipelineRequest({ - url: "https://bing.com", - method: "POST", - tracingOptions: { tracingContext: noContext ? undefined : noopTracingContext }, - }); - - const response: PipelineResponse = { - headers: createHttpHeaders(), - request: request, - status: 200, - }; - const next = vi.fn(); - next.mockResolvedValue(response); - return { request, next }; - } - - beforeEach(() => { - activeInstrumenter = new MockInstrumenter(); - useInstrumenter(activeInstrumenter); - }); - - afterEach(() => { - vi.restoreAllMocks(); - }); - - it("will create a span with the correct data", async () => { - const policy = tracingPolicy(); - const { request, next } = createTestRequest(); - await policy.sendRequest(request, next); - - const createdSpan = activeInstrumenter.lastSpanCreated; - assert.exists(createdSpan); - const mockSpan = createdSpan!; - assert.isTrue(mockSpan.endCalled, "expected span to be ended"); - assert.equal(mockSpan.name, "HTTP POST"); - assert.equal(mockSpan.getAttribute("http.method"), "POST"); - assert.equal(mockSpan.getAttribute("http.url"), request.url); - assert.equal(mockSpan.getAttribute("requestId"), request.requestId); - assert.equal(mockSpan.getAttribute("http.status_code"), 200); // createTestRequest's response will return 200 OK - }); - - it("will sanitize URLs", async () => { - const policy = tracingPolicy({ additionalAllowedQueryParameters: ["allowedQueryParam"] }); - const request = createPipelineRequest({ - url: "https://bing.com/search?redactedParam=redactedValue&allowedQueryParam=allowedValue", - tracingOptions: { tracingContext: noopTracingContext }, - }); - - const response: PipelineResponse = { - headers: createHttpHeaders(), - request: request, - status: 200, - }; - const next = vi.fn(); - next.mockResolvedValue(response); - - await policy.sendRequest(request, next); - const createdSpan = activeInstrumenter.lastSpanCreated; - if (!createdSpan) { - assert.fail("expected span to be created"); - } - - const spanUrlValue = new URL(createdSpan.getAttribute("http.url") as string); - assert.equal(spanUrlValue.searchParams.get("redactedParam"), "REDACTED"); - assert.equal(spanUrlValue.searchParams.get("allowedQueryParam"), "allowedValue"); - }); - - it("will set request headers correctly", async () => { - vi.spyOn(activeInstrumenter, "createRequestHeaders").mockReturnValue({ - testheader: "testvalue", - }); - const { request, next } = createTestRequest(); - - const policy = tracingPolicy(); - await policy.sendRequest(request, next); - assert.equal(request.headers.get("testheader"), "testvalue"); - }); - - it("will close a span if an error is encountered", async () => { - const request = createPipelineRequest({ - url: "https://bing.com", - tracingOptions: { - tracingContext: noopTracingContext, - }, - }); - - const policy = tracingPolicy(); - const next = vi.fn(); - const requestError = new RestError("Bad Request.", { statusCode: 400 }); - next.mockRejectedValue(requestError); - - await expect(policy.sendRequest(request, next)).rejects.toThrow(requestError); - const createdSpan = activeInstrumenter.lastSpanCreated; - assert.exists(createdSpan); - const mockSpan = createdSpan!; - assert.equal(mockSpan.status?.status, "error"); - if (mockSpan.status?.status === "error") { - assert.equal(mockSpan.status?.error, requestError); - } - assert.isTrue(mockSpan.endCalled, "end was expected to be called!"); - assert.equal(mockSpan.getAttribute("http.status_code"), 400); - }); - - it("will create a span even if tracingContext is missing", async () => { - const policy = tracingPolicy(); - const { request, next } = createTestRequest({ noContext: true }); - await policy.sendRequest(request, next); - - const createdSpan = activeInstrumenter.lastSpanCreated; - assert.exists(createdSpan); - }); - - describe("span errors", () => { - it("will not fail the request when creating a span throws", async () => { - vi.spyOn(activeInstrumenter, "startSpan").mockImplementation(() => { - throw "boom"; - }); - const { request, next } = createTestRequest(); - const policy = tracingPolicy(); - - await expect(policy.sendRequest(request, next)).resolves; - }); - - it("will not fail the request when post-processing success fails", async () => { - const mockSpan = new MockSpan("mock"); - vi.spyOn(mockSpan, "end").mockImplementation(() => { - throw new Error("end is not a function"); - }); - activeInstrumenter.setStaticSpan(mockSpan); - const { request, next } = createTestRequest(); - const policy = tracingPolicy(); - - await expect(policy.sendRequest(request, next)).resolves; - }); - - it("will not fail the request when post-processing error fails", async () => { - const mockSpan = new MockSpan("mock"); - vi.spyOn(mockSpan, "end").mockImplementation(() => { - throw new Error("end is not a function"); - }); - const { request, next } = createTestRequest(); - const policy = tracingPolicy(); - const expectedError = new RestError("Bad Request.", { statusCode: 400 }); - next.mockRejectedValue(expectedError); - - // Expect the pipeline request error, _not_ the error that is thrown when ending a span. - await expect(policy.sendRequest(request, next)).rejects.toThrow(expectedError); - }); - }); -}); diff --git a/sdk/core/ts-http-runtime/test/util/aborterUtils.spec.ts b/sdk/core/ts-http-runtime/test/util/aborterUtils.spec.ts index 4f8ece5f5355..d74d3caf4433 100644 --- a/sdk/core/ts-http-runtime/test/util/aborterUtils.spec.ts +++ b/sdk/core/ts-http-runtime/test/util/aborterUtils.spec.ts @@ -3,7 +3,8 @@ import { describe, it, assert, expect, vi, afterEach } from "vitest"; import type { AbortSignalLike } from "../../src/abort-controller/AbortSignalLike.js"; -import { cancelablePromiseRace, createAbortablePromise } from "../../src/index.js"; +import { createAbortablePromise } from "../../src/util/createAbortablePromise.js"; +import { cancelablePromiseRace } from "../../src/util/aborterUtils.js"; describe("createAbortablePromise", function () { let token: ReturnType; diff --git a/sdk/core/ts-http-runtime/test/util/delay.spec.ts b/sdk/core/ts-http-runtime/test/util/delay.spec.ts index 6fe5e639f30e..6ff7c32c4633 100644 --- a/sdk/core/ts-http-runtime/test/util/delay.spec.ts +++ b/sdk/core/ts-http-runtime/test/util/delay.spec.ts @@ -2,7 +2,7 @@ // Licensed under the MIT License. import { describe, it, assert, vi, afterEach } from "vitest"; -import { calculateRetryDelay, delay } from "../../src/index.js"; +import { calculateRetryDelay, delay } from "../../src/util/delay.js"; describe("delay", function () { afterEach(function () { diff --git a/sdk/core/ts-http-runtime/test/util/sha256.spec.ts b/sdk/core/ts-http-runtime/test/util/sha256.spec.ts index fb4af561430c..8343fec4e396 100644 --- a/sdk/core/ts-http-runtime/test/util/sha256.spec.ts +++ b/sdk/core/ts-http-runtime/test/util/sha256.spec.ts @@ -2,7 +2,7 @@ // Licensed under the MIT License. import { describe, it, assert } from "vitest"; -import { computeSha256Hash, computeSha256Hmac } from "../../src/index.js"; +import { computeSha256Hash, computeSha256Hmac } from "../../src/util/sha256.js"; describe("SHA-256", function () { describe("Hash", function () { diff --git a/sdk/core/ts-http-runtime/test/util/typeGuards.spec.ts b/sdk/core/ts-http-runtime/test/util/typeGuards.spec.ts index 38de4e0ba846..d2c4df11d727 100644 --- a/sdk/core/ts-http-runtime/test/util/typeGuards.spec.ts +++ b/sdk/core/ts-http-runtime/test/util/typeGuards.spec.ts @@ -2,7 +2,7 @@ // Licensed under the MIT License. import { describe, it, assert } from "vitest"; -import { isDefined, isObjectWithProperties, objectHasProperty } from "../../src/index.js"; +import { isDefined, isObjectWithProperties, objectHasProperty } from "../../src/util/typeGuards.js"; describe("Type guards", function () { describe("isDefined", function () { From 288e1eba9a4b2b8a9b12472362107a0617bbf535 Mon Sep 17 00:00:00 2001 From: Timo van Veenendaal Date: Thu, 7 Nov 2024 09:20:29 -0800 Subject: [PATCH 02/11] format --- .../test/bearerTokenAuthenticationPolicy.spec.ts | 13 +++---------- ...bearerTokenAuthenticationPolicyChallenge.spec.ts | 7 ++----- .../test/node/checkEnvironment.spec.ts | 11 ++++++++++- .../ts-http-runtime/test/node/proxyPolicy.spec.ts | 11 ++++++----- sdk/core/ts-http-runtime/test/pipeline.spec.ts | 5 +---- 5 files changed, 22 insertions(+), 25 deletions(-) diff --git a/sdk/core/ts-http-runtime/test/bearerTokenAuthenticationPolicy.spec.ts b/sdk/core/ts-http-runtime/test/bearerTokenAuthenticationPolicy.spec.ts index 44c557784c4a..b3298f8538a5 100644 --- a/sdk/core/ts-http-runtime/test/bearerTokenAuthenticationPolicy.spec.ts +++ b/sdk/core/ts-http-runtime/test/bearerTokenAuthenticationPolicy.spec.ts @@ -3,17 +3,10 @@ import { describe, it, assert, expect, vi, beforeEach, afterEach } from "vitest"; import type { AccessToken, TokenCredential } from "../src/auth/tokenCredential.js"; -import type { - PipelinePolicy, - PipelineResponse, - SendRequest, -} from "../src/index.js"; -import { - createHttpHeaders, - createPipelineRequest, -} from "../src/index.js"; +import type { PipelinePolicy, PipelineResponse, SendRequest } from "../src/index.js"; +import { createHttpHeaders, createPipelineRequest } from "../src/index.js"; import { DEFAULT_CYCLER_OPTIONS } from "../src/util/tokenCycler.js"; -import type { AuthorizeRequestOnChallengeOptions} from "../src/policies/bearerTokenAuthenticationPolicy.js"; +import type { AuthorizeRequestOnChallengeOptions } from "../src/policies/bearerTokenAuthenticationPolicy.js"; import { bearerTokenAuthenticationPolicy } from "../src/policies/bearerTokenAuthenticationPolicy.js"; const { refreshWindowInMs: defaultRefreshWindow } = DEFAULT_CYCLER_OPTIONS; diff --git a/sdk/core/ts-http-runtime/test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts b/sdk/core/ts-http-runtime/test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts index 71572a5b00b5..520f5688fc0a 100644 --- a/sdk/core/ts-http-runtime/test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts +++ b/sdk/core/ts-http-runtime/test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts @@ -7,15 +7,12 @@ import type { GetTokenOptions, TokenCredential, } from "../../src/auth/tokenCredential.js"; -import type { - HttpClient, - PipelineResponse, -} from "../../src/index.js"; +import type { HttpClient, PipelineResponse } from "../../src/index.js"; import { TextDecoder } from "node:util"; import { createPipelineRequest } from "../../src/pipelineRequest.js"; import { createHttpHeaders } from "../../src/httpHeaders.js"; import { createEmptyPipeline } from "../../src/pipeline.js"; -import type { AuthorizeRequestOnChallengeOptions} from "../../src/policies/bearerTokenAuthenticationPolicy.js"; +import type { AuthorizeRequestOnChallengeOptions } from "../../src/policies/bearerTokenAuthenticationPolicy.js"; import { bearerTokenAuthenticationPolicy } from "../../src/policies/bearerTokenAuthenticationPolicy.js"; export interface TestChallenge { diff --git a/sdk/core/ts-http-runtime/test/node/checkEnvironment.spec.ts b/sdk/core/ts-http-runtime/test/node/checkEnvironment.spec.ts index 72a1b5ebac54..faa3d7416af2 100644 --- a/sdk/core/ts-http-runtime/test/node/checkEnvironment.spec.ts +++ b/sdk/core/ts-http-runtime/test/node/checkEnvironment.spec.ts @@ -2,7 +2,16 @@ // Licensed under the MIT License. import { describe, it, assert } from "vitest"; -import { isBrowser, isBun, isDeno, isNode, isNodeLike, isNodeRuntime, isReactNative, isWebWorker } from "../../src/util/checkEnvironment.js"; +import { + isBrowser, + isBun, + isDeno, + isNode, + isNodeLike, + isNodeRuntime, + isReactNative, + isWebWorker, +} from "../../src/util/checkEnvironment.js"; describe("checkEnvironment (node)", function () { describe("isBun (node)", function () { diff --git a/sdk/core/ts-http-runtime/test/node/proxyPolicy.spec.ts b/sdk/core/ts-http-runtime/test/node/proxyPolicy.spec.ts index ce12d3ee82db..bb42911cb435 100644 --- a/sdk/core/ts-http-runtime/test/node/proxyPolicy.spec.ts +++ b/sdk/core/ts-http-runtime/test/node/proxyPolicy.spec.ts @@ -4,12 +4,13 @@ import * as process from "node:process"; import { describe, it, assert, vi, afterEach } from "vitest"; import type { Agent, PipelineRequest } from "../../src/index.js"; +import { type ProxySettings, type SendRequest, createPipelineRequest } from "../../src/index.js"; import { - type ProxySettings, - type SendRequest, - createPipelineRequest -} from "../../src/index.js"; -import { getDefaultProxySettings, globalNoProxyList, loadNoProxy, proxyPolicy } from "../../src/policies/proxyPolicy.js"; + getDefaultProxySettings, + globalNoProxyList, + loadNoProxy, + proxyPolicy, +} from "../../src/policies/proxyPolicy.js"; interface ProxyAgent extends Agent { proxy: URL; diff --git a/sdk/core/ts-http-runtime/test/pipeline.spec.ts b/sdk/core/ts-http-runtime/test/pipeline.spec.ts index 7655a82795e2..93225e7594ec 100644 --- a/sdk/core/ts-http-runtime/test/pipeline.spec.ts +++ b/sdk/core/ts-http-runtime/test/pipeline.spec.ts @@ -3,10 +3,7 @@ import { describe, it, assert } from "vitest"; import type { HttpClient, PipelinePolicy } from "../src/index.js"; -import { - createHttpHeaders, - createPipelineRequest, -} from "../src/index.js"; +import { createHttpHeaders, createPipelineRequest } from "../src/index.js"; import { createEmptyPipeline } from "../src/pipeline.js"; import { createPipelineFromOptions } from "../src/createPipelineFromOptions.js"; From acc3b269ab1342f96a9c69f07d1b7b9ba93230f6 Mon Sep 17 00:00:00 2001 From: Timo van Veenendaal Date: Thu, 7 Nov 2024 09:36:58 -0800 Subject: [PATCH 03/11] Fix tests --- .../review/azure-core-comparison.diff | 5 ++-- sdk/core/ts-http-runtime/src/index.ts | 1 + .../test/browser/checkEnvironment.spec.ts | 2 +- .../test/browser/decompressResponsePolicy.ts | 2 +- .../test/browser/proxyPolicy.spec.ts | 2 +- .../test/defaultLogPolicy.spec.ts | 8 ++++-- .../test/node/decompressResponsePolicy.ts | 3 ++- .../ts-http-runtime/test/snippets.spec.ts | 27 +++---------------- 8 files changed, 19 insertions(+), 31 deletions(-) diff --git a/sdk/core/ts-http-runtime/review/azure-core-comparison.diff b/sdk/core/ts-http-runtime/review/azure-core-comparison.diff index 260d26c3c066..6bc50e531746 100644 --- a/sdk/core/ts-http-runtime/review/azure-core-comparison.diff +++ b/sdk/core/ts-http-runtime/review/azure-core-comparison.diff @@ -904,10 +904,10 @@ index e9751e2..e4f8769 100644 HttpClient, HttpHeaders as PipelineHeaders, diff --git a/src/index.ts b/src/index.ts -index 688a7ea..9cda6b3 100644 +index 688a7ea..3a13f13 100644 --- a/src/index.ts +++ b/src/index.ts -@@ -9,117 +9,70 @@ declare global { +@@ -9,117 +9,71 @@ declare global { interface ReadableStream {} interface TransformStream {} } @@ -916,6 +916,7 @@ index 688a7ea..9cda6b3 100644 -export type { HttpMethods } from "@azure/core-util"; + +export { AbortSignalLike } from "./abort-controller/AbortSignalLike.js"; ++export { AbortError } from "./abort-controller/AbortError.js"; +export { + createClientLogger, + TypeSpecRuntimeLogger, diff --git a/sdk/core/ts-http-runtime/src/index.ts b/sdk/core/ts-http-runtime/src/index.ts index 9cda6b3c29fe..3a13f13a876d 100644 --- a/sdk/core/ts-http-runtime/src/index.ts +++ b/sdk/core/ts-http-runtime/src/index.ts @@ -12,6 +12,7 @@ declare global { /* eslint-enable @typescript-eslint/no-unused-vars */ export { AbortSignalLike } from "./abort-controller/AbortSignalLike.js"; +export { AbortError } from "./abort-controller/AbortError.js"; export { createClientLogger, TypeSpecRuntimeLogger, diff --git a/sdk/core/ts-http-runtime/test/browser/checkEnvironment.spec.ts b/sdk/core/ts-http-runtime/test/browser/checkEnvironment.spec.ts index 050ab0107f2c..169702448015 100644 --- a/sdk/core/ts-http-runtime/test/browser/checkEnvironment.spec.ts +++ b/sdk/core/ts-http-runtime/test/browser/checkEnvironment.spec.ts @@ -10,7 +10,7 @@ import { isNodeRuntime, isReactNative, isWebWorker, -} from "../../src/index.js"; +} from "../../src/util/checkEnvironment.js"; import { describe, it, assert } from "vitest"; describe("checkEnvironment (browser)", function () { diff --git a/sdk/core/ts-http-runtime/test/browser/decompressResponsePolicy.ts b/sdk/core/ts-http-runtime/test/browser/decompressResponsePolicy.ts index f99ac28c6e1c..a3696c755024 100644 --- a/sdk/core/ts-http-runtime/test/browser/decompressResponsePolicy.ts +++ b/sdk/core/ts-http-runtime/test/browser/decompressResponsePolicy.ts @@ -2,7 +2,7 @@ // Licensed under the MIT License. import { describe, it, assert } from "vitest"; -import { decompressResponsePolicy } from "../../src/index.js"; +import { decompressResponsePolicy } from "../../src/policies/decompressResponsePolicy.js"; describe("decompressResponsePolicy (browser)", function () { it("Throws on creation", function () { diff --git a/sdk/core/ts-http-runtime/test/browser/proxyPolicy.spec.ts b/sdk/core/ts-http-runtime/test/browser/proxyPolicy.spec.ts index d68117ba4072..713a5c6d05b3 100644 --- a/sdk/core/ts-http-runtime/test/browser/proxyPolicy.spec.ts +++ b/sdk/core/ts-http-runtime/test/browser/proxyPolicy.spec.ts @@ -2,7 +2,7 @@ // Licensed under the MIT License. import { describe, it, assert } from "vitest"; -import { proxyPolicy } from "../../src/index.js"; +import { proxyPolicy } from "../../src/policies/proxyPolicy.js"; describe("proxyPolicy (browser)", function () { it("Throws on creation", function () { diff --git a/sdk/core/ts-http-runtime/test/defaultLogPolicy.spec.ts b/sdk/core/ts-http-runtime/test/defaultLogPolicy.spec.ts index 7a4827495cce..453b34c8e379 100644 --- a/sdk/core/ts-http-runtime/test/defaultLogPolicy.spec.ts +++ b/sdk/core/ts-http-runtime/test/defaultLogPolicy.spec.ts @@ -7,7 +7,7 @@ import type { PipelinePolicy } from "../src/pipeline.js"; import { createHttpHeaders } from "../src/httpHeaders.js"; import { createPipelineFromOptions } from "../src/createPipelineFromOptions.js"; import { createPipelineRequest } from "../src/pipelineRequest.js"; -import { isNodeLike } from "../src/util/checkEnvironment.js"; +import { isBrowser, isNodeLike } from "../src/util/checkEnvironment.js"; describe("defaultLogPolicy", function () { it("should be invoked on every retry", async function () { @@ -65,7 +65,11 @@ describe("defaultLogPolicy", function () { ); const expectedOrder: string[] = orderedPolicies.map((policy) => policy.name); - const repeatedPolicies = ["redirectPolicy", "testSignPolicy", "logPolicy"]; + + // redirectPolicy is not added in browser + const repeatedPolicies = isBrowser + ? ["testSignPolicy", "logPolicy"] + : ["redirectPolicy", "testSignPolicy", "logPolicy"]; for (let i = 0; i < DEFAULT_RETRY_POLICY_COUNT; i++) { expectedOrder.push(...repeatedPolicies); } diff --git a/sdk/core/ts-http-runtime/test/node/decompressResponsePolicy.ts b/sdk/core/ts-http-runtime/test/node/decompressResponsePolicy.ts index bf4451dc408d..6f4f97df7e23 100644 --- a/sdk/core/ts-http-runtime/test/node/decompressResponsePolicy.ts +++ b/sdk/core/ts-http-runtime/test/node/decompressResponsePolicy.ts @@ -3,7 +3,8 @@ import { describe, it, assert, expect, vi } from "vitest"; import type { SendRequest } from "../../src/index.js"; -import { createPipelineRequest, decompressResponsePolicy } from "../../src/index.js"; +import { createPipelineRequest } from "../../src/index.js"; +import { decompressResponsePolicy } from "../../src/policies/decompressResponsePolicy.js"; describe("decompressResponsePolicy (node)", function () { it("Sets the expected flag on the request", function () { diff --git a/sdk/core/ts-http-runtime/test/snippets.spec.ts b/sdk/core/ts-http-runtime/test/snippets.spec.ts index 4dd91ceabd45..cc58da3d3ffa 100644 --- a/sdk/core/ts-http-runtime/test/snippets.spec.ts +++ b/sdk/core/ts-http-runtime/test/snippets.spec.ts @@ -3,7 +3,7 @@ /* eslint "@typescript-eslint/no-shadow": "off" */ -import { describe, it, assert } from "vitest"; +import { describe, it } from "vitest"; import type { HttpClient, PipelinePhase, @@ -11,10 +11,10 @@ import type { PipelineRequest, PipelineResponse, SendRequest, - AddPipelineOptions, + AddPolicyOptions, Client, } from "@typespec/ts-http-runtime"; -import { AbortError, createTracingClient } from "@typespec/ts-http-runtime"; +import { AbortError } from "@typespec/ts-http-runtime"; interface GetOperationResult {} interface DetectFromUrl {} @@ -72,7 +72,7 @@ describe("snippets", () => { it("pipeline", () => { interface Pipeline { - addPolicy(policy: PipelinePolicy, options?: AddPipelineOptions): void; + addPolicy(policy: PipelinePolicy, options?: AddPolicyOptions): void; removePolicy(options: { name?: string; phase?: PipelinePhase }): PipelinePolicy[]; sendRequest(httpClient: HttpClient, request: PipelineRequest): Promise; getOrderedPolicies(): PipelinePolicy[]; @@ -114,23 +114,4 @@ describe("snippets", () => { path: Routes; }; }); - - it("with_span_example", async () => { - const tracingClient = createTracingClient({ - namespace: "test.namespace", - packageName: "test-package", - packageVersion: "1.0.0", - }); - - const options = {}; - - const myOperationResult = await tracingClient.withSpan( - "myClassName.myOperationName", - options, - (updatedOptions) => { - // Do something with the updated options. - return "myOperationResult"; - }, - ); - }); }); From 4050403370d4fa1c5a3d60dd54a0b06e3b43d40c Mon Sep 17 00:00:00 2001 From: Timo van Veenendaal Date: Thu, 7 Nov 2024 10:27:31 -0800 Subject: [PATCH 04/11] build --- sdk/core/ts-http-runtime/review/ts-http-runtime.api.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sdk/core/ts-http-runtime/review/ts-http-runtime.api.md b/sdk/core/ts-http-runtime/review/ts-http-runtime.api.md index 1503c4bf6291..5f0b4dfb6e44 100644 --- a/sdk/core/ts-http-runtime/review/ts-http-runtime.api.md +++ b/sdk/core/ts-http-runtime/review/ts-http-runtime.api.md @@ -4,6 +4,11 @@ ```ts +// @public +export class AbortError extends Error { + constructor(message?: string); +} + // @public export interface AbortSignalLike { readonly aborted: boolean; From 154eb94f217065764cbd4fa2184a151c51663389 Mon Sep 17 00:00:00 2001 From: Timo van Veenendaal Date: Mon, 2 Dec 2024 09:32:23 -0800 Subject: [PATCH 05/11] 0.1.0; temporarily reverse some renames --- sdk/core/ts-http-runtime/CHANGELOG.md | 4 +++- sdk/core/ts-http-runtime/package.json | 2 +- .../review/azure-core-comparison.diff | 12 ++++++------ sdk/core/ts-http-runtime/src/constants.ts | 2 +- sdk/core/ts-http-runtime/src/index.ts | 6 +++--- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/sdk/core/ts-http-runtime/CHANGELOG.md b/sdk/core/ts-http-runtime/CHANGELOG.md index 13dd08af78ab..7d2c6ad76e20 100644 --- a/sdk/core/ts-http-runtime/CHANGELOG.md +++ b/sdk/core/ts-http-runtime/CHANGELOG.md @@ -1,9 +1,11 @@ # Release History -## 1.0.0-beta.1 (Unreleased) +## 0.1.0 (Unreleased) ### Features Added +- Initial release of the `@typespec/ts-http-runtime` package. + ### Breaking Changes ### Bugs Fixed diff --git a/sdk/core/ts-http-runtime/package.json b/sdk/core/ts-http-runtime/package.json index b899d9663f22..74ee765648b6 100644 --- a/sdk/core/ts-http-runtime/package.json +++ b/sdk/core/ts-http-runtime/package.json @@ -1,6 +1,6 @@ { "name": "@typespec/ts-http-runtime", - "version": "1.0.0-beta.1", + "version": "0.1.0", "description": "Isomorphic client library for making HTTP requests in node.js and browser.", "sdk-type": "client", "type": "module", diff --git a/sdk/core/ts-http-runtime/review/azure-core-comparison.diff b/sdk/core/ts-http-runtime/review/azure-core-comparison.diff index 0cf5f870ce9b..21bcc86113a5 100644 --- a/sdk/core/ts-http-runtime/review/azure-core-comparison.diff +++ b/sdk/core/ts-http-runtime/review/azure-core-comparison.diff @@ -837,7 +837,7 @@ index 9e947e6..f2a1cac 100644 onUploadProgress: options.onUploadProgress, onDownloadProgress: options.onDownloadProgress, diff --git a/src/constants.ts b/src/constants.ts -index b32fff7..60da499 100644 +index b32fff7..e81a30d 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,6 +1,6 @@ @@ -845,7 +845,7 @@ index b32fff7..60da499 100644 // Licensed under the MIT License. -export const SDK_VERSION: string = "1.18.2"; -+export const SDK_VERSION: string = "1.0.0-beta.1"; ++export const SDK_VERSION: string = "0.1.0"; export const DEFAULT_RETRY_POLICY_COUNT = 3; diff --git a/src/createPipelineFromOptions.ts b/src/createPipelineFromOptions.ts @@ -904,7 +904,7 @@ index e9751e2..e4f8769 100644 HttpClient, HttpHeaders as PipelineHeaders, diff --git a/src/index.ts b/src/index.ts -index 688a7ea..3a13f13 100644 +index 688a7ea..b773fa2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,117 +9,71 @@ declare global { @@ -1076,9 +1076,9 @@ index 688a7ea..3a13f13 100644 + FullOperationResponse, +} from "./client/common.js"; +export type { PipelineOptions, TelemetryOptions } from "./createPipelineFromOptions.js"; -+export type { LogPolicyOptions as LogOptions } from "./policies/logPolicy.js"; -+export type { RedirectPolicyOptions as RedirectOptions } from "./policies/redirectPolicy.js"; -+export type { UserAgentPolicyOptions as UserAgentOptions } from "./policies/userAgentPolicy.js"; ++export type { LogPolicyOptions } from "./policies/logPolicy.js"; ++export type { RedirectPolicyOptions } from "./policies/redirectPolicy.js"; ++export type { UserAgentPolicyOptions } from "./policies/userAgentPolicy.js"; diff --git a/src/interfaces.ts b/src/interfaces.ts index 26b806d..5d790ad 100644 --- a/src/interfaces.ts diff --git a/sdk/core/ts-http-runtime/src/constants.ts b/sdk/core/ts-http-runtime/src/constants.ts index 60da499956bd..e81a30d6546f 100644 --- a/sdk/core/ts-http-runtime/src/constants.ts +++ b/sdk/core/ts-http-runtime/src/constants.ts @@ -1,6 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -export const SDK_VERSION: string = "1.0.0-beta.1"; +export const SDK_VERSION: string = "0.1.0"; export const DEFAULT_RETRY_POLICY_COUNT = 3; diff --git a/sdk/core/ts-http-runtime/src/index.ts b/sdk/core/ts-http-runtime/src/index.ts index 3a13f13a876d..b773fa2ac381 100644 --- a/sdk/core/ts-http-runtime/src/index.ts +++ b/sdk/core/ts-http-runtime/src/index.ts @@ -74,6 +74,6 @@ export type { FullOperationResponse, } from "./client/common.js"; export type { PipelineOptions, TelemetryOptions } from "./createPipelineFromOptions.js"; -export type { LogPolicyOptions as LogOptions } from "./policies/logPolicy.js"; -export type { RedirectPolicyOptions as RedirectOptions } from "./policies/redirectPolicy.js"; -export type { UserAgentPolicyOptions as UserAgentOptions } from "./policies/userAgentPolicy.js"; +export type { LogPolicyOptions } from "./policies/logPolicy.js"; +export type { RedirectPolicyOptions } from "./policies/redirectPolicy.js"; +export type { UserAgentPolicyOptions } from "./policies/userAgentPolicy.js"; From 1a835d216becc88e888c1f3ac6789d2487f127fb Mon Sep 17 00:00:00 2001 From: Timo van Veenendaal Date: Mon, 2 Dec 2024 15:04:54 -0800 Subject: [PATCH 06/11] lint --- sdk/core/ts-http-runtime/eslint.config.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/core/ts-http-runtime/eslint.config.mjs b/sdk/core/ts-http-runtime/eslint.config.mjs index 5593ae4bf151..255e2dde2a81 100644 --- a/sdk/core/ts-http-runtime/eslint.config.mjs +++ b/sdk/core/ts-http-runtime/eslint.config.mjs @@ -6,6 +6,7 @@ export default [ rules: { "@azure/azure-sdk/ts-package-json-engine-is-present": "off", "@azure/azure-sdk/ts-package-json-name": "off", + "@azure/azure-sdk/ts-versioning-semver": "off", }, }, ]; From 990d12a7b88754253d5ad9aead51f6619758fe61 Mon Sep 17 00:00:00 2001 From: Timo van Veenendaal Date: Mon, 2 Dec 2024 15:54:16 -0800 Subject: [PATCH 07/11] build --- .../review/azure-core-comparison.diff | 195 +++++++++-- .../review/ts-http-runtime.api.md | 12 +- .../src/policies/tracingPolicy.ts | 170 --------- .../test/tracingPolicy.spec.ts | 324 ------------------ 4 files changed, 176 insertions(+), 525 deletions(-) delete mode 100644 sdk/core/ts-http-runtime/src/policies/tracingPolicy.ts delete mode 100644 sdk/core/ts-http-runtime/test/tracingPolicy.spec.ts diff --git a/sdk/core/ts-http-runtime/review/azure-core-comparison.diff b/sdk/core/ts-http-runtime/review/azure-core-comparison.diff index 6543d15465a4..5f8e382580ba 100644 --- a/sdk/core/ts-http-runtime/review/azure-core-comparison.diff +++ b/sdk/core/ts-http-runtime/review/azure-core-comparison.diff @@ -1950,40 +1950,185 @@ index a40d4ab..0000000 - }; -} diff --git a/src/policies/tracingPolicy.ts b/src/policies/tracingPolicy.ts -index 4f54b0d..4131837 100644 +deleted file mode 100644 +index 4f54b0d..0000000 --- a/src/policies/tracingPolicy.ts -+++ b/src/policies/tracingPolicy.ts -@@ -1,18 +1,14 @@ - // Copyright (c) Microsoft Corporation. - // Licensed under the MIT License. - ++++ /dev/null +@@ -1,174 +0,0 @@ +-// Copyright (c) Microsoft Corporation. +-// Licensed under the MIT License. +- -import { - type TracingClient, - type TracingContext, - type TracingSpan, - createTracingClient, -} from "@azure/core-tracing"; -+import type { TracingClient, TracingContext, TracingSpan } from "../tracing/interfaces.js"; -+import { createTracingClient } from "../tracing/tracingClient.js"; - import { SDK_VERSION } from "../constants.js"; - import type { PipelineRequest, PipelineResponse, SendRequest } from "../interfaces.js"; - import type { PipelinePolicy } from "../pipeline.js"; - import { getUserAgentValue } from "../util/userAgent.js"; - import { logger } from "../log.js"; +-import { SDK_VERSION } from "../constants.js"; +-import type { PipelineRequest, PipelineResponse, SendRequest } from "../interfaces.js"; +-import type { PipelinePolicy } from "../pipeline.js"; +-import { getUserAgentValue } from "../util/userAgent.js"; +-import { logger } from "../log.js"; -import { getErrorMessage, isError } from "@azure/core-util"; -+import { getErrorMessage, isError } from "../util/error.js"; - import { isRestError } from "../restError.js"; - import { Sanitizer } from "../util/sanitizer.js"; - -@@ -92,7 +88,7 @@ function tryCreateTracingClient(): TracingClient | undefined { - try { - return createTracingClient({ - namespace: "", +-import { isRestError } from "../restError.js"; +-import { Sanitizer } from "../util/sanitizer.js"; +- +-/** +- * The programmatic identifier of the tracingPolicy. +- */ +-export const tracingPolicyName = "tracingPolicy"; +- +-/** +- * Options to configure the tracing policy. +- */ +-export interface TracingPolicyOptions { +- /** +- * String prefix to add to the user agent logged as metadata +- * on the generated Span. +- * Defaults to an empty string. +- */ +- userAgentPrefix?: string; +- /** +- * Query string names whose values will be logged when logging is enabled. By default no +- * query string values are logged. +- */ +- additionalAllowedQueryParameters?: string[]; +-} +- +-/** +- * A simple policy to create OpenTelemetry Spans for each request made by the pipeline +- * that has SpanOptions with a parent. +- * Requests made without a parent Span will not be recorded. +- * @param options - Options to configure the telemetry logged by the tracing policy. +- */ +-export function tracingPolicy(options: TracingPolicyOptions = {}): PipelinePolicy { +- const userAgentPromise = getUserAgentValue(options.userAgentPrefix); +- const sanitizer = new Sanitizer({ +- additionalAllowedQueryParameters: options.additionalAllowedQueryParameters, +- }); +- const tracingClient = tryCreateTracingClient(); +- +- return { +- name: tracingPolicyName, +- async sendRequest(request: PipelineRequest, next: SendRequest): Promise { +- if (!tracingClient) { +- return next(request); +- } +- +- const userAgent = await userAgentPromise; +- +- const spanAttributes = { +- "http.url": sanitizer.sanitizeUrl(request.url), +- "http.method": request.method, +- "http.user_agent": userAgent, +- requestId: request.requestId, +- }; +- if (userAgent) { +- spanAttributes["http.user_agent"] = userAgent; +- } +- +- const { span, tracingContext } = tryCreateSpan(tracingClient, request, spanAttributes) ?? {}; +- +- if (!span || !tracingContext) { +- return next(request); +- } +- +- try { +- const response = await tracingClient.withContext(tracingContext, next, request); +- tryProcessResponse(span, response); +- return response; +- } catch (err: any) { +- tryProcessError(span, err); +- throw err; +- } +- }, +- }; +-} +- +-function tryCreateTracingClient(): TracingClient | undefined { +- try { +- return createTracingClient({ +- namespace: "", - packageName: "@azure/core-rest-pipeline", -+ packageName: "@typespec/ts-http-runtime", - packageVersion: SDK_VERSION, - }); - } catch (e: unknown) { +- packageVersion: SDK_VERSION, +- }); +- } catch (e: unknown) { +- logger.warning(`Error when creating the TracingClient: ${getErrorMessage(e)}`); +- return undefined; +- } +-} +- +-function tryCreateSpan( +- tracingClient: TracingClient, +- request: PipelineRequest, +- spanAttributes: Record, +-): { span: TracingSpan; tracingContext: TracingContext } | undefined { +- try { +- // As per spec, we do not need to differentiate between HTTP and HTTPS in span name. +- const { span, updatedOptions } = tracingClient.startSpan( +- `HTTP ${request.method}`, +- { tracingOptions: request.tracingOptions }, +- { +- spanKind: "client", +- spanAttributes, +- }, +- ); +- +- // If the span is not recording, don't do any more work. +- if (!span.isRecording()) { +- span.end(); +- return undefined; +- } +- +- // set headers +- const headers = tracingClient.createRequestHeaders( +- updatedOptions.tracingOptions.tracingContext, +- ); +- for (const [key, value] of Object.entries(headers)) { +- request.headers.set(key, value); +- } +- return { span, tracingContext: updatedOptions.tracingOptions.tracingContext }; +- } catch (e: any) { +- logger.warning(`Skipping creating a tracing span due to an error: ${getErrorMessage(e)}`); +- return undefined; +- } +-} +- +-function tryProcessError(span: TracingSpan, error: unknown): void { +- try { +- span.setStatus({ +- status: "error", +- error: isError(error) ? error : undefined, +- }); +- if (isRestError(error) && error.statusCode) { +- span.setAttribute("http.status_code", error.statusCode); +- } +- span.end(); +- } catch (e: any) { +- logger.warning(`Skipping tracing span processing due to an error: ${getErrorMessage(e)}`); +- } +-} +- +-function tryProcessResponse(span: TracingSpan, response: PipelineResponse): void { +- try { +- span.setAttribute("http.status_code", response.status); +- const serviceRequestId = response.headers.get("x-ms-request-id"); +- if (serviceRequestId) { +- span.setAttribute("serviceRequestId", serviceRequestId); +- } +- // Per semantic conventions, only set the status to error if the status code is 4xx or 5xx. +- // Otherwise, the status MUST remain unset. +- // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status +- if (response.status >= 400) { +- span.setStatus({ +- status: "error", +- }); +- } +- span.end(); +- } catch (e: any) { +- logger.warning(`Skipping tracing span processing due to an error: ${getErrorMessage(e)}`); +- } +-} diff --git a/src/restError.ts b/src/restError.ts index 0b2e69f..bc09e78 100644 --- a/src/restError.ts diff --git a/sdk/core/ts-http-runtime/review/ts-http-runtime.api.md b/sdk/core/ts-http-runtime/review/ts-http-runtime.api.md index 5f0b4dfb6e44..d3c8cf43be85 100644 --- a/sdk/core/ts-http-runtime/review/ts-http-runtime.api.md +++ b/sdk/core/ts-http-runtime/review/ts-http-runtime.api.md @@ -70,7 +70,7 @@ export type ClientOptions = PipelineOptions & { allowInsecureConnection?: boolean; additionalPolicies?: AdditionalPolicyConfig[]; httpClient?: HttpClient; - loggingOptions?: LogOptions; + loggingOptions?: LogPolicyOptions; }; // @public @@ -185,7 +185,7 @@ export interface KeyObject { } // @public -export interface LogOptions { +export interface LogPolicyOptions { additionalAllowedHeaderNames?: string[]; additionalAllowedQueryParameters?: string[]; logger?: Debugger; @@ -250,11 +250,11 @@ export interface Pipeline { // @public export interface PipelineOptions { proxyOptions?: ProxySettings; - redirectOptions?: RedirectOptions; + redirectOptions?: RedirectPolicyOptions; retryOptions?: PipelineRetryOptions; telemetryOptions?: TelemetryOptions; tlsOptions?: TlsSettings; - userAgentOptions?: UserAgentOptions; + userAgentOptions?: UserAgentPolicyOptions; } // @public @@ -354,7 +354,7 @@ export type RawHttpHeadersInput = Record; export type RawResponseCallback = (rawResponse: FullOperationResponse, error?: unknown) => void; // @public -export interface RedirectOptions { +export interface RedirectPolicyOptions { maxRetries?: number; } @@ -464,7 +464,7 @@ export interface TypeSpecRuntimeLogger { export function uint8ArrayToString(bytes: Uint8Array, format: EncodingType): string; // @public -export interface UserAgentOptions { +export interface UserAgentPolicyOptions { userAgentPrefix?: string; } diff --git a/sdk/core/ts-http-runtime/src/policies/tracingPolicy.ts b/sdk/core/ts-http-runtime/src/policies/tracingPolicy.ts deleted file mode 100644 index 413183785533..000000000000 --- a/sdk/core/ts-http-runtime/src/policies/tracingPolicy.ts +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -import type { TracingClient, TracingContext, TracingSpan } from "../tracing/interfaces.js"; -import { createTracingClient } from "../tracing/tracingClient.js"; -import { SDK_VERSION } from "../constants.js"; -import type { PipelineRequest, PipelineResponse, SendRequest } from "../interfaces.js"; -import type { PipelinePolicy } from "../pipeline.js"; -import { getUserAgentValue } from "../util/userAgent.js"; -import { logger } from "../log.js"; -import { getErrorMessage, isError } from "../util/error.js"; -import { isRestError } from "../restError.js"; -import { Sanitizer } from "../util/sanitizer.js"; - -/** - * The programmatic identifier of the tracingPolicy. - */ -export const tracingPolicyName = "tracingPolicy"; - -/** - * Options to configure the tracing policy. - */ -export interface TracingPolicyOptions { - /** - * String prefix to add to the user agent logged as metadata - * on the generated Span. - * Defaults to an empty string. - */ - userAgentPrefix?: string; - /** - * Query string names whose values will be logged when logging is enabled. By default no - * query string values are logged. - */ - additionalAllowedQueryParameters?: string[]; -} - -/** - * A simple policy to create OpenTelemetry Spans for each request made by the pipeline - * that has SpanOptions with a parent. - * Requests made without a parent Span will not be recorded. - * @param options - Options to configure the telemetry logged by the tracing policy. - */ -export function tracingPolicy(options: TracingPolicyOptions = {}): PipelinePolicy { - const userAgentPromise = getUserAgentValue(options.userAgentPrefix); - const sanitizer = new Sanitizer({ - additionalAllowedQueryParameters: options.additionalAllowedQueryParameters, - }); - const tracingClient = tryCreateTracingClient(); - - return { - name: tracingPolicyName, - async sendRequest(request: PipelineRequest, next: SendRequest): Promise { - if (!tracingClient) { - return next(request); - } - - const userAgent = await userAgentPromise; - - const spanAttributes = { - "http.url": sanitizer.sanitizeUrl(request.url), - "http.method": request.method, - "http.user_agent": userAgent, - requestId: request.requestId, - }; - if (userAgent) { - spanAttributes["http.user_agent"] = userAgent; - } - - const { span, tracingContext } = tryCreateSpan(tracingClient, request, spanAttributes) ?? {}; - - if (!span || !tracingContext) { - return next(request); - } - - try { - const response = await tracingClient.withContext(tracingContext, next, request); - tryProcessResponse(span, response); - return response; - } catch (err: any) { - tryProcessError(span, err); - throw err; - } - }, - }; -} - -function tryCreateTracingClient(): TracingClient | undefined { - try { - return createTracingClient({ - namespace: "", - packageName: "@typespec/ts-http-runtime", - packageVersion: SDK_VERSION, - }); - } catch (e: unknown) { - logger.warning(`Error when creating the TracingClient: ${getErrorMessage(e)}`); - return undefined; - } -} - -function tryCreateSpan( - tracingClient: TracingClient, - request: PipelineRequest, - spanAttributes: Record, -): { span: TracingSpan; tracingContext: TracingContext } | undefined { - try { - // As per spec, we do not need to differentiate between HTTP and HTTPS in span name. - const { span, updatedOptions } = tracingClient.startSpan( - `HTTP ${request.method}`, - { tracingOptions: request.tracingOptions }, - { - spanKind: "client", - spanAttributes, - }, - ); - - // If the span is not recording, don't do any more work. - if (!span.isRecording()) { - span.end(); - return undefined; - } - - // set headers - const headers = tracingClient.createRequestHeaders( - updatedOptions.tracingOptions.tracingContext, - ); - for (const [key, value] of Object.entries(headers)) { - request.headers.set(key, value); - } - return { span, tracingContext: updatedOptions.tracingOptions.tracingContext }; - } catch (e: any) { - logger.warning(`Skipping creating a tracing span due to an error: ${getErrorMessage(e)}`); - return undefined; - } -} - -function tryProcessError(span: TracingSpan, error: unknown): void { - try { - span.setStatus({ - status: "error", - error: isError(error) ? error : undefined, - }); - if (isRestError(error) && error.statusCode) { - span.setAttribute("http.status_code", error.statusCode); - } - span.end(); - } catch (e: any) { - logger.warning(`Skipping tracing span processing due to an error: ${getErrorMessage(e)}`); - } -} - -function tryProcessResponse(span: TracingSpan, response: PipelineResponse): void { - try { - span.setAttribute("http.status_code", response.status); - const serviceRequestId = response.headers.get("x-ms-request-id"); - if (serviceRequestId) { - span.setAttribute("serviceRequestId", serviceRequestId); - } - // Per semantic conventions, only set the status to error if the status code is 4xx or 5xx. - // Otherwise, the status MUST remain unset. - // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status - if (response.status >= 400) { - span.setStatus({ - status: "error", - }); - } - span.end(); - } catch (e: any) { - logger.warning(`Skipping tracing span processing due to an error: ${getErrorMessage(e)}`); - } -} diff --git a/sdk/core/ts-http-runtime/test/tracingPolicy.spec.ts b/sdk/core/ts-http-runtime/test/tracingPolicy.spec.ts deleted file mode 100644 index 7d9e45d2d8ef..000000000000 --- a/sdk/core/ts-http-runtime/test/tracingPolicy.spec.ts +++ /dev/null @@ -1,324 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -import { describe, it, assert, expect, vi, beforeEach, afterEach, type Mock } from "vitest"; -import type { PipelineRequest, PipelineResponse, SendRequest } from "../src/index.js"; -import { - RestError, - createHttpHeaders, - createPipelineRequest, - tracingPolicy, -} from "../src/index.js"; -import type { - Instrumenter, - InstrumenterSpanOptions, - SpanStatus, - TracingContext, - TracingSpan, - TracingSpanOptions, -} from "../src/tracing/interfaces.js"; -import { useInstrumenter } from "../src/tracing/instrumenter.js"; - -class MockSpan implements TracingSpan { - spanAttributes: Record = {}; - endCalled: boolean = false; - status?: SpanStatus; - exceptions: Array = []; - - constructor( - public name: string, - spanOptions: TracingSpanOptions = {}, - ) { - this.spanAttributes = spanOptions.spanAttributes ?? {}; - } - - isRecording(): boolean { - return true; - } - - recordException(exception: Error | string): void { - this.exceptions.push(exception); - } - - end(): void { - this.endCalled = true; - } - - setStatus(status: SpanStatus): void { - this.status = status; - } - - setAttribute(name: string, value: unknown): void { - this.spanAttributes[name] = value; - } - - getAttribute(name: string): unknown { - return this.spanAttributes[name]; - } -} - -const noopTracingContext: TracingContext = { - deleteValue() { - return this; - }, - getValue() { - return undefined; - }, - setValue() { - return this; - }, -}; - -class MockInstrumenter implements Instrumenter { - lastSpanCreated: MockSpan | undefined; - staticSpan: MockSpan | undefined; - - setStaticSpan(span: MockSpan): void { - this.staticSpan = span; - } - startSpan( - name: string, - spanOptions: InstrumenterSpanOptions, - ): { - span: TracingSpan; - tracingContext: TracingContext; - } { - const tracingContext = spanOptions.tracingContext ?? noopTracingContext; - if (this.staticSpan) { - return { span: this.staticSpan, tracingContext }; - } - const span = new MockSpan(name, spanOptions); - this.lastSpanCreated = span; - return { - span, - tracingContext, - }; - } - withContext< - CallbackArgs extends unknown[], - Callback extends (...args: CallbackArgs) => ReturnType, - >( - _context: TracingContext, - callback: Callback, - ...callbackArgs: CallbackArgs - ): ReturnType { - return callback(...callbackArgs); - } - - parseTraceparentHeader(_traceparentHeader: string): TracingContext | undefined { - return undefined; - } - createRequestHeaders(_tracingContext?: TracingContext): Record { - return {}; - } -} - -describe("tracingPolicy", function () { - let activeInstrumenter: MockInstrumenter; - - function createTestRequest({ noContext = false } = {}): { - request: PipelineRequest; - next: Mock; - } { - const request = createPipelineRequest({ - url: "https://bing.com", - method: "POST", - tracingOptions: { tracingContext: noContext ? undefined : noopTracingContext }, - }); - - const response: PipelineResponse = { - headers: createHttpHeaders(), - request: request, - status: 200, - }; - const next = vi.fn(); - next.mockResolvedValue(response); - return { request, next }; - } - - beforeEach(() => { - activeInstrumenter = new MockInstrumenter(); - useInstrumenter(activeInstrumenter); - }); - - afterEach(() => { - vi.restoreAllMocks(); - }); - - it("will create a span with the correct data", async () => { - const policy = tracingPolicy(); - const { request, next } = createTestRequest(); - await policy.sendRequest(request, next); - - const createdSpan = activeInstrumenter.lastSpanCreated; - assert.exists(createdSpan); - const mockSpan = createdSpan!; - assert.isTrue(mockSpan.endCalled, "expected span to be ended"); - assert.equal(mockSpan.name, "HTTP POST"); - assert.equal(mockSpan.getAttribute("http.method"), "POST"); - assert.equal(mockSpan.getAttribute("http.url"), request.url); - assert.equal(mockSpan.getAttribute("requestId"), request.requestId); - assert.equal(mockSpan.getAttribute("http.status_code"), 200); // createTestRequest's response will return 200 OK - }); - - it("will sanitize URLs", async () => { - const policy = tracingPolicy({ additionalAllowedQueryParameters: ["allowedQueryParam"] }); - const request = createPipelineRequest({ - url: "https://bing.com/search?redactedParam=redactedValue&allowedQueryParam=allowedValue", - tracingOptions: { tracingContext: noopTracingContext }, - }); - - const response: PipelineResponse = { - headers: createHttpHeaders(), - request: request, - status: 200, - }; - const next = vi.fn(); - next.mockResolvedValue(response); - - await policy.sendRequest(request, next); - const createdSpan = activeInstrumenter.lastSpanCreated; - if (!createdSpan) { - assert.fail("expected span to be created"); - } - - const spanUrlValue = new URL(createdSpan.getAttribute("http.url") as string); - assert.equal(spanUrlValue.searchParams.get("redactedParam"), "REDACTED"); - assert.equal(spanUrlValue.searchParams.get("allowedQueryParam"), "allowedValue"); - }); - - it("will set request headers correctly", async () => { - vi.spyOn(activeInstrumenter, "createRequestHeaders").mockReturnValue({ - testheader: "testvalue", - }); - const { request, next } = createTestRequest(); - - const policy = tracingPolicy(); - await policy.sendRequest(request, next); - assert.equal(request.headers.get("testheader"), "testvalue"); - }); - - it("will close a span if an error is encountered", async () => { - const request = createPipelineRequest({ - url: "https://bing.com", - tracingOptions: { - tracingContext: noopTracingContext, - }, - }); - - const policy = tracingPolicy(); - const next = vi.fn(); - const requestError = new RestError("Bad Request.", { statusCode: 400 }); - next.mockRejectedValue(requestError); - - await expect(policy.sendRequest(request, next)).rejects.toThrow(requestError); - const createdSpan = activeInstrumenter.lastSpanCreated; - assert.exists(createdSpan); - const mockSpan = createdSpan!; - assert.equal(mockSpan.status?.status, "error"); - if (mockSpan.status?.status === "error") { - assert.equal(mockSpan.status?.error, requestError); - } - assert.isTrue(mockSpan.endCalled, "end was expected to be called!"); - assert.equal(mockSpan.getAttribute("http.status_code"), 400); - }); - - it("will create a span even if tracingContext is missing", async () => { - const policy = tracingPolicy(); - const { request, next } = createTestRequest({ noContext: true }); - await policy.sendRequest(request, next); - - const createdSpan = activeInstrumenter.lastSpanCreated; - assert.exists(createdSpan); - }); - - describe("HTTP status codes", () => { - const data = [ - { - statusCode: 100, - expectedSpanStatus: undefined, - }, - { - statusCode: 201, - expectedSpanStatus: undefined, - }, - { - statusCode: 302, - expectedSpanStatus: undefined, - }, - { - statusCode: 400, - expectedSpanStatus: "error", - }, - { - statusCode: 500, - expectedSpanStatus: "error", - }, - ]; - - for (const { statusCode, expectedSpanStatus } of data) { - it(`will set the span status to ${expectedSpanStatus} for a status code of ${statusCode}`, async () => { - const request = createPipelineRequest({ - url: "https://bing.com", - tracingOptions: { - tracingContext: noopTracingContext, - }, - }); - - const response = { - headers: createHttpHeaders(), - request: request, - status: statusCode, - bodyAsText: JSON.stringify({}), - }; - - const policy = tracingPolicy(); - const next = vi.fn(); - next.mockResolvedValue(response); - - await policy.sendRequest(request, next); - const createdSpan = activeInstrumenter.lastSpanCreated; - assert.exists(createdSpan); - assert.equal(createdSpan?.status?.status, expectedSpanStatus); - }); - } - }); - - describe("span errors", () => { - it("will not fail the request when creating a span throws", async () => { - vi.spyOn(activeInstrumenter, "startSpan").mockImplementation(() => { - throw "boom"; - }); - const { request, next } = createTestRequest(); - const policy = tracingPolicy(); - - await expect(policy.sendRequest(request, next)).resolves; - }); - - it("will not fail the request when post-processing success fails", async () => { - const mockSpan = new MockSpan("mock"); - vi.spyOn(mockSpan, "end").mockImplementation(() => { - throw new Error("end is not a function"); - }); - activeInstrumenter.setStaticSpan(mockSpan); - const { request, next } = createTestRequest(); - const policy = tracingPolicy(); - - await expect(policy.sendRequest(request, next)).resolves; - }); - - it("will not fail the request when post-processing error fails", async () => { - const mockSpan = new MockSpan("mock"); - vi.spyOn(mockSpan, "end").mockImplementation(() => { - throw new Error("end is not a function"); - }); - const { request, next } = createTestRequest(); - const policy = tracingPolicy(); - const expectedError = new RestError("Bad Request.", { statusCode: 400 }); - next.mockRejectedValue(expectedError); - - // Expect the pipeline request error, _not_ the error that is thrown when ending a span. - await expect(policy.sendRequest(request, next)).rejects.toThrow(expectedError); - }); - }); -}); From 510866ee20c7bb925aef6fd39ef1864e65d92966 Mon Sep 17 00:00:00 2001 From: Timo van Veenendaal Date: Mon, 2 Dec 2024 16:20:26 -0800 Subject: [PATCH 08/11] release date! --- sdk/core/ts-http-runtime/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/core/ts-http-runtime/CHANGELOG.md b/sdk/core/ts-http-runtime/CHANGELOG.md index 7d2c6ad76e20..2b66a10fa515 100644 --- a/sdk/core/ts-http-runtime/CHANGELOG.md +++ b/sdk/core/ts-http-runtime/CHANGELOG.md @@ -1,6 +1,6 @@ # Release History -## 0.1.0 (Unreleased) +## 0.1.0 (2024-12-03) ### Features Added From 612e1fe239f55a5e7540ce84e78be58ec3352ba0 Mon Sep 17 00:00:00 2001 From: Timo van Veenendaal Date: Mon, 2 Dec 2024 16:21:44 -0800 Subject: [PATCH 09/11] Update CHANGELOG --- sdk/core/ts-http-runtime/CHANGELOG.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/sdk/core/ts-http-runtime/CHANGELOG.md b/sdk/core/ts-http-runtime/CHANGELOG.md index 2b66a10fa515..07f636e62bf4 100644 --- a/sdk/core/ts-http-runtime/CHANGELOG.md +++ b/sdk/core/ts-http-runtime/CHANGELOG.md @@ -5,9 +5,3 @@ ### Features Added - Initial release of the `@typespec/ts-http-runtime` package. - -### Breaking Changes - -### Bugs Fixed - -### Other Changes From 63567da065107e991ef74a421c49d255bcacca0c Mon Sep 17 00:00:00 2001 From: Timo van Veenendaal Date: Mon, 2 Dec 2024 16:42:14 -0800 Subject: [PATCH 10/11] Add a couple more helpers that got missed --- sdk/core/ts-http-runtime/review/ts-http-runtime.api.md | 9 +++++++++ sdk/core/ts-http-runtime/src/index.ts | 2 ++ 2 files changed, 11 insertions(+) diff --git a/sdk/core/ts-http-runtime/review/ts-http-runtime.api.md b/sdk/core/ts-http-runtime/review/ts-http-runtime.api.md index d3c8cf43be85..c744d16f8c16 100644 --- a/sdk/core/ts-http-runtime/review/ts-http-runtime.api.md +++ b/sdk/core/ts-http-runtime/review/ts-http-runtime.api.md @@ -82,6 +82,12 @@ export function createHttpHeaders(rawHeaders?: RawHttpHeadersInput): HttpHeaders // @public export function createPipelineRequest(options: PipelineRequestOptions): PipelineRequest; +// @public +export function createRestError(response: PathUncheckedResponse): RestError; + +// @public +export function createRestError(message: string, response: PathUncheckedResponse): RestError; + // @public export interface Debugger { (...args: any[]): void; @@ -204,6 +210,9 @@ export interface OperationOptions { requestOptions?: OperationRequestOptions; } +// @public +export function operationOptionsToRequestParameters(options: OperationOptions): RequestParameters; + // @public export interface OperationRequestOptions { allowInsecureConnection?: boolean; diff --git a/sdk/core/ts-http-runtime/src/index.ts b/sdk/core/ts-http-runtime/src/index.ts index b773fa2ac381..deb427945738 100644 --- a/sdk/core/ts-http-runtime/src/index.ts +++ b/sdk/core/ts-http-runtime/src/index.ts @@ -54,6 +54,8 @@ export type { Pipeline, PipelinePolicy, AddPolicyOptions, PipelinePhase } from " export { RestError, isRestError, type RestErrorOptions } from "./restError.js"; export { stringToUint8Array, uint8ArrayToString, type EncodingType } from "./util/bytesEncoding.js"; export { getClient } from "./client/getClient.js"; +export { operationOptionsToRequestParameters } from "./client/operationOptionHelpers.js"; +export { createRestError } from "./client/restError.js"; export type { Client, ClientOptions, From 06d83ed163cb6dd7933290a980a1dec3d7a70b49 Mon Sep 17 00:00:00 2001 From: Timo van Veenendaal Date: Mon, 2 Dec 2024 16:57:01 -0800 Subject: [PATCH 11/11] Update diff --- sdk/core/ts-http-runtime/review/azure-core-comparison.diff | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sdk/core/ts-http-runtime/review/azure-core-comparison.diff b/sdk/core/ts-http-runtime/review/azure-core-comparison.diff index 5f8e382580ba..4c153e485177 100644 --- a/sdk/core/ts-http-runtime/review/azure-core-comparison.diff +++ b/sdk/core/ts-http-runtime/review/azure-core-comparison.diff @@ -904,10 +904,10 @@ index e9751e2..e4f8769 100644 HttpClient, HttpHeaders as PipelineHeaders, diff --git a/src/index.ts b/src/index.ts -index 688a7ea..b773fa2 100644 +index 688a7ea..deb4279 100644 --- a/src/index.ts +++ b/src/index.ts -@@ -9,117 +9,71 @@ declare global { +@@ -9,117 +9,73 @@ declare global { interface ReadableStream {} interface TransformStream {} } @@ -1016,6 +1016,8 @@ index 688a7ea..b773fa2 100644 +export { RestError, isRestError, type RestErrorOptions } from "./restError.js"; +export { stringToUint8Array, uint8ArrayToString, type EncodingType } from "./util/bytesEncoding.js"; +export { getClient } from "./client/getClient.js"; ++export { operationOptionsToRequestParameters } from "./client/operationOptionHelpers.js"; ++export { createRestError } from "./client/restError.js"; export type { - RetryStrategy, - RetryInformation,