Core AMQP cleanup for v2 (#12210)
This commit is contained in:
Родитель
937f4b5012
Коммит
54247fdd1b
|
@ -4,8 +4,39 @@
|
|||
|
||||
- `AmqpAnnotatedMessage` interface that closely represents the AMQP annotated message from the [AMQP spec](https://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#section-message-format) has been added. New `AmqpMessageHeaders` and `AmqpMessageProperties` interfaces(properties with camelCasing) have been added in the place of re-exports from "rhea" library(properties with snake_casing).
|
||||
[PR 12091](https://github.com/Azure/azure-sdk-for-js/pull/12091)
|
||||
- `Message` from "rhea" library which was being exported as `AmqpMessage` is removed.
|
||||
[PR 12169](https://github.com/Azure/azure-sdk-for-js/pull/12169)
|
||||
|
||||
### Breaking changes
|
||||
|
||||
We are cleaning the public API surface by
|
||||
|
||||
- removing exports that are either not used by either `@azure/event-hubs` and `@azure/service-bus` packages (which are the two main consumers of this package)
|
||||
- AsyncLockOptions
|
||||
- executePromisesSequentially
|
||||
- Func
|
||||
- getNewAsyncLock
|
||||
- isNode
|
||||
- randomNumberFromInterval
|
||||
- Timeout
|
||||
- moving the clases/methods/interfaces that are very specific to Event Hubs/Service Bus to their corresponding packages.
|
||||
- SharedKeyCredential
|
||||
- As part of this move the `negotiateClaim()` method now takes the token string directly instead of the `AccessToken` object.
|
||||
- EventHubConnectionConfig
|
||||
- avoid re-exporting things from `rhea-promise` and `@azure/core-auth`
|
||||
- Dictionary
|
||||
- isAmqpError
|
||||
- Message
|
||||
- TokenCredential
|
||||
- isTokenCredential
|
||||
- AccessToken
|
||||
- removing all IotHub related artifacts. These existed to support the IotHub support we had in Event Hubs v2 which has since been removed in Event Hubs v5 for a better separation of concerns
|
||||
- IotHubConnectionConfig
|
||||
- IotHubConnectionStringModel
|
||||
- IotSharedKeyCredential
|
||||
- isIotHubConnectionString
|
||||
- removing all Event Hubs, Storage and Service Bus interfaces meant to be used with the `parseConnectionString()` method
|
||||
- ServiceBusConnectionStringModel
|
||||
- StorageConnectionStringModel
|
||||
- EventHubsConnectionStringModel
|
||||
|
||||
## 1.1.7 (2020-10-28)
|
||||
|
||||
|
|
|
@ -69,7 +69,6 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@azure/abort-controller": "^1.0.0",
|
||||
"@azure/core-auth": "^1.1.3",
|
||||
"@azure/logger": "^1.0.0",
|
||||
"@types/async-lock": "^1.1.0",
|
||||
"@types/is-buffer": "^2.0.0",
|
||||
|
|
|
@ -5,13 +5,9 @@
|
|||
```ts
|
||||
|
||||
import { AbortSignalLike } from '@azure/abort-controller';
|
||||
import { AccessToken } from '@azure/core-auth';
|
||||
import { AmqpError } from 'rhea-promise';
|
||||
import AsyncLock from 'async-lock';
|
||||
import { Connection } from 'rhea-promise';
|
||||
import { Dictionary } from 'rhea-promise';
|
||||
import { isAmqpError } from 'rhea-promise';
|
||||
import { isTokenCredential } from '@azure/core-auth';
|
||||
import { Message } from 'rhea-promise';
|
||||
import { MessageHeader } from 'rhea-promise';
|
||||
import { MessageProperties } from 'rhea-promise';
|
||||
|
@ -21,11 +17,8 @@ import { ReqResLink } from 'rhea-promise';
|
|||
import { Sender } from 'rhea-promise';
|
||||
import { SenderOptions } from 'rhea-promise';
|
||||
import { Session } from 'rhea-promise';
|
||||
import { TokenCredential } from '@azure/core-auth';
|
||||
import { WebSocketImpl } from 'rhea-promise';
|
||||
|
||||
export { AccessToken }
|
||||
|
||||
// @public
|
||||
export interface AmqpAnnotatedMessage {
|
||||
applicationProperties?: {
|
||||
|
@ -89,14 +82,6 @@ export const AmqpMessageProperties: {
|
|||
|
||||
export { AsyncLock }
|
||||
|
||||
// @public
|
||||
export interface AsyncLockOptions {
|
||||
domainReentrant?: boolean;
|
||||
maxPending?: number;
|
||||
Promise?: any;
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
// @public
|
||||
export class CbsClient {
|
||||
constructor(connection: Connection, connectionLock: string);
|
||||
|
@ -106,7 +91,7 @@ export class CbsClient {
|
|||
readonly connectionLock: string;
|
||||
readonly endpoint: string;
|
||||
init(): Promise<void>;
|
||||
negotiateClaim(audience: string, tokenObject: AccessToken, tokenType: TokenType): Promise<CbsResponse>;
|
||||
negotiateClaim(audience: string, token: string, tokenType: TokenType): Promise<CbsResponse>;
|
||||
remove(): void;
|
||||
readonly replyTo: string;
|
||||
}
|
||||
|
@ -250,7 +235,6 @@ export interface ConnectionContextBase {
|
|||
dataTransformer: DataTransformer;
|
||||
negotiateClaimLock: string;
|
||||
refreshConnection: () => void;
|
||||
readonly tokenCredential: SharedKeyCredential | TokenCredential;
|
||||
wasConnectionCloseCalled: boolean;
|
||||
}
|
||||
|
||||
|
@ -392,7 +376,6 @@ export interface CreateConnectionContextBaseParameters {
|
|||
dataTransformer?: DataTransformer;
|
||||
isEntityPathRequired?: boolean;
|
||||
operationTimeoutInMs?: number;
|
||||
tokenCredential?: SharedKeyCredential | TokenCredential;
|
||||
}
|
||||
|
||||
// @public
|
||||
|
@ -413,8 +396,6 @@ export const defaultLock: AsyncLock;
|
|||
// @public
|
||||
export function delay<T>(delayInMs: number, abortSignal?: AbortSignalLike, abortErrorMsg?: string, value?: T): Promise<T>;
|
||||
|
||||
export { Dictionary }
|
||||
|
||||
// @public
|
||||
export enum ErrorNameConditionMapper {
|
||||
AddressAlreadyInUseError = "com.microsoft:address-already-in-use",
|
||||
|
@ -464,98 +445,12 @@ export enum ErrorNameConditionMapper {
|
|||
UnauthorizedError = "amqp:unauthorized-access"
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface EventHubConnectionConfig extends ConnectionConfig {
|
||||
entityPath: string;
|
||||
getManagementAddress(): string;
|
||||
getManagementAudience(): string;
|
||||
getReceiverAddress(partitionId: string | number, consumergroup?: string): string;
|
||||
getReceiverAudience(partitionId: string | number, consumergroup?: string): string;
|
||||
getSenderAddress(partitionId?: string | number): string;
|
||||
getSenderAudience(partitionId?: string | number): string;
|
||||
}
|
||||
|
||||
// @public
|
||||
export const EventHubConnectionConfig: {
|
||||
create(connectionString: string, path?: string | undefined): EventHubConnectionConfig;
|
||||
createFromConnectionConfig(config: ConnectionConfig): EventHubConnectionConfig;
|
||||
validate(config: EventHubConnectionConfig): void;
|
||||
};
|
||||
|
||||
// @public
|
||||
export interface EventHubConnectionStringModel {
|
||||
// (undocumented)
|
||||
[x: string]: any;
|
||||
// (undocumented)
|
||||
Endpoint: string;
|
||||
// (undocumented)
|
||||
EntityPath?: string;
|
||||
// (undocumented)
|
||||
SharedAccessKey: string;
|
||||
// (undocumented)
|
||||
SharedAccessKeyName: string;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export function executePromisesSequentially(promiseFactories: Array<any>, kickstart?: any): Promise<any>;
|
||||
|
||||
// @public
|
||||
export type Func<T, V> = (a: T) => V;
|
||||
|
||||
// @public
|
||||
export function getNewAsyncLock(options?: AsyncLockOptions): AsyncLock;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface IotHubConnectionConfig {
|
||||
connectionString: string;
|
||||
deviceId?: string;
|
||||
entityPath: string;
|
||||
host: string;
|
||||
hostName: string;
|
||||
sharedAccessKey: string;
|
||||
sharedAccessKeyName: string;
|
||||
}
|
||||
|
||||
// @public
|
||||
export const IotHubConnectionConfig: {
|
||||
create(connectionString: string, path?: string | undefined): IotHubConnectionConfig;
|
||||
validate(config: IotHubConnectionConfig): void;
|
||||
convertToEventHubConnectionConfig(iotHubConfig: IotHubConnectionConfig): EventHubConnectionConfig;
|
||||
};
|
||||
|
||||
// @public
|
||||
export interface IotHubConnectionStringModel {
|
||||
// (undocumented)
|
||||
DeviceId?: string;
|
||||
// (undocumented)
|
||||
HostName: string;
|
||||
// (undocumented)
|
||||
SharedAccessKey: string;
|
||||
// (undocumented)
|
||||
SharedAccessKeyName: string;
|
||||
}
|
||||
|
||||
// @public
|
||||
export class IotSharedKeyCredential extends SharedKeyCredential {
|
||||
getToken(audience: string): AccessToken;
|
||||
}
|
||||
|
||||
export { isAmqpError }
|
||||
|
||||
// @public
|
||||
export function isIotHubConnectionString(connectionString: string): boolean;
|
||||
|
||||
// @public
|
||||
export function isMessagingError(error: Error | MessagingError): error is MessagingError;
|
||||
|
||||
// @public
|
||||
export const isNode: boolean;
|
||||
|
||||
// @public
|
||||
export function isSystemError(err: any): err is NetworkSystemError;
|
||||
|
||||
export { isTokenCredential }
|
||||
|
||||
// @public
|
||||
export const logger: import("@azure/logger").AzureLogger;
|
||||
|
||||
|
@ -605,9 +500,6 @@ export type ParsedOutput<T> = {
|
|||
[P in keyof T]: T[P];
|
||||
};
|
||||
|
||||
// @public
|
||||
export function randomNumberFromInterval(min: number, max: number): number;
|
||||
|
||||
// @public
|
||||
export class RequestResponseLink implements ReqResLink {
|
||||
constructor(session: Session, sender: Sender, receiver: Receiver);
|
||||
|
@ -685,44 +577,6 @@ export interface SendRequestOptions {
|
|||
timeoutInMs?: number;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface ServiceBusConnectionStringModel {
|
||||
// (undocumented)
|
||||
[x: string]: any;
|
||||
// (undocumented)
|
||||
Endpoint: string;
|
||||
// (undocumented)
|
||||
EntityPath?: string;
|
||||
// (undocumented)
|
||||
SharedAccessKey: string;
|
||||
// (undocumented)
|
||||
SharedAccessKeyName: string;
|
||||
}
|
||||
|
||||
// @public
|
||||
export class SharedKeyCredential {
|
||||
constructor(keyName: string, key: string);
|
||||
protected _createToken(expiry: number, audience: string, hashInput?: string | Buffer): AccessToken;
|
||||
static fromConnectionString(connectionString: string): SharedKeyCredential;
|
||||
getToken(audience: string): AccessToken;
|
||||
key: string;
|
||||
keyName: string;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface StorageConnectionStringModel {
|
||||
// (undocumented)
|
||||
[x: string]: any;
|
||||
// (undocumented)
|
||||
AccountKey: string;
|
||||
// (undocumented)
|
||||
AccountName: string;
|
||||
// (undocumented)
|
||||
DefaultEndpointsProtocol: string;
|
||||
// (undocumented)
|
||||
EndpointSuffix: string;
|
||||
}
|
||||
|
||||
// @public
|
||||
export enum SystemErrorConditionMapper {
|
||||
// (undocumented)
|
||||
|
@ -747,22 +601,6 @@ export enum SystemErrorConditionMapper {
|
|||
ETIMEDOUT = "com.microsoft:timeout"
|
||||
}
|
||||
|
||||
// @public
|
||||
export class Timeout {
|
||||
// (undocumented)
|
||||
clear(): void;
|
||||
// (undocumented)
|
||||
set<T>(t: number, value?: T): Promise<T>;
|
||||
// (undocumented)
|
||||
static set<T>(t: number, value?: T): Promise<T>;
|
||||
// (undocumented)
|
||||
wrap<T>(promise: Promise<T>, t: number, value?: T): Promise<T>;
|
||||
// (undocumented)
|
||||
static wrap<T>(promise: Promise<T>, t: number, value?: T): Promise<T>;
|
||||
}
|
||||
|
||||
export { TokenCredential }
|
||||
|
||||
// @public
|
||||
export enum TokenType {
|
||||
CbsTokenTypeJwt = "jwt",
|
||||
|
|
|
@ -4,9 +4,7 @@
|
|||
import { Connection, ConnectionOptions, generate_uuid } from "rhea-promise";
|
||||
import { CbsClient } from "./cbs";
|
||||
import { DataTransformer, DefaultDataTransformer } from "./dataTransformer";
|
||||
import { TokenCredential } from "@azure/core-auth";
|
||||
import { ConnectionConfig } from "./connectionConfig/connectionConfig";
|
||||
import { SharedKeyCredential } from "./auth/sas";
|
||||
|
||||
import { Constants } from "./util/constants";
|
||||
import { getFrameworkInfo, getPlatformInfo } from "./util/runtimeInfo";
|
||||
|
@ -32,11 +30,6 @@ export interface ConnectionContextBase {
|
|||
* acquire the lock for negotiating cbs claim by an entity on that connection.
|
||||
*/
|
||||
negotiateClaimLock: string;
|
||||
/**
|
||||
* @property {SharedKeyCredential | TokenCredential} tokenCredential The credential to be used for getting tokens
|
||||
* for authentication for the EventHub client.
|
||||
*/
|
||||
readonly tokenCredential: SharedKeyCredential | TokenCredential;
|
||||
/**
|
||||
* @property {Connection} connection The underlying AMQP connection.
|
||||
*/
|
||||
|
@ -102,11 +95,6 @@ export interface CreateConnectionContextBaseParameters {
|
|||
* the AMQP connection.
|
||||
*/
|
||||
connectionProperties: ConnectionProperties;
|
||||
/**
|
||||
* @property {SharedKeyCredential | TokenCredential} [tokenCredential] The credential to be used for Authentication.
|
||||
* Default value: SharedKeyCredentials.
|
||||
*/
|
||||
tokenCredential?: SharedKeyCredential | TokenCredential;
|
||||
/**
|
||||
* @property {DataTransformer} [dataTransformer] The datatransformer to be used for encoding and
|
||||
* decoding messages. Default value: DefaultDataTransformer
|
||||
|
@ -190,12 +178,6 @@ export const ConnectionContextBase = {
|
|||
connectionId: connection.id,
|
||||
cbsSession: new CbsClient(connection, connectionLock),
|
||||
config: parameters.config,
|
||||
tokenCredential:
|
||||
parameters.tokenCredential ||
|
||||
new SharedKeyCredential(
|
||||
parameters.config.sharedAccessKeyName,
|
||||
parameters.config.sharedAccessKey
|
||||
),
|
||||
dataTransformer: parameters.dataTransformer || new DefaultDataTransformer(),
|
||||
refreshConnection() {
|
||||
const connection = new Connection(connectionOptions);
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { SharedKeyCredential } from "./sas";
|
||||
import { AccessToken } from "@azure/core-auth";
|
||||
import { Buffer } from "buffer";
|
||||
|
||||
/**
|
||||
* @class IotSharedKeyCredential
|
||||
* @ignore
|
||||
* Defines the IotSharedKeyCredential for IotHub.
|
||||
*/
|
||||
export class IotSharedKeyCredential extends SharedKeyCredential {
|
||||
/**
|
||||
* Gets the sas token for the specified audience for IotHub.
|
||||
* @ignore
|
||||
* @param {string} [audience] - The audience for which the token is desired. If not
|
||||
* provided then the Endpoint from the connection string will be applied.
|
||||
*/
|
||||
getToken(audience: string): AccessToken {
|
||||
return this._createToken(
|
||||
Math.floor(Date.now() / 1000) + 3600,
|
||||
audience,
|
||||
Buffer.from(this.key, "base64")
|
||||
);
|
||||
}
|
||||
}
|
|
@ -2,7 +2,6 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
import { TokenType } from "./auth/token";
|
||||
import { AccessToken } from "@azure/core-auth";
|
||||
import {
|
||||
Message as RheaMessage,
|
||||
Connection,
|
||||
|
@ -181,18 +180,18 @@ export class CbsClient {
|
|||
*
|
||||
* - **ManagementClient**
|
||||
* - `"sb://<your-namespace>.servicebus.windows.net/<event-hub-name>/$management"`.
|
||||
* @param {TokenInfo} tokenObject The token object that needs to be sent in the put-token request.
|
||||
* @param {string} token The token that needs to be sent in the put-token request.
|
||||
* @return {Promise<any>} Returns a Promise that resolves when $cbs authentication is successful
|
||||
* and rejects when an error occurs during $cbs authentication.
|
||||
*/
|
||||
async negotiateClaim(
|
||||
audience: string,
|
||||
tokenObject: AccessToken,
|
||||
token: string,
|
||||
tokenType: TokenType
|
||||
): Promise<CbsResponse> {
|
||||
try {
|
||||
const request: RheaMessage = {
|
||||
body: tokenObject.token,
|
||||
body: token,
|
||||
message_id: generate_uuid(),
|
||||
reply_to: this.replyTo,
|
||||
to: this.endpoint,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { ServiceBusConnectionStringModel, parseConnectionString } from "../util/utils";
|
||||
import { parseConnectionString } from "../util/utils";
|
||||
import { WebSocketImpl } from "rhea-promise";
|
||||
|
||||
/**
|
||||
|
@ -83,7 +83,12 @@ export const ConnectionConfig = {
|
|||
create(connectionString: string, path?: string): ConnectionConfig {
|
||||
connectionString = String(connectionString);
|
||||
|
||||
const parsedCS = parseConnectionString<ServiceBusConnectionStringModel>(connectionString);
|
||||
const parsedCS = parseConnectionString<{
|
||||
Endpoint: string;
|
||||
SharedAccessKeyName: string;
|
||||
SharedAccessKey: string;
|
||||
EntityPath?: string;
|
||||
}>(connectionString);
|
||||
if (!parsedCS.Endpoint) {
|
||||
throw new TypeError("Missing Endpoint in Connection String.");
|
||||
}
|
||||
|
|
|
@ -1,127 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { ConnectionConfig } from "./connectionConfig";
|
||||
import { EventHubConnectionConfig } from "./eventhubConnectionConfig";
|
||||
import { IotHubConnectionStringModel, parseConnectionString } from "../util/utils";
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
export interface IotHubConnectionConfig {
|
||||
/**
|
||||
* @property {string} endpoint - The iothub endpoint `"<iothub-namespace>.azure-devices.net"`.
|
||||
*/
|
||||
hostName: string;
|
||||
/**
|
||||
* @property {string} host - The host `"<yournamespace>"`.
|
||||
*/
|
||||
host: string;
|
||||
/**
|
||||
* @property {string} connectionString - The IotHub connection string.
|
||||
*/
|
||||
connectionString: string;
|
||||
/**
|
||||
* @property {string} entityPath - The name/path of the entity to which the connection needs to happen.
|
||||
*/
|
||||
entityPath: string;
|
||||
/**
|
||||
* @property {string} sharedAccessKeyName - The name of the access key.
|
||||
*/
|
||||
sharedAccessKeyName: string;
|
||||
/**
|
||||
* @property {string} sharedAccessKey - The secret value of the access key.
|
||||
*/
|
||||
sharedAccessKey: string;
|
||||
/**
|
||||
* @property {string} [deviceId] - The unique device identifier.
|
||||
*/
|
||||
deviceId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @module IotHubConnectionConfig
|
||||
* @ignore
|
||||
*/
|
||||
export const IotHubConnectionConfig = {
|
||||
/**
|
||||
* Creates the connection config.
|
||||
* @ignore
|
||||
* @param {string} connectionString - The event hub connection string
|
||||
* @param {string} [path] - The name/path of the entity (hub name) to which the connection needs to happen
|
||||
*/
|
||||
create(connectionString: string, path?: string): IotHubConnectionConfig {
|
||||
connectionString = String(connectionString);
|
||||
|
||||
const parsedCS = parseConnectionString<IotHubConnectionStringModel>(connectionString);
|
||||
if (!path) {
|
||||
path = "messages/events";
|
||||
}
|
||||
const result: IotHubConnectionConfig = {
|
||||
connectionString: connectionString,
|
||||
hostName: parsedCS.HostName,
|
||||
host: parsedCS && parsedCS.HostName ? parsedCS.HostName.split(".")[0] : "",
|
||||
entityPath: path,
|
||||
sharedAccessKeyName: parsedCS.SharedAccessKeyName,
|
||||
sharedAccessKey: parsedCS.SharedAccessKey,
|
||||
deviceId: parsedCS.DeviceId
|
||||
};
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Validates the properties of connection config.
|
||||
* @ignore
|
||||
* @param {ConnectionConfig} config The connection config to be validated.
|
||||
*/
|
||||
validate(config: IotHubConnectionConfig): void {
|
||||
if (!config) {
|
||||
throw new TypeError("Missing configuration");
|
||||
}
|
||||
|
||||
if (!config.hostName) {
|
||||
throw new TypeError("Missing 'hostName' in configuration");
|
||||
}
|
||||
config.hostName = String(config.hostName);
|
||||
|
||||
if (!config.entityPath) {
|
||||
throw new TypeError("Missing 'entityPath' in configuration");
|
||||
}
|
||||
config.entityPath = String(config.entityPath);
|
||||
|
||||
if (!config.sharedAccessKeyName) {
|
||||
throw new TypeError("Missing 'sharedAccessKeyName' in configuration");
|
||||
}
|
||||
config.sharedAccessKeyName = String(config.sharedAccessKeyName);
|
||||
|
||||
if (!config.sharedAccessKey) {
|
||||
throw new TypeError("Missing 'sharedAccessKey' in configuration");
|
||||
}
|
||||
config.sharedAccessKey = String(config.sharedAccessKey);
|
||||
|
||||
if (config.deviceId) {
|
||||
config.deviceId = String(config.deviceId);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Convert iothub connection config to eventhub connection config.
|
||||
* @ignore
|
||||
* @param {IotHubConnectionConfig} iotHubConfig
|
||||
*/
|
||||
convertToEventHubConnectionConfig(
|
||||
iotHubConfig: IotHubConnectionConfig
|
||||
): EventHubConnectionConfig {
|
||||
IotHubConnectionConfig.validate(iotHubConfig);
|
||||
const config: ConnectionConfig = {
|
||||
sharedAccessKey: iotHubConfig.sharedAccessKey,
|
||||
sharedAccessKeyName: iotHubConfig.sharedAccessKeyName,
|
||||
entityPath: iotHubConfig.entityPath,
|
||||
host: iotHubConfig.hostName,
|
||||
// `sb://` prefix to match with the endpoint in the connection string from the portal
|
||||
endpoint: `sb://${iotHubConfig.hostName}/`,
|
||||
connectionString: iotHubConfig.connectionString
|
||||
};
|
||||
return EventHubConnectionConfig.createFromConnectionConfig(config);
|
||||
}
|
||||
};
|
|
@ -7,13 +7,8 @@ export { RequestResponseLink, SendRequestOptions } from "./requestResponseLink";
|
|||
export { retry, RetryOptions, RetryConfig, RetryOperationType, RetryMode } from "./retry";
|
||||
export { DataTransformer, DefaultDataTransformer } from "./dataTransformer";
|
||||
export { TokenType } from "./auth/token";
|
||||
export { AccessToken, TokenCredential, isTokenCredential } from "@azure/core-auth";
|
||||
export { SharedKeyCredential } from "./auth/sas";
|
||||
export { IotSharedKeyCredential } from "./auth/iotSas";
|
||||
|
||||
export { ConnectionConfig, ConnectionConfigOptions } from "./connectionConfig/connectionConfig";
|
||||
export { EventHubConnectionConfig } from "./connectionConfig/eventhubConnectionConfig";
|
||||
export { IotHubConnectionConfig } from "./connectionConfig/iothubConnectionConfig";
|
||||
|
||||
export { CbsClient, CbsResponse } from "./cbs";
|
||||
export { Constants } from "./util/constants";
|
||||
|
@ -24,7 +19,6 @@ export {
|
|||
ConnectionProperties,
|
||||
CreateConnectionContextBaseParameters
|
||||
} from "./ConnectionContextBase";
|
||||
export { Dictionary, isAmqpError } from "rhea-promise";
|
||||
export {
|
||||
MessagingError,
|
||||
MessageErrorCodes,
|
||||
|
@ -40,22 +34,10 @@ export {
|
|||
} from "./errors";
|
||||
export {
|
||||
delay,
|
||||
Timeout,
|
||||
EventHubConnectionStringModel,
|
||||
executePromisesSequentially,
|
||||
parseConnectionString,
|
||||
IotHubConnectionStringModel,
|
||||
StorageConnectionStringModel,
|
||||
defaultLock,
|
||||
Func,
|
||||
ParsedOutput,
|
||||
getNewAsyncLock,
|
||||
AsyncLockOptions,
|
||||
ServiceBusConnectionStringModel,
|
||||
isIotHubConnectionString,
|
||||
randomNumberFromInterval,
|
||||
AsyncLock,
|
||||
isNode,
|
||||
WebSocketOptions
|
||||
} from "./util/utils";
|
||||
export { AmqpAnnotatedMessage } from "./amqpAnnotatedMessage";
|
||||
|
|
|
@ -57,49 +57,6 @@ export interface WebSocketOptions {
|
|||
export const isNode =
|
||||
!!process && !!process.version && !!process.versions && !!process.versions.node;
|
||||
|
||||
/**
|
||||
* Describes the servicebus connection string model.
|
||||
*/
|
||||
export interface ServiceBusConnectionStringModel {
|
||||
Endpoint: string;
|
||||
SharedAccessKeyName: string;
|
||||
SharedAccessKey: string;
|
||||
EntityPath?: string;
|
||||
[x: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the eventhub connection string model.
|
||||
*/
|
||||
export interface EventHubConnectionStringModel {
|
||||
Endpoint: string;
|
||||
SharedAccessKeyName: string;
|
||||
SharedAccessKey: string;
|
||||
EntityPath?: string;
|
||||
[x: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the storage connection string model.
|
||||
*/
|
||||
export interface StorageConnectionStringModel {
|
||||
DefaultEndpointsProtocol: string;
|
||||
AccountName: string;
|
||||
AccountKey: string;
|
||||
EndpointSuffix: string;
|
||||
[x: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the iothub connection string model.
|
||||
*/
|
||||
export interface IotHubConnectionStringModel {
|
||||
HostName: string;
|
||||
SharedAccessKeyName: string;
|
||||
SharedAccessKey: string;
|
||||
DeviceId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines an object with possible properties defined in T.
|
||||
* @type ParsedOutput<T>
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { ConnectionConfig, EventHubConnectionConfig, IotHubConnectionConfig } from "../src";
|
||||
import { ConnectionConfig } from "../src";
|
||||
import * as chai from "chai";
|
||||
import { isSharedAccessSignature } from "../src/connectionConfig/connectionConfig";
|
||||
import { SharedAccessSignatureCredential } from "../src/auth/sas";
|
||||
const should = chai.should();
|
||||
|
||||
describe("ConnectionConfig", function() {
|
||||
|
@ -197,241 +196,6 @@ describe("ConnectionConfig", function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe("EventHub", function() {
|
||||
it("should fail if connection config does not contain path and the connectionstring also does not contain EntityPath", function(done) {
|
||||
const connectionString =
|
||||
"Endpoint=sb://hostname.servicebus.windows.net/;SharedAccessKeyName=sakName;SharedAccessKey=sak";
|
||||
try {
|
||||
EventHubConnectionConfig.create(connectionString);
|
||||
done(new Error("Should not have reached here."));
|
||||
} catch (err) {
|
||||
err.message.should.match(/Either provide "path" or the "connectionString".*/gi);
|
||||
}
|
||||
done();
|
||||
});
|
||||
|
||||
it("should correctly populate config properties from an EventHubs connection string and the helper methods should work as expected", function(done) {
|
||||
const config = EventHubConnectionConfig.create(
|
||||
"Endpoint=sb://hostname.servicebus.windows.net/;SharedAccessKeyName=sakName;SharedAccessKey=sak;EntityPath=ep"
|
||||
);
|
||||
config.should.have.property("host").that.equals("hostname.servicebus.windows.net");
|
||||
config.should.have.property("sharedAccessKeyName").that.equals("sakName");
|
||||
config.should.have.property("sharedAccessKey").that.equals("sak");
|
||||
config.should.have.property("entityPath").that.equals("ep");
|
||||
|
||||
config.getManagementAddress().should.equal("ep/$management");
|
||||
config.getSenderAddress().should.equal("ep");
|
||||
config.getSenderAddress("0").should.equal("ep/Partitions/0");
|
||||
config.getSenderAddress(0).should.equal("ep/Partitions/0");
|
||||
config.getReceiverAddress("0").should.equal("ep/ConsumerGroups/$default/Partitions/0");
|
||||
config.getReceiverAddress(0).should.equal("ep/ConsumerGroups/$default/Partitions/0");
|
||||
config.getReceiverAddress("0", "cg").should.equal("ep/ConsumerGroups/cg/Partitions/0");
|
||||
config.getReceiverAddress(0, "cg").should.equal("ep/ConsumerGroups/cg/Partitions/0");
|
||||
|
||||
config
|
||||
.getManagementAudience()
|
||||
.should.equal("sb://hostname.servicebus.windows.net/ep/$management");
|
||||
config.getSenderAudience().should.equal("sb://hostname.servicebus.windows.net/ep");
|
||||
config
|
||||
.getSenderAudience("0")
|
||||
.should.equal("sb://hostname.servicebus.windows.net/ep/Partitions/0");
|
||||
config
|
||||
.getSenderAudience(0)
|
||||
.should.equal("sb://hostname.servicebus.windows.net/ep/Partitions/0");
|
||||
config
|
||||
.getReceiverAudience("0")
|
||||
.should.equal(
|
||||
"sb://hostname.servicebus.windows.net/ep/ConsumerGroups/$default/Partitions/0"
|
||||
);
|
||||
config
|
||||
.getReceiverAudience(0)
|
||||
.should.equal(
|
||||
"sb://hostname.servicebus.windows.net/ep/ConsumerGroups/$default/Partitions/0"
|
||||
);
|
||||
config
|
||||
.getReceiverAudience("0", "cg")
|
||||
.should.equal("sb://hostname.servicebus.windows.net/ep/ConsumerGroups/cg/Partitions/0");
|
||||
config
|
||||
.getReceiverAudience(0, "cg")
|
||||
.should.equal("sb://hostname.servicebus.windows.net/ep/ConsumerGroups/cg/Partitions/0");
|
||||
done();
|
||||
});
|
||||
|
||||
it("requires that Endpoint be present in the connection string", (done) => {
|
||||
const connectionString = `Endpoint=sb://a`;
|
||||
|
||||
should.throw(() => {
|
||||
EventHubConnectionConfig.create(connectionString);
|
||||
}, /must contain EntityPath/);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe("IotHub", function() {
|
||||
const iotString =
|
||||
"HostName=someiot.azure-devices.net;SharedAccessKeyName=sakName;SharedAccessKey=sak;DeviceId=device-1234";
|
||||
it("should correctly create an iothub connection config from an iothub connectionstring", function(done) {
|
||||
const config = IotHubConnectionConfig.create(iotString);
|
||||
config.should.have.property("hostName").that.equals("someiot.azure-devices.net");
|
||||
config.should.have.property("host").that.equals("someiot");
|
||||
config.should.have.property("sharedAccessKeyName").that.equals("sakName");
|
||||
config.should.have.property("sharedAccessKey").that.equals("sak");
|
||||
config.should.have.property("entityPath").that.equals("messages/events");
|
||||
done();
|
||||
});
|
||||
|
||||
it("populates path from the path argument if provided", function(done) {
|
||||
const config = IotHubConnectionConfig.create(iotString, "abc");
|
||||
config.should.have.property("entityPath").that.equals("abc");
|
||||
done();
|
||||
});
|
||||
|
||||
it("converts an IotHubConnectionConfig to an EventHubConnectionConfig", function(done) {
|
||||
const config = IotHubConnectionConfig.create(iotString);
|
||||
const ehConfig = IotHubConnectionConfig.convertToEventHubConnectionConfig(config);
|
||||
ehConfig.should.have.property("endpoint").that.equals("sb://someiot.azure-devices.net/");
|
||||
ehConfig.should.have.property("host").that.equals("someiot.azure-devices.net");
|
||||
ehConfig.should.have.property("sharedAccessKeyName").that.equals("sakName");
|
||||
ehConfig.should.have.property("sharedAccessKey").that.equals("sak");
|
||||
ehConfig.should.have.property("entityPath").that.equals("messages/events");
|
||||
|
||||
ehConfig.getManagementAddress().should.equal("messages/events/$management");
|
||||
ehConfig.getSenderAddress().should.equal("messages/events");
|
||||
ehConfig.getSenderAddress("0").should.equal("messages/events/Partitions/0");
|
||||
ehConfig.getSenderAddress(0).should.equal("messages/events/Partitions/0");
|
||||
ehConfig
|
||||
.getReceiverAddress("0")
|
||||
.should.equal("messages/events/ConsumerGroups/$default/Partitions/0");
|
||||
ehConfig
|
||||
.getReceiverAddress(0)
|
||||
.should.equal("messages/events/ConsumerGroups/$default/Partitions/0");
|
||||
ehConfig
|
||||
.getReceiverAddress("0", "cg")
|
||||
.should.equal("messages/events/ConsumerGroups/cg/Partitions/0");
|
||||
ehConfig
|
||||
.getReceiverAddress(0, "cg")
|
||||
.should.equal("messages/events/ConsumerGroups/cg/Partitions/0");
|
||||
|
||||
ehConfig
|
||||
.getManagementAudience()
|
||||
.should.equal("sb://someiot.azure-devices.net/messages/events/$management");
|
||||
ehConfig.getSenderAudience().should.equal("sb://someiot.azure-devices.net/messages/events");
|
||||
ehConfig
|
||||
.getSenderAudience("0")
|
||||
.should.equal("sb://someiot.azure-devices.net/messages/events/Partitions/0");
|
||||
ehConfig
|
||||
.getSenderAudience(0)
|
||||
.should.equal("sb://someiot.azure-devices.net/messages/events/Partitions/0");
|
||||
ehConfig
|
||||
.getReceiverAudience("0")
|
||||
.should.equal(
|
||||
"sb://someiot.azure-devices.net/messages/events/ConsumerGroups/$default/Partitions/0"
|
||||
);
|
||||
ehConfig
|
||||
.getReceiverAudience(0)
|
||||
.should.equal(
|
||||
"sb://someiot.azure-devices.net/messages/events/ConsumerGroups/$default/Partitions/0"
|
||||
);
|
||||
ehConfig
|
||||
.getReceiverAudience("0", "cg")
|
||||
.should.equal(
|
||||
"sb://someiot.azure-devices.net/messages/events/ConsumerGroups/cg/Partitions/0"
|
||||
);
|
||||
ehConfig
|
||||
.getReceiverAudience(0, "cg")
|
||||
.should.equal(
|
||||
"sb://someiot.azure-devices.net/messages/events/ConsumerGroups/cg/Partitions/0"
|
||||
);
|
||||
done();
|
||||
});
|
||||
|
||||
describe("Throws error if required connection config properties are not present", function() {
|
||||
const connectionString = `
|
||||
Endpoint=sb://someiot.azure-devices.net/;
|
||||
SharedAccessKeyName=sakName;
|
||||
SharedAccessKey=sakName;
|
||||
EntityPath=messages/events;
|
||||
`;
|
||||
|
||||
it("requires that connection config be present", (done) => {
|
||||
should.throw(() => {
|
||||
IotHubConnectionConfig.validate(undefined as any);
|
||||
}, /Missing configuration/);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it("requires that hostName be present in the connection config", (done) => {
|
||||
const config: IotHubConnectionConfig = {
|
||||
connectionString: connectionString,
|
||||
hostName: "",
|
||||
host: "someiot",
|
||||
sharedAccessKeyName: "sakName",
|
||||
sharedAccessKey: "sak",
|
||||
entityPath: "messages/events"
|
||||
};
|
||||
|
||||
should.throw(() => {
|
||||
IotHubConnectionConfig.validate(config);
|
||||
}, /Missing 'hostName'/);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it("requires that host be present in the connection config", (done) => {
|
||||
const config: IotHubConnectionConfig = {
|
||||
connectionString: connectionString,
|
||||
hostName: "someiot.azure-devices.net",
|
||||
host: "someiot",
|
||||
sharedAccessKeyName: "sakName",
|
||||
sharedAccessKey: "sak",
|
||||
entityPath: ""
|
||||
};
|
||||
|
||||
should.throw(() => {
|
||||
IotHubConnectionConfig.validate(config);
|
||||
}, /Missing 'entityPath'/);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it("requires that sharedAccessKeyName be present in the connection config", (done) => {
|
||||
const config: IotHubConnectionConfig = {
|
||||
connectionString: connectionString,
|
||||
hostName: "someiot.azure-devices.net",
|
||||
host: "someiot",
|
||||
sharedAccessKeyName: "",
|
||||
sharedAccessKey: "sak",
|
||||
entityPath: "messages/events"
|
||||
};
|
||||
|
||||
should.throw(() => {
|
||||
IotHubConnectionConfig.validate(config);
|
||||
}, /Missing 'sharedAccessKeyName'/);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it("requires that sharedAccessKey be present in the connection config", (done) => {
|
||||
const config: IotHubConnectionConfig = {
|
||||
connectionString: connectionString,
|
||||
hostName: "someiot.azure-devices.net",
|
||||
host: "someiot",
|
||||
sharedAccessKeyName: "sakName",
|
||||
sharedAccessKey: "",
|
||||
entityPath: "messages/events"
|
||||
};
|
||||
|
||||
should.throw(() => {
|
||||
IotHubConnectionConfig.validate(config);
|
||||
}, /Missing 'sharedAccessKey'/);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("SharedAccessSignature", () => {
|
||||
[
|
||||
"Endpoint=hello;SharedAccessSignature=SharedAccessSignature sr=<resource>&sig=someb64=&se=<expiry>&skn=<keyname>",
|
||||
|
@ -461,28 +225,5 @@ describe("ConnectionConfig", function() {
|
|||
connectionString: "Endpoint=hello;SharedAccessSignature=SharedAccessSignature hellosig"
|
||||
});
|
||||
});
|
||||
|
||||
it("SharedAccessSignatureCredential", () => {
|
||||
const sasCred = new SharedAccessSignatureCredential("SharedAccessSignature se=<blah>");
|
||||
const accessToken = sasCred.getToken("audience isn't used");
|
||||
|
||||
should.equal(
|
||||
accessToken.token,
|
||||
"SharedAccessSignature se=<blah>",
|
||||
"SAS URI we were constructed with should just be returned verbatim without interpretation (and the audience is ignored)"
|
||||
);
|
||||
|
||||
should.equal(
|
||||
accessToken.expiresOnTimestamp,
|
||||
0,
|
||||
"SAS URI always returns 0 for expiry (ignoring what's in the SAS token)"
|
||||
);
|
||||
|
||||
// these just exist because we're a SharedKeyCredential but we don't currently
|
||||
// parse any attributes out (they're available but we've carved out a spot so
|
||||
// they're not needed.)
|
||||
should.equal(sasCred.key, "");
|
||||
should.equal(sasCred.keyName, "");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,13 +3,7 @@
|
|||
|
||||
import * as chai from "chai";
|
||||
const should = chai.should();
|
||||
import {
|
||||
CbsClient,
|
||||
ConnectionConfig,
|
||||
ConnectionContextBase,
|
||||
DefaultDataTransformer,
|
||||
SharedKeyCredential
|
||||
} from "../src";
|
||||
import { CbsClient, ConnectionConfig, ConnectionContextBase, DefaultDataTransformer } from "../src";
|
||||
import { Connection } from "rhea-promise";
|
||||
import { isNode } from "../src/util/utils";
|
||||
|
||||
|
@ -32,10 +26,8 @@ describe("ConnectionContextBase", function() {
|
|||
should.exist(context.connectionId);
|
||||
should.exist(context.connectionLock);
|
||||
should.exist(context.negotiateClaimLock);
|
||||
should.exist(context.tokenCredential);
|
||||
should.exist(context.dataTransformer);
|
||||
context.wasConnectionCloseCalled.should.equal(false);
|
||||
context.tokenCredential.should.instanceOf(SharedKeyCredential);
|
||||
context.connection.should.instanceOf(Connection);
|
||||
context.connection.options.properties!.product.should.equal("MSJSClient");
|
||||
context.connection.options.properties!["user-agent"].should.equal("/js-amqp-client");
|
||||
|
@ -141,7 +133,6 @@ describe("ConnectionContextBase", function() {
|
|||
should.exist(context.connectionId);
|
||||
should.exist(context.connectionLock);
|
||||
should.exist(context.negotiateClaimLock);
|
||||
should.exist(context.tokenCredential);
|
||||
should.exist(context.dataTransformer);
|
||||
context.wasConnectionCloseCalled.should.equal(false);
|
||||
context.cbsSession.should.instanceOf(CbsClient);
|
||||
|
|
|
@ -91,9 +91,12 @@
|
|||
"@azure/core-amqp": "2.0.0-beta.1",
|
||||
"@azure/core-asynciterator-polyfill": "^1.0.0",
|
||||
"@azure/core-tracing": "1.0.0-preview.9",
|
||||
"@azure/core-auth": "^1.1.3",
|
||||
"@azure/logger": "^1.0.0",
|
||||
"@opentelemetry/api": "^0.10.2",
|
||||
"buffer": "^5.2.1",
|
||||
"is-buffer": "^2.0.3",
|
||||
"jssha": "^3.1.0",
|
||||
"process": "^0.11.10",
|
||||
"rhea-promise": "^1.0.0",
|
||||
"tslib": "^2.0.0",
|
||||
|
|
|
@ -10,7 +10,7 @@ import { OperationTracingOptions } from '@azure/core-tracing';
|
|||
import { RetryOptions } from '@azure/core-amqp';
|
||||
import { Span } from '@opentelemetry/api';
|
||||
import { SpanContext } from '@opentelemetry/api';
|
||||
import { TokenCredential } from '@azure/core-amqp';
|
||||
import { TokenCredential } from '@azure/core-auth';
|
||||
import { WebSocketImpl } from 'rhea-promise';
|
||||
import { WebSocketOptions } from '@azure/core-amqp';
|
||||
|
||||
|
|
|
@ -13,17 +13,15 @@ import {
|
|||
ConnectionContextBase,
|
||||
Constants,
|
||||
CreateConnectionContextBaseParameters,
|
||||
EventHubConnectionConfig,
|
||||
SharedKeyCredential,
|
||||
TokenCredential,
|
||||
isTokenCredential,
|
||||
parseConnectionString,
|
||||
EventHubConnectionStringModel,
|
||||
ConnectionConfig
|
||||
} from "@azure/core-amqp";
|
||||
import { TokenCredential, isTokenCredential } from "@azure/core-auth";
|
||||
import { ManagementClient, ManagementClientOptions } from "./managementClient";
|
||||
import { EventHubClientOptions } from "./models/public";
|
||||
import { Connection, ConnectionEvents, Dictionary, EventContext, OnAmqpEvent } from "rhea-promise";
|
||||
import { EventHubConnectionConfig } from "./eventhubConnectionConfig";
|
||||
import { SharedKeyCredential } from "./eventhubSharedKeyCredential";
|
||||
|
||||
/**
|
||||
* @internal
|
||||
|
@ -37,6 +35,11 @@ export interface ConnectionContext extends ConnectionContextBase {
|
|||
* parsing the connection string.
|
||||
*/
|
||||
readonly config: EventHubConnectionConfig;
|
||||
/**
|
||||
* @property {SharedKeyCredential | TokenCredential} [tokenCredential] The credential to be used for Authentication.
|
||||
* Default value: SharedKeyCredentials.
|
||||
*/
|
||||
tokenCredential: SharedKeyCredential | TokenCredential;
|
||||
/**
|
||||
* @property wasConnectionCloseCalled Indicates whether the close() method was
|
||||
* called on theconnection object.
|
||||
|
@ -163,7 +166,6 @@ export namespace ConnectionContext {
|
|||
|
||||
const parameters: CreateConnectionContextBaseParameters = {
|
||||
config: config,
|
||||
tokenCredential: tokenCredential,
|
||||
// re-enabling this will be a post-GA discussion.
|
||||
// dataTransformer: options.dataTransformer,
|
||||
isEntityPathRequired: true,
|
||||
|
@ -175,6 +177,7 @@ export namespace ConnectionContext {
|
|||
};
|
||||
// Let us create the base context and then add EventHub specific ConnectionContext properties.
|
||||
const connectionContext = ConnectionContextBase.create(parameters) as ConnectionContext;
|
||||
connectionContext.tokenCredential = tokenCredential;
|
||||
connectionContext.wasConnectionCloseCalled = false;
|
||||
connectionContext.senders = {};
|
||||
connectionContext.receivers = {};
|
||||
|
@ -450,7 +453,7 @@ export function createConnectionContext(
|
|||
hostOrConnectionString = String(hostOrConnectionString);
|
||||
|
||||
if (!isTokenCredential(credentialOrOptions)) {
|
||||
const parsedCS = parseConnectionString<EventHubConnectionStringModel>(hostOrConnectionString);
|
||||
const parsedCS = parseConnectionString<{ EntityPath?: string }>(hostOrConnectionString);
|
||||
if (
|
||||
!(parsedCS.EntityPath || (typeof eventHubNameOrOptions === "string" && eventHubNameOrOptions))
|
||||
) {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import { getTracer } from "@azure/core-tracing";
|
||||
import { Span, SpanContext, SpanKind } from "@opentelemetry/api";
|
||||
import { EventHubConnectionConfig } from "@azure/core-amqp";
|
||||
import { EventHubConnectionConfig } from "../eventhubConnectionConfig";
|
||||
|
||||
/**
|
||||
* @internal
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
} from "./models/public";
|
||||
import { InMemoryCheckpointStore } from "./inMemoryCheckpointStore";
|
||||
import { CheckpointStore, EventProcessor, FullEventProcessorOptions } from "./eventProcessor";
|
||||
import { Constants, TokenCredential } from "@azure/core-amqp";
|
||||
import { Constants } from "@azure/core-amqp";
|
||||
import { logger } from "./log";
|
||||
|
||||
import {
|
||||
|
@ -19,7 +19,7 @@ import {
|
|||
Subscription,
|
||||
SubscriptionEventHandlers
|
||||
} from "./eventHubConsumerClientModels";
|
||||
import { isTokenCredential } from "@azure/core-amqp";
|
||||
import { TokenCredential, isTokenCredential } from "@azure/core-auth";
|
||||
import { EventHubProperties, PartitionProperties } from "./managementClient";
|
||||
import { PartitionGate } from "./impl/partitionGate";
|
||||
import { v4 as uuid } from "uuid";
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { isTokenCredential, TokenCredential } from "@azure/core-amqp";
|
||||
import { isTokenCredential, TokenCredential } from "@azure/core-auth";
|
||||
import { getTracer } from "@azure/core-tracing";
|
||||
import { CanonicalCode, Link, Span, SpanContext, SpanKind } from "@opentelemetry/api";
|
||||
import { ConnectionContext, createConnectionContext } from "./connectionContext";
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the MIT license.
|
||||
/* eslint-disable eqeqeq */
|
||||
|
||||
import { ConnectionConfig } from "./connectionConfig";
|
||||
import { ConnectionConfig } from "@azure/core-amqp";
|
||||
|
||||
/**
|
||||
* Describes the connection config object that is created after parsing an EventHub connection
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { ServiceBusConnectionStringModel, parseConnectionString } from "../util/utils";
|
||||
import { parseConnectionString } from "@azure/core-amqp";
|
||||
import { AccessToken } from "@azure/core-auth";
|
||||
import { Buffer } from "buffer";
|
||||
import isBuffer from "is-buffer";
|
||||
|
@ -78,9 +78,11 @@ export class SharedKeyCredential {
|
|||
* @param {string} connectionString - The EventHub/ServiceBus connection string
|
||||
*/
|
||||
static fromConnectionString(connectionString: string): SharedKeyCredential {
|
||||
const parsed = parseConnectionString<
|
||||
ServiceBusConnectionStringModel & { SharedAccessSignature: string }
|
||||
>(connectionString);
|
||||
const parsed = parseConnectionString<{
|
||||
SharedAccessSignature: string;
|
||||
SharedAccessKeyName: string;
|
||||
SharedAccessKey: string;
|
||||
}>(connectionString);
|
||||
|
||||
if (parsed.SharedAccessSignature == null) {
|
||||
return new SharedKeyCredential(parsed.SharedAccessKeyName, parsed.SharedAccessKey);
|
|
@ -35,5 +35,6 @@ export { EventDataBatch, TryAddOptions } from "./eventDataBatch";
|
|||
export { Checkpoint } from "./partitionProcessor";
|
||||
export { CheckpointStore, PartitionOwnership } from "./eventProcessor";
|
||||
export { CloseReason } from "./models/public";
|
||||
export { MessagingError, RetryOptions, TokenCredential, WebSocketOptions } from "@azure/core-amqp";
|
||||
export { MessagingError, RetryOptions, WebSocketOptions } from "@azure/core-amqp";
|
||||
export { TokenCredential } from "@azure/core-auth";
|
||||
export { logger } from "./log";
|
||||
|
|
|
@ -2,16 +2,12 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
import { v4 as uuid } from "uuid";
|
||||
import {
|
||||
AccessToken,
|
||||
Constants,
|
||||
SharedKeyCredential,
|
||||
TokenType,
|
||||
defaultLock
|
||||
} from "@azure/core-amqp";
|
||||
import { Constants, TokenType, defaultLock } from "@azure/core-amqp";
|
||||
import { AccessToken } from "@azure/core-auth";
|
||||
import { ConnectionContext } from "./connectionContext";
|
||||
import { AwaitableSender, Receiver } from "rhea-promise";
|
||||
import { logger } from "./log";
|
||||
import { SharedKeyCredential } from "../src/eventhubSharedKeyCredential";
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
|
@ -175,7 +171,7 @@ export class LinkEntity {
|
|||
this.address
|
||||
);
|
||||
await defaultLock.acquire(this._context.negotiateClaimLock, () => {
|
||||
return this._context.cbsSession.negotiateClaim(this.audience, tokenObject, tokenType);
|
||||
return this._context.cbsSession.negotiateClaim(this.audience, tokenObject.token, tokenType);
|
||||
});
|
||||
logger.verbose(
|
||||
"[%s] Negotiated claim for %s '%s' with with address: %s",
|
||||
|
|
|
@ -9,7 +9,6 @@ import {
|
|||
RetryOperationType,
|
||||
RetryOptions,
|
||||
SendRequestOptions,
|
||||
SharedKeyCredential,
|
||||
defaultLock,
|
||||
retry,
|
||||
translate
|
||||
|
@ -33,6 +32,8 @@ import { OperationNames } from "./models/private";
|
|||
import { Span, SpanContext, SpanKind, CanonicalCode } from "@opentelemetry/api";
|
||||
import { getParentSpan, OperationOptions } from "./util/operationOptions";
|
||||
import { getTracer } from "@azure/core-tracing";
|
||||
import { SharedKeyCredential } from "../src/eventhubSharedKeyCredential";
|
||||
|
||||
/**
|
||||
* Describes the runtime information of an Event Hub.
|
||||
*/
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import {
|
||||
parseConnectionString,
|
||||
ServiceBusConnectionStringModel,
|
||||
SharedKeyCredential
|
||||
} from "@azure/core-amqp";
|
||||
import { parseConnectionString } from "@azure/core-amqp";
|
||||
import { EventHubConsumerClient } from "../src/eventHubConsumerClient";
|
||||
import { EnvVarKeys, getEnvVars } from "./utils/testUtils";
|
||||
import chai from "chai";
|
||||
import { EventHubProducerClient } from "../src";
|
||||
import { SharedKeyCredential } from "../src/eventhubSharedKeyCredential";
|
||||
|
||||
const should = chai.should();
|
||||
const env = getEnvVars();
|
||||
|
@ -18,7 +15,7 @@ describe("Authentication via SAS", () => {
|
|||
const service = {
|
||||
connectionString: env[EnvVarKeys.EVENTHUB_CONNECTION_STRING],
|
||||
path: env[EnvVarKeys.EVENTHUB_NAME],
|
||||
fqdn: parseConnectionString<ServiceBusConnectionStringModel>(
|
||||
fqdn: parseConnectionString<{ Endpoint: string }>(
|
||||
env[EnvVarKeys.EVENTHUB_CONNECTION_STRING]
|
||||
).Endpoint.replace(/\/+$/, "")
|
||||
};
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { EventHubConnectionConfig } from "../src/eventhubConnectionConfig";
|
||||
import chai from "chai";
|
||||
const should = chai.should();
|
||||
|
||||
describe("ConnectionConfig", function() {
|
||||
describe("EventHub", function() {
|
||||
it("should fail if connection config does not contain path and the connectionstring also does not contain EntityPath", function(done) {
|
||||
const connectionString =
|
||||
"Endpoint=sb://hostname.servicebus.windows.net/;SharedAccessKeyName=sakName;SharedAccessKey=sak";
|
||||
try {
|
||||
EventHubConnectionConfig.create(connectionString);
|
||||
done(new Error("Should not have reached here."));
|
||||
} catch (err) {
|
||||
err.message.should.match(/Either provide "path" or the "connectionString".*/gi);
|
||||
}
|
||||
done();
|
||||
});
|
||||
|
||||
it("should correctly populate config properties from an EventHubs connection string and the helper methods should work as expected", function(done) {
|
||||
const config = EventHubConnectionConfig.create(
|
||||
"Endpoint=sb://hostname.servicebus.windows.net/;SharedAccessKeyName=sakName;SharedAccessKey=sak;EntityPath=ep"
|
||||
);
|
||||
config.should.have.property("host").that.equals("hostname.servicebus.windows.net");
|
||||
config.should.have.property("sharedAccessKeyName").that.equals("sakName");
|
||||
config.should.have.property("sharedAccessKey").that.equals("sak");
|
||||
config.should.have.property("entityPath").that.equals("ep");
|
||||
|
||||
config.getManagementAddress().should.equal("ep/$management");
|
||||
config.getSenderAddress().should.equal("ep");
|
||||
config.getSenderAddress("0").should.equal("ep/Partitions/0");
|
||||
config.getSenderAddress(0).should.equal("ep/Partitions/0");
|
||||
config.getReceiverAddress("0").should.equal("ep/ConsumerGroups/$default/Partitions/0");
|
||||
config.getReceiverAddress(0).should.equal("ep/ConsumerGroups/$default/Partitions/0");
|
||||
config.getReceiverAddress("0", "cg").should.equal("ep/ConsumerGroups/cg/Partitions/0");
|
||||
config.getReceiverAddress(0, "cg").should.equal("ep/ConsumerGroups/cg/Partitions/0");
|
||||
|
||||
config
|
||||
.getManagementAudience()
|
||||
.should.equal("sb://hostname.servicebus.windows.net/ep/$management");
|
||||
config.getSenderAudience().should.equal("sb://hostname.servicebus.windows.net/ep");
|
||||
config
|
||||
.getSenderAudience("0")
|
||||
.should.equal("sb://hostname.servicebus.windows.net/ep/Partitions/0");
|
||||
config
|
||||
.getSenderAudience(0)
|
||||
.should.equal("sb://hostname.servicebus.windows.net/ep/Partitions/0");
|
||||
config
|
||||
.getReceiverAudience("0")
|
||||
.should.equal(
|
||||
"sb://hostname.servicebus.windows.net/ep/ConsumerGroups/$default/Partitions/0"
|
||||
);
|
||||
config
|
||||
.getReceiverAudience(0)
|
||||
.should.equal(
|
||||
"sb://hostname.servicebus.windows.net/ep/ConsumerGroups/$default/Partitions/0"
|
||||
);
|
||||
config
|
||||
.getReceiverAudience("0", "cg")
|
||||
.should.equal("sb://hostname.servicebus.windows.net/ep/ConsumerGroups/cg/Partitions/0");
|
||||
config
|
||||
.getReceiverAudience(0, "cg")
|
||||
.should.equal("sb://hostname.servicebus.windows.net/ep/ConsumerGroups/cg/Partitions/0");
|
||||
done();
|
||||
});
|
||||
|
||||
it("requires that Endpoint be present in the connection string", (done) => {
|
||||
const connectionString = `Endpoint=sb://a`;
|
||||
|
||||
should.throw(() => {
|
||||
EventHubConnectionConfig.create(connectionString);
|
||||
}, /must contain EntityPath/);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -5,7 +5,7 @@ import chai from "chai";
|
|||
import { createMessageSpan } from "../../src/diagnostics/messageSpan";
|
||||
import { TraceFlags, SpanContext } from "@opentelemetry/api";
|
||||
import { TestTracer, setTracer, getTracer } from "@azure/core-tracing";
|
||||
import { EventHubConnectionConfig } from "@azure/core-amqp";
|
||||
import { EventHubConnectionConfig } from "../../src/eventhubConnectionConfig";
|
||||
|
||||
const should = chai.should();
|
||||
const assert = chai.assert;
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import * as chai from "chai";
|
||||
chai.should();
|
||||
import debugModule from "debug";
|
||||
const debug = debugModule("azure:core-amqp:token-spec");
|
||||
import { IotSharedKeyCredential, SharedKeyCredential } from "../src";
|
||||
import chai from "chai";
|
||||
const should = chai.should();
|
||||
import {
|
||||
SharedKeyCredential,
|
||||
SharedAccessSignatureCredential
|
||||
} from "../src/eventhubSharedKeyCredential";
|
||||
|
||||
describe("SharedKeyCredential", function(): void {
|
||||
it("should work as expected with required parameters", async function(): Promise<void> {
|
||||
|
@ -13,9 +14,7 @@ describe("SharedKeyCredential", function(): void {
|
|||
const key = "importantValue";
|
||||
const tokenProvider = new SharedKeyCredential(keyName, key);
|
||||
const now = Math.floor(Date.now() / 1000) + 3600;
|
||||
debug(">>> now: %d", now);
|
||||
const tokenInfo = tokenProvider.getToken("myaudience");
|
||||
debug(">>> Token Info is: %O", tokenInfo);
|
||||
tokenInfo.token.should.match(
|
||||
/SharedAccessSignature sr=myaudience&sig=(.*)&se=\d{10}&skn=myKeyName/g
|
||||
);
|
||||
|
@ -28,28 +27,32 @@ describe("SharedKeyCredential", function(): void {
|
|||
"Endpoint=sb://hostname.servicebus.windows.net/;SharedAccessKeyName=sakName;SharedAccessKey=sak;EntityPath=ep";
|
||||
const tokenProvider = SharedKeyCredential.fromConnectionString(cs);
|
||||
const now = Math.floor(Date.now() / 1000) + 3600;
|
||||
debug(">>> now: %d", now);
|
||||
const tokenInfo = tokenProvider.getToken("sb://hostname.servicebus.windows.net/");
|
||||
debug(">>> Token Info is: %O", tokenInfo);
|
||||
tokenInfo.token.should.match(
|
||||
/SharedAccessSignature sr=sb%3A%2F%2Fhostname.servicebus.windows.net%2F&sig=(.*)&se=\d{10}&skn=sakName/g
|
||||
);
|
||||
tokenInfo.expiresOnTimestamp.should.equal(now);
|
||||
});
|
||||
});
|
||||
it("SharedAccessSignatureCredential", () => {
|
||||
const sasCred = new SharedAccessSignatureCredential("SharedAccessSignature se=<blah>");
|
||||
const accessToken = sasCred.getToken("audience isn't used");
|
||||
|
||||
describe("IotSharedKeyCredential", function(): void {
|
||||
it("should work as expected with required parameters", async function(): Promise<void> {
|
||||
const keyName = "myKeyName";
|
||||
const key = "importantValue";
|
||||
const tokenProvider = new IotSharedKeyCredential(keyName, key);
|
||||
const now = Math.floor(Date.now() / 1000) + 3600;
|
||||
debug(">>> now: %d", now);
|
||||
const tokenInfo = tokenProvider.getToken("myaudience");
|
||||
debug(">>> Token Info is: %O", tokenInfo);
|
||||
tokenInfo.token.should.match(
|
||||
/SharedAccessSignature sr=myaudience&sig=(.*)&se=\d{10}&skn=myKeyName/g
|
||||
should.equal(
|
||||
accessToken.token,
|
||||
"SharedAccessSignature se=<blah>",
|
||||
"SAS URI we were constructed with should just be returned verbatim without interpretation (and the audience is ignored)"
|
||||
);
|
||||
tokenInfo.expiresOnTimestamp.should.equal(now);
|
||||
|
||||
should.equal(
|
||||
accessToken.expiresOnTimestamp,
|
||||
0,
|
||||
"SAS URI always returns 0 for expiry (ignoring what's in the SAS token)"
|
||||
);
|
||||
|
||||
// these just exist because we're a SharedKeyCredential but we don't currently
|
||||
// parse any attributes out (they're available but we've carved out a spot so
|
||||
// they're not needed.)
|
||||
should.equal(sasCred.key, "");
|
||||
should.equal(sasCred.keyName, "");
|
||||
});
|
||||
});
|
|
@ -102,12 +102,14 @@
|
|||
"@azure/core-http": "^1.2.0",
|
||||
"@azure/core-tracing": "1.0.0-preview.9",
|
||||
"@azure/core-paging": "^1.1.1",
|
||||
"@azure/core-auth": "^1.1.3",
|
||||
"@azure/logger": "^1.0.0",
|
||||
"@opentelemetry/api": "^0.10.2",
|
||||
"@types/is-buffer": "^2.0.0",
|
||||
"@types/long": "^4.0.0",
|
||||
"buffer": "^5.2.1",
|
||||
"is-buffer": "^2.0.3",
|
||||
"jssha": "^3.1.0",
|
||||
"long": "^4.0.0",
|
||||
"process": "^0.11.10",
|
||||
"tslib": "^2.0.0",
|
||||
|
|
|
@ -20,7 +20,7 @@ import { RetryOptions } from '@azure/core-amqp';
|
|||
import { ServiceClient } from '@azure/core-http';
|
||||
import { Span } from '@opentelemetry/api';
|
||||
import { SpanContext } from '@opentelemetry/api';
|
||||
import { TokenCredential } from '@azure/core-amqp';
|
||||
import { TokenCredential } from '@azure/core-auth';
|
||||
import { TokenType } from '@azure/core-amqp';
|
||||
import { UserAgentOptions } from '@azure/core-http';
|
||||
import { WebSocketImpl } from 'rhea-promise';
|
||||
|
|
|
@ -8,10 +8,9 @@ import {
|
|||
ConnectionContextBase,
|
||||
Constants,
|
||||
CreateConnectionContextBaseParameters,
|
||||
SharedKeyCredential,
|
||||
TokenCredential,
|
||||
delay
|
||||
} from "@azure/core-amqp";
|
||||
import { TokenCredential } from "@azure/core-auth";
|
||||
import { ServiceBusClientOptions } from "./constructorHelpers";
|
||||
import { Connection, ConnectionEvents, EventContext, OnAmqpEvent } from "rhea-promise";
|
||||
import { MessageSender } from "./core/messageSender";
|
||||
|
@ -20,6 +19,7 @@ import { MessageReceiver } from "./core/messageReceiver";
|
|||
import { ManagementClient } from "./core/managementClient";
|
||||
import { formatUserAgentPrefix } from "./util/utils";
|
||||
import { getRuntimeInfo } from "./util/runtimeInfo";
|
||||
import { SharedKeyCredential } from "./servicebusSharedKeyCredential";
|
||||
|
||||
/**
|
||||
* @internal
|
||||
|
@ -28,6 +28,11 @@ import { getRuntimeInfo } from "./util/runtimeInfo";
|
|||
* tokenCredential, senders, receivers, etc. about the ServiceBus client.
|
||||
*/
|
||||
export interface ConnectionContext extends ConnectionContextBase {
|
||||
/**
|
||||
* @property {SharedKeyCredential | TokenCredential} [tokenCredential] The credential to be used for Authentication.
|
||||
* Default value: SharedKeyCredentials.
|
||||
*/
|
||||
tokenCredential: SharedKeyCredential | TokenCredential;
|
||||
/**
|
||||
* @property A map of active Service Bus Senders with sender name as key.
|
||||
*/
|
||||
|
@ -141,7 +146,6 @@ export namespace ConnectionContext {
|
|||
)} ${getRuntimeInfo()}`;
|
||||
const parameters: CreateConnectionContextBaseParameters = {
|
||||
config: config,
|
||||
tokenCredential: tokenCredential,
|
||||
// re-enabling this will be a post-GA discussion similar to event-hubs.
|
||||
// dataTransformer: options.dataTransformer,
|
||||
isEntityPathRequired: false,
|
||||
|
@ -153,6 +157,7 @@ export namespace ConnectionContext {
|
|||
};
|
||||
// Let us create the base context and then add ServiceBus specific ConnectionContext properties.
|
||||
const connectionContext = ConnectionContextBase.create(parameters) as ConnectionContext;
|
||||
connectionContext.tokenCredential = tokenCredential;
|
||||
connectionContext.senders = {};
|
||||
connectionContext.messageReceivers = {};
|
||||
connectionContext.messageSessions = {};
|
||||
|
|
|
@ -1,15 +1,11 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import {
|
||||
ConnectionConfig,
|
||||
RetryOptions,
|
||||
SharedKeyCredential,
|
||||
TokenCredential,
|
||||
WebSocketOptions
|
||||
} from "@azure/core-amqp";
|
||||
import { ConnectionConfig, RetryOptions, WebSocketOptions } from "@azure/core-amqp";
|
||||
import { TokenCredential } from "@azure/core-auth";
|
||||
import { ConnectionContext } from "./connectionContext";
|
||||
import { UserAgentOptions } from "@azure/core-http";
|
||||
import { SharedKeyCredential } from "./servicebusSharedKeyCredential";
|
||||
|
||||
/**
|
||||
* Describes the options that can be provided while creating the ServiceBusClient.
|
||||
|
|
|
@ -2,14 +2,13 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
import {
|
||||
AccessToken,
|
||||
Constants,
|
||||
SharedKeyCredential,
|
||||
TokenType,
|
||||
defaultLock,
|
||||
RequestResponseLink,
|
||||
MessagingError
|
||||
} from "@azure/core-amqp";
|
||||
import { AccessToken } from "@azure/core-auth";
|
||||
import { ConnectionContext } from "../connectionContext";
|
||||
import {
|
||||
AwaitableSender,
|
||||
|
@ -22,6 +21,7 @@ import {
|
|||
import { getUniqueName, StandardAbortMessage } from "../util/utils";
|
||||
import { AbortError, AbortSignalLike } from "@azure/abort-controller";
|
||||
import { ServiceBusLogger } from "../log";
|
||||
import { SharedKeyCredential } from "../servicebusSharedKeyCredential";
|
||||
|
||||
/**
|
||||
* @internal
|
||||
|
@ -444,7 +444,7 @@ export abstract class LinkEntity<LinkT extends Receiver | AwaitableSender | Requ
|
|||
}
|
||||
await defaultLock.acquire(this._context.negotiateClaimLock, () => {
|
||||
this.checkIfConnectionReady();
|
||||
return this._context.cbsSession.negotiateClaim(this.audience, tokenObject, tokenType);
|
||||
return this._context.cbsSession.negotiateClaim(this.audience, tokenObject.token, tokenType);
|
||||
});
|
||||
this._logger.verbose(
|
||||
"%s Negotiated claim for %s '%s' with with address: %s",
|
||||
|
|
|
@ -10,10 +10,10 @@ export {
|
|||
MessageErrorCodes,
|
||||
MessagingError,
|
||||
RetryOptions,
|
||||
TokenCredential,
|
||||
TokenType,
|
||||
WebSocketOptions
|
||||
} from "@azure/core-amqp";
|
||||
export { TokenCredential } from "@azure/core-auth";
|
||||
export { OperationOptions } from "@azure/core-http";
|
||||
export { Delivery, WebSocketImpl } from "rhea-promise";
|
||||
export { ServiceBusClientOptions } from "./constructorHelpers";
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import {
|
||||
Constants as AMQPConstants,
|
||||
isTokenCredential,
|
||||
parseConnectionString,
|
||||
TokenCredential
|
||||
} from "@azure/core-amqp";
|
||||
import { Constants as AMQPConstants, parseConnectionString } from "@azure/core-amqp";
|
||||
import { TokenCredential, isTokenCredential } from "@azure/core-auth";
|
||||
import {
|
||||
bearerTokenAuthenticationPolicy,
|
||||
createPipelineFromOptions,
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { TokenCredential, isTokenCredential, ConnectionConfig } from "@azure/core-amqp";
|
||||
import { ConnectionConfig } from "@azure/core-amqp";
|
||||
import { TokenCredential, isTokenCredential } from "@azure/core-auth";
|
||||
import {
|
||||
ServiceBusClientOptions,
|
||||
createConnectionContextForConnectionString,
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { parseConnectionString } from "@azure/core-amqp";
|
||||
import { AccessToken } from "@azure/core-auth";
|
||||
import { Buffer } from "buffer";
|
||||
import isBuffer from "is-buffer";
|
||||
import jssha from "jssha";
|
||||
|
||||
/**
|
||||
* @class SharedKeyCredential
|
||||
* Defines the SharedKeyCredential .
|
||||
*/
|
||||
export class SharedKeyCredential {
|
||||
/**
|
||||
* @property {string} keyName - The name of the EventHub/ServiceBus key.
|
||||
*/
|
||||
keyName: string;
|
||||
|
||||
/**
|
||||
* @property {string} key - The secret value associated with the above EventHub/ServiceBus key.
|
||||
*/
|
||||
key: string;
|
||||
|
||||
/**
|
||||
* Initializes a new instance of SharedKeyCredential
|
||||
* @constructor
|
||||
* @param {string} keyName - The name of the EventHub/ServiceBus key.
|
||||
* @param {string} key - The secret value associated with the above EventHub/ServiceBus key
|
||||
*/
|
||||
constructor(keyName: string, key: string) {
|
||||
this.keyName = keyName;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the sas token for the specified audience
|
||||
* @param {string} [audience] - The audience for which the token is desired.
|
||||
*/
|
||||
getToken(audience: string): AccessToken {
|
||||
return this._createToken(Math.floor(Date.now() / 1000) + 3600, audience);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the sas token based on the provided information
|
||||
* @param {string | number} expiry - The time period in unix time after which the token will expire.
|
||||
* @param {string} [audience] - The audience for which the token is desired.
|
||||
* @param {string | Buffer} [hashInput] The input to be provided to hmac to create the hash.
|
||||
*/
|
||||
protected _createToken(
|
||||
expiry: number,
|
||||
audience: string,
|
||||
hashInput?: string | Buffer
|
||||
): AccessToken {
|
||||
audience = encodeURIComponent(audience);
|
||||
const keyName = encodeURIComponent(this.keyName);
|
||||
const stringToSign = audience + "\n" + expiry;
|
||||
hashInput = hashInput || this.key;
|
||||
let shaObj: any;
|
||||
if (isBuffer(hashInput)) {
|
||||
shaObj = new jssha("SHA-256", "ARRAYBUFFER");
|
||||
shaObj.setHMACKey(hashInput, "ARRAYBUFFER");
|
||||
shaObj.update(Buffer.from(stringToSign));
|
||||
} else {
|
||||
shaObj = new jssha("SHA-256", "TEXT");
|
||||
shaObj.setHMACKey(hashInput, "TEXT");
|
||||
shaObj.update(stringToSign);
|
||||
}
|
||||
const sig = encodeURIComponent(shaObj.getHMAC("B64"));
|
||||
return {
|
||||
token: `SharedAccessSignature sr=${audience}&sig=${sig}&se=${expiry}&skn=${keyName}`,
|
||||
expiresOnTimestamp: expiry
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a token provider from the EventHub/ServiceBus connection string;
|
||||
* @param {string} connectionString - The EventHub/ServiceBus connection string
|
||||
*/
|
||||
static fromConnectionString(connectionString: string): SharedKeyCredential {
|
||||
const parsed = parseConnectionString<{
|
||||
SharedAccessSignature: string;
|
||||
SharedAccessKeyName: string;
|
||||
SharedAccessKey: string;
|
||||
}>(connectionString);
|
||||
|
||||
if (parsed.SharedAccessSignature == null) {
|
||||
return new SharedKeyCredential(parsed.SharedAccessKeyName, parsed.SharedAccessKey);
|
||||
} else {
|
||||
return new SharedAccessSignatureCredential(parsed.SharedAccessSignature);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A credential that takes a SharedAccessSignature:
|
||||
* `SharedAccessSignature sr=<resource>&sig=<signature>&se=<expiry>&skn=<keyname>`
|
||||
*
|
||||
* @internal
|
||||
* @ignore
|
||||
*/
|
||||
export class SharedAccessSignatureCredential extends SharedKeyCredential {
|
||||
private _accessToken: AccessToken;
|
||||
|
||||
/**
|
||||
* @param sharedAccessSignature A shared access signature of the form
|
||||
* `SharedAccessSignature sr=<resource>&sig=<signature>&se=<expiry>&skn=<keyname>`
|
||||
*/
|
||||
constructor(sharedAccessSignature: string) {
|
||||
super("", "");
|
||||
|
||||
this._accessToken = {
|
||||
token: sharedAccessSignature,
|
||||
expiresOnTimestamp: 0
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a valid token for authenticaton.
|
||||
*
|
||||
* @param _audience Not applicable in SharedAccessSignatureCredential as the token is not re-generated at every invocation of the method
|
||||
*/
|
||||
getToken(_audience: string): AccessToken {
|
||||
return this._accessToken;
|
||||
}
|
||||
}
|
|
@ -4,7 +4,8 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import { AccessToken, SharedKeyCredential } from "@azure/core-amqp";
|
||||
import { AccessToken } from "@azure/core-auth";
|
||||
import { SharedKeyCredential } from "../servicebusSharedKeyCredential";
|
||||
import { HttpHeaders, ServiceClientCredentials, WebResource } from "@azure/core-http";
|
||||
import { generateKey } from "./crypto";
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { isNode } from "@azure/core-amqp";
|
||||
import { PageSettings } from "@azure/core-paging";
|
||||
import { DefaultAzureCredential } from "@azure/identity";
|
||||
import chai from "chai";
|
||||
|
@ -14,7 +13,7 @@ import { RuleProperties, CreateRuleOptions } from "../src/serializers/ruleResour
|
|||
import { CreateSubscriptionOptions } from "../src/serializers/subscriptionResourceSerializer";
|
||||
import { CreateTopicOptions } from "../src/serializers/topicResourceSerializer";
|
||||
import { ServiceBusAdministrationClient } from "../src/serviceBusAtomManagementClient";
|
||||
import { EntityStatus, EntityAvailabilityStatus } from "../src/util/utils";
|
||||
import { EntityStatus, EntityAvailabilityStatus, isNode } from "../src/util/utils";
|
||||
import { EnvVarNames, getEnvVars } from "./utils/envVarUtils";
|
||||
import { recreateQueue, recreateSubscription, recreateTopic } from "./utils/managementUtils";
|
||||
import { EntityNames } from "./utils/testUtils";
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { SharedKeyCredential } from "@azure/core-amqp";
|
||||
import { SharedKeyCredential } from "../src/servicebusSharedKeyCredential";
|
||||
import chai from "chai";
|
||||
import { ServiceBusClient, ServiceBusReceiver, parseServiceBusConnectionString } from "../src";
|
||||
import { getEnvVars } from "./utils/envVarUtils";
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import chai from "chai";
|
||||
const should = chai.should();
|
||||
import {
|
||||
SharedKeyCredential,
|
||||
SharedAccessSignatureCredential
|
||||
} from "../../src/servicebusSharedKeyCredential";
|
||||
|
||||
describe("SharedKeyCredential", function(): void {
|
||||
it("should work as expected with required parameters", async function(): Promise<void> {
|
||||
const keyName = "myKeyName";
|
||||
const key = "importantValue";
|
||||
const tokenProvider = new SharedKeyCredential(keyName, key);
|
||||
const now = Math.floor(Date.now() / 1000) + 3600;
|
||||
const tokenInfo = tokenProvider.getToken("myaudience");
|
||||
tokenInfo.token.should.match(
|
||||
/SharedAccessSignature sr=myaudience&sig=(.*)&se=\d{10}&skn=myKeyName/g
|
||||
);
|
||||
tokenInfo.expiresOnTimestamp.should.equal(now);
|
||||
});
|
||||
it("should work as expected when created from a connection string", async function(): Promise<
|
||||
void
|
||||
> {
|
||||
const cs =
|
||||
"Endpoint=sb://hostname.servicebus.windows.net/;SharedAccessKeyName=sakName;SharedAccessKey=sak;EntityPath=ep";
|
||||
const tokenProvider = SharedKeyCredential.fromConnectionString(cs);
|
||||
const now = Math.floor(Date.now() / 1000) + 3600;
|
||||
const tokenInfo = tokenProvider.getToken("sb://hostname.servicebus.windows.net/");
|
||||
tokenInfo.token.should.match(
|
||||
/SharedAccessSignature sr=sb%3A%2F%2Fhostname.servicebus.windows.net%2F&sig=(.*)&se=\d{10}&skn=sakName/g
|
||||
);
|
||||
tokenInfo.expiresOnTimestamp.should.equal(now);
|
||||
});
|
||||
it("SharedAccessSignatureCredential", () => {
|
||||
const sasCred = new SharedAccessSignatureCredential("SharedAccessSignature se=<blah>");
|
||||
const accessToken = sasCred.getToken("audience isn't used");
|
||||
|
||||
should.equal(
|
||||
accessToken.token,
|
||||
"SharedAccessSignature se=<blah>",
|
||||
"SAS URI we were constructed with should just be returned verbatim without interpretation (and the audience is ignored)"
|
||||
);
|
||||
|
||||
should.equal(
|
||||
accessToken.expiresOnTimestamp,
|
||||
0,
|
||||
"SAS URI always returns 0 for expiry (ignoring what's in the SAS token)"
|
||||
);
|
||||
|
||||
// these just exist because we're a SharedKeyCredential but we don't currently
|
||||
// parse any attributes out (they're available but we've carved out a spot so
|
||||
// they're not needed.)
|
||||
should.equal(sasCred.key, "");
|
||||
should.equal(sasCred.keyName, "");
|
||||
});
|
||||
});
|
|
@ -8,7 +8,8 @@ import {
|
|||
ReceiverEvents,
|
||||
ReceiverOptions
|
||||
} from "rhea-promise";
|
||||
import { DefaultDataTransformer, AccessToken, Constants } from "@azure/core-amqp";
|
||||
import { DefaultDataTransformer, Constants } from "@azure/core-amqp";
|
||||
import { AccessToken } from "@azure/core-auth";
|
||||
import { EventEmitter } from "events";
|
||||
import { getUniqueName } from "../../src/util/utils";
|
||||
import { Link } from "rhea-promise/typings/lib/link";
|
||||
|
|
|
@ -18,7 +18,7 @@ import {
|
|||
getRandomTestClientTypeWithNoSessions
|
||||
} from "./utils/testutils2";
|
||||
import { getDeliveryProperty } from "./utils/misc";
|
||||
import { isNode } from "@azure/core-amqp";
|
||||
import { isNode } from "../src/util/utils";
|
||||
import { verifyMessageCount } from "./utils/managementUtils";
|
||||
import sinon from "sinon";
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче