279 lines
11 KiB
JavaScript
279 lines
11 KiB
JavaScript
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
import { convertHttpClient, createRequestPolicyFactoryPolicy, } from "@azure/core-http-compat";
|
|
import { bearerTokenAuthenticationPolicy, decompressResponsePolicyName, } from "@azure/core-rest-pipeline";
|
|
import { authorizeRequestOnTenantChallenge, createClientPipeline } from "@azure/core-client";
|
|
import { parseXML, stringifyXML } from "@azure/core-xml";
|
|
import { isTokenCredential } from "@azure/core-auth";
|
|
import { logger } from "./log.js";
|
|
import { StorageRetryPolicyFactory } from "./StorageRetryPolicyFactory.js";
|
|
import { StorageSharedKeyCredential } from "./credentials/StorageSharedKeyCredential.js";
|
|
import { AnonymousCredential } from "./credentials/AnonymousCredential.js";
|
|
import { StorageOAuthScopes, StorageBlobLoggingAllowedHeaderNames, StorageBlobLoggingAllowedQueryParameters, SDK_VERSION, } from "./utils/constants.js";
|
|
import { getCachedDefaultHttpClient, storageRequestFailureDetailsParserPolicy, } from "@azure/storage-common";
|
|
import { storageBrowserPolicy } from "./policies/StorageBrowserPolicyV2.js";
|
|
import { storageRetryPolicy } from "./policies/StorageRetryPolicyV2.js";
|
|
import { storageSharedKeyCredentialPolicy } from "./policies/StorageSharedKeyCredentialPolicyV2.js";
|
|
import { StorageBrowserPolicyFactory } from "./StorageBrowserPolicyFactory.js";
|
|
import { storageCorrectContentLengthPolicy } from "./policies/StorageCorrectContentLengthPolicy.js";
|
|
// Export following interfaces and types for customers who want to implement their
|
|
// own RequestPolicy or HTTPClient
|
|
export { StorageOAuthScopes, };
|
|
/**
|
|
* A helper to decide if a given argument satisfies the Pipeline contract
|
|
* @param pipeline - An argument that may be a Pipeline
|
|
* @returns true when the argument satisfies the Pipeline contract
|
|
*/
|
|
export function isPipelineLike(pipeline) {
|
|
if (!pipeline || typeof pipeline !== "object") {
|
|
return false;
|
|
}
|
|
const castPipeline = pipeline;
|
|
return (Array.isArray(castPipeline.factories) &&
|
|
typeof castPipeline.options === "object" &&
|
|
typeof castPipeline.toServiceClientOptions === "function");
|
|
}
|
|
/**
|
|
* A Pipeline class containing HTTP request policies.
|
|
* You can create a default Pipeline by calling {@link newPipeline}.
|
|
* Or you can create a Pipeline with your own policies by the constructor of Pipeline.
|
|
*
|
|
* Refer to {@link newPipeline} and provided policies before implementing your
|
|
* customized Pipeline.
|
|
*/
|
|
export class Pipeline {
|
|
/**
|
|
* A list of chained request policy factories.
|
|
*/
|
|
factories;
|
|
/**
|
|
* Configures pipeline logger and HTTP client.
|
|
*/
|
|
options;
|
|
/**
|
|
* Creates an instance of Pipeline. Customize HTTPClient by implementing IHttpClient interface.
|
|
*
|
|
* @param factories -
|
|
* @param options -
|
|
*/
|
|
constructor(factories, options = {}) {
|
|
this.factories = factories;
|
|
this.options = options;
|
|
}
|
|
/**
|
|
* Transfer Pipeline object to ServiceClientOptions object which is required by
|
|
* ServiceClient constructor.
|
|
*
|
|
* @returns The ServiceClientOptions object from this Pipeline.
|
|
*/
|
|
toServiceClientOptions() {
|
|
return {
|
|
httpClient: this.options.httpClient,
|
|
requestPolicyFactories: this.factories,
|
|
};
|
|
}
|
|
}
|
|
/**
|
|
* Creates a new Pipeline object with Credential provided.
|
|
*
|
|
* @param credential - Such as AnonymousCredential, StorageSharedKeyCredential or any credential from the `@azure/identity` package to authenticate requests to the service. You can also provide an object that implements the TokenCredential interface. If not specified, AnonymousCredential is used.
|
|
* @param pipelineOptions - Optional. Options.
|
|
* @returns A new Pipeline object.
|
|
*/
|
|
export function newPipeline(credential, pipelineOptions = {}) {
|
|
if (!credential) {
|
|
credential = new AnonymousCredential();
|
|
}
|
|
const pipeline = new Pipeline([], pipelineOptions);
|
|
pipeline._credential = credential;
|
|
return pipeline;
|
|
}
|
|
function processDownlevelPipeline(pipeline) {
|
|
const knownFactoryFunctions = [
|
|
isAnonymousCredential,
|
|
isStorageSharedKeyCredential,
|
|
isCoreHttpBearerTokenFactory,
|
|
isStorageBrowserPolicyFactory,
|
|
isStorageRetryPolicyFactory,
|
|
isStorageTelemetryPolicyFactory,
|
|
isCoreHttpPolicyFactory,
|
|
];
|
|
if (pipeline.factories.length) {
|
|
const novelFactories = pipeline.factories.filter((factory) => {
|
|
return !knownFactoryFunctions.some((knownFactory) => knownFactory(factory));
|
|
});
|
|
if (novelFactories.length) {
|
|
const hasInjector = novelFactories.some((factory) => isInjectorPolicyFactory(factory));
|
|
// if there are any left over, wrap in a requestPolicyFactoryPolicy
|
|
return {
|
|
wrappedPolicies: createRequestPolicyFactoryPolicy(novelFactories),
|
|
afterRetry: hasInjector,
|
|
};
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
export function getCoreClientOptions(pipeline) {
|
|
const { httpClient: v1Client, ...restOptions } = pipeline.options;
|
|
let httpClient = pipeline._coreHttpClient;
|
|
if (!httpClient) {
|
|
httpClient = v1Client ? convertHttpClient(v1Client) : getCachedDefaultHttpClient();
|
|
pipeline._coreHttpClient = httpClient;
|
|
}
|
|
let corePipeline = pipeline._corePipeline;
|
|
if (!corePipeline) {
|
|
const packageDetails = `azsdk-js-azure-storage-blob/${SDK_VERSION}`;
|
|
const userAgentPrefix = restOptions.userAgentOptions && restOptions.userAgentOptions.userAgentPrefix
|
|
? `${restOptions.userAgentOptions.userAgentPrefix} ${packageDetails}`
|
|
: `${packageDetails}`;
|
|
corePipeline = createClientPipeline({
|
|
...restOptions,
|
|
loggingOptions: {
|
|
additionalAllowedHeaderNames: StorageBlobLoggingAllowedHeaderNames,
|
|
additionalAllowedQueryParameters: StorageBlobLoggingAllowedQueryParameters,
|
|
logger: logger.info,
|
|
},
|
|
userAgentOptions: {
|
|
userAgentPrefix,
|
|
},
|
|
serializationOptions: {
|
|
stringifyXML,
|
|
serializerOptions: {
|
|
xml: {
|
|
// Use customized XML char key of "#" so we can deserialize metadata
|
|
// with "_" key
|
|
xmlCharKey: "#",
|
|
},
|
|
},
|
|
},
|
|
deserializationOptions: {
|
|
parseXML,
|
|
serializerOptions: {
|
|
xml: {
|
|
// Use customized XML char key of "#" so we can deserialize metadata
|
|
// with "_" key
|
|
xmlCharKey: "#",
|
|
},
|
|
},
|
|
},
|
|
});
|
|
corePipeline.removePolicy({ phase: "Retry" });
|
|
corePipeline.removePolicy({ name: decompressResponsePolicyName });
|
|
corePipeline.addPolicy(storageCorrectContentLengthPolicy());
|
|
corePipeline.addPolicy(storageRetryPolicy(restOptions.retryOptions), { phase: "Retry" });
|
|
corePipeline.addPolicy(storageRequestFailureDetailsParserPolicy());
|
|
corePipeline.addPolicy(storageBrowserPolicy());
|
|
const downlevelResults = processDownlevelPipeline(pipeline);
|
|
if (downlevelResults) {
|
|
corePipeline.addPolicy(downlevelResults.wrappedPolicies, downlevelResults.afterRetry ? { afterPhase: "Retry" } : undefined);
|
|
}
|
|
const credential = getCredentialFromPipeline(pipeline);
|
|
if (isTokenCredential(credential)) {
|
|
corePipeline.addPolicy(bearerTokenAuthenticationPolicy({
|
|
credential,
|
|
scopes: restOptions.audience ?? StorageOAuthScopes,
|
|
challengeCallbacks: { authorizeRequestOnChallenge: authorizeRequestOnTenantChallenge },
|
|
}), { phase: "Sign" });
|
|
}
|
|
else if (credential instanceof StorageSharedKeyCredential) {
|
|
corePipeline.addPolicy(storageSharedKeyCredentialPolicy({
|
|
accountName: credential.accountName,
|
|
accountKey: credential.accountKey,
|
|
}), { phase: "Sign" });
|
|
}
|
|
pipeline._corePipeline = corePipeline;
|
|
}
|
|
return {
|
|
...restOptions,
|
|
allowInsecureConnection: true,
|
|
httpClient,
|
|
pipeline: corePipeline,
|
|
};
|
|
}
|
|
export function getCredentialFromPipeline(pipeline) {
|
|
// see if we squirreled one away on the type itself
|
|
if (pipeline._credential) {
|
|
return pipeline._credential;
|
|
}
|
|
// if it came from another package, loop over the factories and look for one like before
|
|
let credential = new AnonymousCredential();
|
|
for (const factory of pipeline.factories) {
|
|
if (isTokenCredential(factory.credential)) {
|
|
// Only works if the factory has been attached a "credential" property.
|
|
// We do that in newPipeline() when using TokenCredential.
|
|
credential = factory.credential;
|
|
}
|
|
else if (isStorageSharedKeyCredential(factory)) {
|
|
return factory;
|
|
}
|
|
}
|
|
return credential;
|
|
}
|
|
function isStorageSharedKeyCredential(factory) {
|
|
if (factory instanceof StorageSharedKeyCredential) {
|
|
return true;
|
|
}
|
|
return factory.constructor.name === "StorageSharedKeyCredential";
|
|
}
|
|
function isAnonymousCredential(factory) {
|
|
if (factory instanceof AnonymousCredential) {
|
|
return true;
|
|
}
|
|
return factory.constructor.name === "AnonymousCredential";
|
|
}
|
|
function isCoreHttpBearerTokenFactory(factory) {
|
|
return isTokenCredential(factory.credential);
|
|
}
|
|
function isStorageBrowserPolicyFactory(factory) {
|
|
if (factory instanceof StorageBrowserPolicyFactory) {
|
|
return true;
|
|
}
|
|
return factory.constructor.name === "StorageBrowserPolicyFactory";
|
|
}
|
|
function isStorageRetryPolicyFactory(factory) {
|
|
if (factory instanceof StorageRetryPolicyFactory) {
|
|
return true;
|
|
}
|
|
return factory.constructor.name === "StorageRetryPolicyFactory";
|
|
}
|
|
function isStorageTelemetryPolicyFactory(factory) {
|
|
return factory.constructor.name === "TelemetryPolicyFactory";
|
|
}
|
|
function isInjectorPolicyFactory(factory) {
|
|
return factory.constructor.name === "InjectorPolicyFactory";
|
|
}
|
|
function isCoreHttpPolicyFactory(factory) {
|
|
const knownPolicies = [
|
|
"GenerateClientRequestIdPolicy",
|
|
"TracingPolicy",
|
|
"LogPolicy",
|
|
"ProxyPolicy",
|
|
"DisableResponseDecompressionPolicy",
|
|
"KeepAlivePolicy",
|
|
"DeserializationPolicy",
|
|
];
|
|
const mockHttpClient = {
|
|
sendRequest: async (request) => {
|
|
return {
|
|
request,
|
|
headers: request.headers.clone(),
|
|
status: 500,
|
|
};
|
|
},
|
|
};
|
|
const mockRequestPolicyOptions = {
|
|
log(_logLevel, _message) {
|
|
/* do nothing */
|
|
},
|
|
shouldLog(_logLevel) {
|
|
return false;
|
|
},
|
|
};
|
|
const policyInstance = factory.create(mockHttpClient, mockRequestPolicyOptions);
|
|
const policyName = policyInstance.constructor.name;
|
|
// bundlers sometimes add a custom suffix to the class name to make it unique
|
|
return knownPolicies.some((knownPolicyName) => {
|
|
return policyName.startsWith(knownPolicyName);
|
|
});
|
|
}
|
|
//# sourceMappingURL=Pipeline.js.map
|