@azure/storage-blob
Version:
Microsoft Azure Storage SDK for JavaScript - Blob
279 lines • 11.4 kB
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