From cca05a9578ca278641bedb1b430220fa2389c7bf Mon Sep 17 00:00:00 2001 From: Shubham Tiwari Date: Wed, 4 Dec 2024 19:08:52 +0530 Subject: [PATCH 01/15] chore: add previewIam domain --- src/rest/PreviewIam.ts | 33 + src/rest/PreviewIamBase.ts | 33 + src/rest/Twilio.ts | 11 + src/rest/previewIam/V1.ts | 46 + src/rest/previewIam/Versionless.ts | 37 + src/rest/previewIam/v1/authorize.ts | 171 ++++ src/rest/previewIam/v1/token.ts | 200 ++++ .../previewIam/versionless/organization.ts | 135 +++ .../versionless/organization/account.ts | 471 ++++++++++ .../organization/roleAssignment.ts | 578 ++++++++++++ .../versionless/organization/user.ts | 851 ++++++++++++++++++ 11 files changed, 2566 insertions(+) create mode 100644 src/rest/PreviewIam.ts create mode 100644 src/rest/PreviewIamBase.ts create mode 100644 src/rest/previewIam/V1.ts create mode 100644 src/rest/previewIam/Versionless.ts create mode 100644 src/rest/previewIam/v1/authorize.ts create mode 100644 src/rest/previewIam/v1/token.ts create mode 100644 src/rest/previewIam/versionless/organization.ts create mode 100644 src/rest/previewIam/versionless/organization/account.ts create mode 100644 src/rest/previewIam/versionless/organization/roleAssignment.ts create mode 100644 src/rest/previewIam/versionless/organization/user.ts diff --git a/src/rest/PreviewIam.ts b/src/rest/PreviewIam.ts new file mode 100644 index 000000000..1d9ff54d5 --- /dev/null +++ b/src/rest/PreviewIam.ts @@ -0,0 +1,33 @@ +import { TokenListInstance } from "./previewIam/v1/token"; +import { AuthorizeListInstance } from "./previewIam/v1/authorize"; +import PreviewIamBase from "./PreviewIamBase"; +import { OrganizationListInstance } from "./previewIam/versionless/organization"; +import Versionless from "./previewIam/Versionless"; + +class PreviewIam extends PreviewIamBase { + _organization?: OrganizationListInstance; + /** + * @deprecated - Use v1.tokens instead + */ + get tokens(): TokenListInstance { + console.warn("tokens is deprecated. Use v1.tokens instead."); + return this.v1.token; + } + + /** + * @deprecated - Use v1.authorize instead + */ + get authorize(): AuthorizeListInstance { + console.warn("authorize is deprecated. Use v1.authorize instead."); + return this.v1.authorize; + } + + /** Getter for organization resource */ + get organization(): OrganizationListInstance { + this._organization = + this._organization || new Versionless(this).organization; + return this._organization; + } +} + +export = PreviewIam; diff --git a/src/rest/PreviewIamBase.ts b/src/rest/PreviewIamBase.ts new file mode 100644 index 000000000..b82f1ac98 --- /dev/null +++ b/src/rest/PreviewIamBase.ts @@ -0,0 +1,33 @@ +/* + * This code was generated by + * ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + * | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + * | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + * + * NOTE: This class is auto generated by OpenAPI Generator. + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import Domain from "../base/Domain"; +import V1 from "./previewIam/V1"; + +class PreviewIamBase extends Domain { + _v1?: V1; + + /** + * Initialize previewIam domain + * + * @param twilio - The twilio client + */ + constructor(twilio: any) { + super(twilio, "https://preview-iam.twilio.com"); + } + + get v1(): V1 { + this._v1 = this._v1 || new V1(this); + return this._v1; + } +} + +export = PreviewIamBase; diff --git a/src/rest/Twilio.ts b/src/rest/Twilio.ts index 8031af04a..902ba5701 100644 --- a/src/rest/Twilio.ts +++ b/src/rest/Twilio.ts @@ -33,6 +33,7 @@ import Notify from "./Notify"; import Numbers from "./Numbers"; import Oauth from "./Oauth"; import Preview from "./Preview"; +import PreviewIam from "./PreviewIam"; import Pricing from "./Pricing"; import Proxy from "./Proxy"; import Routes from "./Routes"; @@ -125,6 +126,8 @@ class Twilio extends Client { _oauth?: Oauth; /** (Twilio.Preview) - preview domain */ _preview?: Preview; + /** (Twilio.PreviewIam) - previewIam domain */ + _previewIam?: PreviewIam; /** (Twilio.Pricing) - pricing domain */ _pricing?: Pricing; /** (Twilio.Proxy) - proxy domain */ @@ -194,6 +197,7 @@ class Twilio extends Client { this.numbers; this.oauth; this.preview; + this.previewIam; this.pricing; this.proxy; this.routes; @@ -334,6 +338,13 @@ class Twilio extends Client { get preview(): Preview { return this._preview ?? (this._preview = new (require("./Preview"))(this)); } + /** Getter for (Twilio.PreviewIam) domain */ + get previewIam(): PreviewIam { + return ( + this._previewIam ?? + (this._previewIam = new (require("./PreviewIam"))(this)) + ); + } /** Getter for (Twilio.Pricing) domain */ get pricing(): Pricing { return this._pricing ?? (this._pricing = new (require("./Pricing"))(this)); diff --git a/src/rest/previewIam/V1.ts b/src/rest/previewIam/V1.ts new file mode 100644 index 000000000..6a27dc820 --- /dev/null +++ b/src/rest/previewIam/V1.ts @@ -0,0 +1,46 @@ +/* + * This code was generated by + * ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + * | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + * | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + * + * Organization Public API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * NOTE: This class is auto generated by OpenAPI Generator. + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import PreviewIamBase from "../PreviewIamBase"; +import Version from "../../base/Version"; +import { AuthorizeListInstance } from "./v1/authorize"; +import { TokenListInstance } from "./v1/token"; + +export default class V1 extends Version { + /** + * Initialize the V1 version of PreviewIam + * + * @param domain - The Twilio (Twilio.PreviewIam) domain + */ + constructor(domain: PreviewIamBase) { + super(domain, "v1"); + } + + /** authorize - { Twilio.PreviewIam.V1.AuthorizeListInstance } resource */ + protected _authorize?: AuthorizeListInstance; + /** token - { Twilio.PreviewIam.V1.TokenListInstance } resource */ + protected _token?: TokenListInstance; + + /** Getter for authorize resource */ + get authorize(): AuthorizeListInstance { + this._authorize = this._authorize || AuthorizeListInstance(this); + return this._authorize; + } + + /** Getter for token resource */ + get token(): TokenListInstance { + this._token = this._token || TokenListInstance(this); + return this._token; + } +} diff --git a/src/rest/previewIam/Versionless.ts b/src/rest/previewIam/Versionless.ts new file mode 100644 index 000000000..57a41e82e --- /dev/null +++ b/src/rest/previewIam/Versionless.ts @@ -0,0 +1,37 @@ +/* + * This code was generated by + * ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + * | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + * | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + * + * Organization Public API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * NOTE: This class is auto generated by OpenAPI Generator. + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import PreviewIamBase from "../PreviewIamBase"; +import Version from "../../base/Version"; +import { OrganizationListInstance } from "./versionless/organization"; + +export default class Versionless extends Version { + /** + * Initialize the Versionless version of PreviewIam + * + * @param domain - The Twilio (Twilio.PreviewIam) domain + */ + constructor(domain: PreviewIamBase) { + super(domain, "Organizations"); + } + + /** organization - { Twilio.PreviewIam.Versionless.OrganizationListInstance } resource */ + protected _organization?: OrganizationListInstance; + + /** Getter for organization resource */ + get organization(): OrganizationListInstance { + this._organization = this._organization || OrganizationListInstance(this); + return this._organization; + } +} diff --git a/src/rest/previewIam/v1/authorize.ts b/src/rest/previewIam/v1/authorize.ts new file mode 100644 index 000000000..df8915e79 --- /dev/null +++ b/src/rest/previewIam/v1/authorize.ts @@ -0,0 +1,171 @@ +/* + * This code was generated by + * ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + * | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + * | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + * + * Organization Public API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * NOTE: This class is auto generated by OpenAPI Generator. + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { inspect, InspectOptions } from "util"; +import V1 from "../V1"; +const deserialize = require("../../../base/deserialize"); +const serialize = require("../../../base/serialize"); +import { isValidPathParam } from "../../../base/utility"; + +/** + * Options to pass to fetch a AuthorizeInstance + */ +export interface AuthorizeListInstanceFetchOptions { + /** Response Type */ + responseType?: string; + /** The Client Identifier */ + clientId?: string; + /** The url to which response will be redirected to */ + redirectUri?: string; + /** The scope of the access request */ + scope?: string; + /** An opaque value which can be used to maintain state between the request and callback */ + state?: string; +} + +export interface AuthorizeSolution {} + +export interface AuthorizeListInstance { + _version: V1; + _solution: AuthorizeSolution; + _uri: string; + + /** + * Fetch a AuthorizeInstance + * + * @param callback - Callback to handle processed record + * + * @returns Resolves to processed AuthorizeInstance + */ + fetch( + callback?: (error: Error | null, item?: AuthorizeInstance) => any + ): Promise; + /** + * Fetch a AuthorizeInstance + * + * @param params - Parameter for request + * @param callback - Callback to handle processed record + * + * @returns Resolves to processed AuthorizeInstance + */ + fetch( + params: AuthorizeListInstanceFetchOptions, + callback?: (error: Error | null, item?: AuthorizeInstance) => any + ): Promise; + + /** + * Provide a user-friendly representation + */ + toJSON(): any; + [inspect.custom](_depth: any, options: InspectOptions): any; +} + +export function AuthorizeListInstance(version: V1): AuthorizeListInstance { + const instance = {} as AuthorizeListInstance; + + instance._version = version; + instance._solution = {}; + instance._uri = `/authorize`; + + instance.fetch = function fetch( + params?: + | AuthorizeListInstanceFetchOptions + | ((error: Error | null, items: AuthorizeInstance) => any), + callback?: (error: Error | null, items: AuthorizeInstance) => any + ): Promise { + if (params instanceof Function) { + callback = params; + params = {}; + } else { + params = params || {}; + } + + let data: any = {}; + + if (params["responseType"] !== undefined) + data["response_type"] = params["responseType"]; + if (params["clientId"] !== undefined) + data["client_id"] = params["clientId"]; + if (params["redirectUri"] !== undefined) + data["redirect_uri"] = params["redirectUri"]; + if (params["scope"] !== undefined) data["scope"] = params["scope"]; + if (params["state"] !== undefined) data["state"] = params["state"]; + + const headers: any = {}; + headers["Accept"] = "application/json"; + + let operationVersion = version, + operationPromise = operationVersion.fetch({ + uri: instance._uri, + method: "get", + params: data, + headers, + }); + + operationPromise = operationPromise.then( + (payload) => new AuthorizeInstance(operationVersion, payload) + ); + + operationPromise = instance._version.setPromiseCallback( + operationPromise, + callback + ); + return operationPromise; + }; + + instance.toJSON = function toJSON() { + return instance._solution; + }; + + instance[inspect.custom] = function inspectImpl( + _depth: any, + options: InspectOptions + ) { + return inspect(instance.toJSON(), options); + }; + + return instance; +} + +interface AuthorizePayload extends AuthorizeResource {} + +interface AuthorizeResource { + redirect_to: string; +} + +export class AuthorizeInstance { + constructor(protected _version: V1, payload: AuthorizeResource) { + this.redirectTo = payload.redirect_to; + } + + /** + * The callback URL + */ + redirectTo: string; + + /** + * Provide a user-friendly representation + * + * @returns Object + */ + toJSON() { + return { + redirectTo: this.redirectTo, + }; + } + + [inspect.custom](_depth: any, options: InspectOptions) { + return inspect(this.toJSON(), options); + } +} diff --git a/src/rest/previewIam/v1/token.ts b/src/rest/previewIam/v1/token.ts new file mode 100644 index 000000000..4e40089a4 --- /dev/null +++ b/src/rest/previewIam/v1/token.ts @@ -0,0 +1,200 @@ +/* + * This code was generated by + * ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + * | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + * | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + * + * Organization Public API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * NOTE: This class is auto generated by OpenAPI Generator. + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { inspect, InspectOptions } from "util"; +import V1 from "../V1"; +const deserialize = require("../../../base/deserialize"); +const serialize = require("../../../base/serialize"); +import { isValidPathParam } from "../../../base/utility"; + +/** + * Options to pass to create a TokenInstance + */ +export interface TokenListInstanceCreateOptions { + /** Grant type is a credential representing resource owner\\\'s authorization which can be used by client to obtain access token. */ + grantType: string; + /** A 34 character string that uniquely identifies this OAuth App. */ + clientId: string; + /** The credential for confidential OAuth App. */ + clientSecret?: string; + /** JWT token related to the authorization code grant type. */ + code?: string; + /** The redirect uri */ + redirectUri?: string; + /** The targeted audience uri */ + audience?: string; + /** JWT token related to refresh access token. */ + refreshToken?: string; + /** The scope of token */ + scope?: string; +} + +export interface TokenSolution {} + +export interface TokenListInstance { + _version: V1; + _solution: TokenSolution; + _uri: string; + + /** + * Create a TokenInstance + * + * @param params - Parameter for request + * @param callback - Callback to handle processed record + * + * @returns Resolves to processed TokenInstance + */ + create( + params: TokenListInstanceCreateOptions, + callback?: (error: Error | null, item?: TokenInstance) => any + ): Promise; + + /** + * Provide a user-friendly representation + */ + toJSON(): any; + [inspect.custom](_depth: any, options: InspectOptions): any; +} + +export function TokenListInstance(version: V1): TokenListInstance { + const instance = {} as TokenListInstance; + + instance._version = version; + instance._solution = {}; + instance._uri = `/token`; + + instance.create = function create( + params: TokenListInstanceCreateOptions, + callback?: (error: Error | null, items: TokenInstance) => any + ): Promise { + if (params === null || params === undefined) { + throw new Error('Required parameter "params" missing.'); + } + + if (params["grantType"] === null || params["grantType"] === undefined) { + throw new Error("Required parameter \"params['grantType']\" missing."); + } + + if (params["clientId"] === null || params["clientId"] === undefined) { + throw new Error("Required parameter \"params['clientId']\" missing."); + } + + let data: any = {}; + + data["grant_type"] = params["grantType"]; + + data["client_id"] = params["clientId"]; + if (params["clientSecret"] !== undefined) + data["client_secret"] = params["clientSecret"]; + if (params["code"] !== undefined) data["code"] = params["code"]; + if (params["redirectUri"] !== undefined) + data["redirect_uri"] = params["redirectUri"]; + if (params["audience"] !== undefined) data["audience"] = params["audience"]; + if (params["refreshToken"] !== undefined) + data["refresh_token"] = params["refreshToken"]; + if (params["scope"] !== undefined) data["scope"] = params["scope"]; + + const headers: any = {}; + headers["Content-Type"] = "application/x-www-form-urlencoded"; + headers["Accept"] = "application/json"; + + let operationVersion = version, + operationPromise = operationVersion.create({ + uri: instance._uri, + method: "post", + data, + headers, + }); + + operationPromise = operationPromise.then( + (payload) => new TokenInstance(operationVersion, payload) + ); + + operationPromise = instance._version.setPromiseCallback( + operationPromise, + callback + ); + return operationPromise; + }; + + instance.toJSON = function toJSON() { + return instance._solution; + }; + + instance[inspect.custom] = function inspectImpl( + _depth: any, + options: InspectOptions + ) { + return inspect(instance.toJSON(), options); + }; + + return instance; +} + +interface TokenPayload extends TokenResource {} + +interface TokenResource { + access_token: string; + refresh_token: string; + id_token: string; + token_type: string; + expires_in: number; +} + +export class TokenInstance { + constructor(protected _version: V1, payload: TokenResource) { + this.accessToken = payload.access_token; + this.refreshToken = payload.refresh_token; + this.idToken = payload.id_token; + this.tokenType = payload.token_type; + this.expiresIn = payload.expires_in; + } + + /** + * Token which carries the necessary information to access a Twilio resource directly. + */ + accessToken: string; + /** + * Token which carries the information necessary to get a new access token. + */ + refreshToken: string; + /** + * Token which carries the information necessary of user profile. + */ + idToken: string; + /** + * Token type + */ + tokenType: string; + expiresIn: number; + + /** + * Provide a user-friendly representation + * + * @returns Object + */ + toJSON() { + return { + accessToken: this.accessToken, + refreshToken: this.refreshToken, + idToken: this.idToken, + tokenType: this.tokenType, + expiresIn: this.expiresIn, + }; + } + + [inspect.custom](_depth: any, options: InspectOptions) { + return inspect(this.toJSON(), options); + } +} diff --git a/src/rest/previewIam/versionless/organization.ts b/src/rest/previewIam/versionless/organization.ts new file mode 100644 index 000000000..c5cbaa524 --- /dev/null +++ b/src/rest/previewIam/versionless/organization.ts @@ -0,0 +1,135 @@ +/* + * This code was generated by + * ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + * | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + * | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + * + * Organization Public API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * NOTE: This class is auto generated by OpenAPI Generator. + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { inspect, InspectOptions } from "util"; +import Versionless from "../Versionless"; +const deserialize = require("../../../base/deserialize"); +const serialize = require("../../../base/serialize"); +import { isValidPathParam } from "../../../base/utility"; +import { AccountListInstance } from "./organization/account"; +import { RoleAssignmentListInstance } from "./organization/roleAssignment"; +import { UserListInstance } from "./organization/user"; + +export interface OrganizationContext { + accounts: AccountListInstance; + roleAssignments: RoleAssignmentListInstance; + users: UserListInstance; + + /** + * Provide a user-friendly representation + */ + toJSON(): any; + [inspect.custom](_depth: any, options: InspectOptions): any; +} + +export interface OrganizationContextSolution { + organizationSid: string; +} + +export class OrganizationContextImpl implements OrganizationContext { + protected _solution: OrganizationContextSolution; + protected _uri: string; + + protected _accounts?: AccountListInstance; + protected _roleAssignments?: RoleAssignmentListInstance; + protected _users?: UserListInstance; + + constructor(protected _version: Versionless, organizationSid: string) { + if (!isValidPathParam(organizationSid)) { + throw new Error("Parameter 'organizationSid' is not valid."); + } + + this._solution = { organizationSid }; + this._uri = `/${organizationSid}`; + } + + get accounts(): AccountListInstance { + this._accounts = + this._accounts || + AccountListInstance(this._version, this._solution.organizationSid); + return this._accounts; + } + + get roleAssignments(): RoleAssignmentListInstance { + this._roleAssignments = + this._roleAssignments || + RoleAssignmentListInstance(this._version, this._solution.organizationSid); + return this._roleAssignments; + } + + get users(): UserListInstance { + this._users = + this._users || + UserListInstance(this._version, this._solution.organizationSid); + return this._users; + } + + /** + * Provide a user-friendly representation + * + * @returns Object + */ + toJSON() { + return this._solution; + } + + [inspect.custom](_depth: any, options: InspectOptions) { + return inspect(this.toJSON(), options); + } +} + +export interface OrganizationSolution {} + +export interface OrganizationListInstance { + _version: Versionless; + _solution: OrganizationSolution; + _uri: string; + + (organizationSid: string): OrganizationContext; + get(organizationSid: string): OrganizationContext; + + /** + * Provide a user-friendly representation + */ + toJSON(): any; + [inspect.custom](_depth: any, options: InspectOptions): any; +} + +export function OrganizationListInstance( + version: Versionless +): OrganizationListInstance { + const instance = ((organizationSid) => + instance.get(organizationSid)) as OrganizationListInstance; + + instance.get = function get(organizationSid): OrganizationContext { + return new OrganizationContextImpl(version, organizationSid); + }; + + instance._version = version; + instance._solution = {}; + instance._uri = ``; + + instance.toJSON = function toJSON() { + return instance._solution; + }; + + instance[inspect.custom] = function inspectImpl( + _depth: any, + options: InspectOptions + ) { + return inspect(instance.toJSON(), options); + }; + + return instance; +} diff --git a/src/rest/previewIam/versionless/organization/account.ts b/src/rest/previewIam/versionless/organization/account.ts new file mode 100644 index 000000000..50db38f49 --- /dev/null +++ b/src/rest/previewIam/versionless/organization/account.ts @@ -0,0 +1,471 @@ +/* + * This code was generated by + * ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + * | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + * | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + * + * Organization Public API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * NOTE: This class is auto generated by OpenAPI Generator. + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { inspect, InspectOptions } from "util"; +import Page, { TwilioResponsePayload } from "../../../../base/Page"; +import Response from "../../../../http/response"; +import Versionless from "../../Versionless"; +const deserialize = require("../../../../base/deserialize"); +const serialize = require("../../../../base/serialize"); +import { isValidPathParam } from "../../../../base/utility"; + +/** + * Options to pass to each + */ +export interface AccountListInstanceEachOptions { + /** */ + pageSize?: number; + /** Function to process each record. If this and a positional callback are passed, this one will be used */ + callback?: (item: AccountInstance, done: (err?: Error) => void) => void; + /** Function to be called upon completion of streaming */ + done?: Function; + /** Upper limit for the number of records to return. each() guarantees never to return more than limit. Default is no limit */ + limit?: number; +} + +/** + * Options to pass to list + */ +export interface AccountListInstanceOptions { + /** */ + pageSize?: number; + /** Upper limit for the number of records to return. list() guarantees never to return more than limit. Default is no limit */ + limit?: number; +} + +/** + * Options to pass to page + */ +export interface AccountListInstancePageOptions { + /** */ + pageSize?: number; + /** Page Number, this value is simply for client state */ + pageNumber?: number; + /** PageToken provided by the API */ + pageToken?: string; +} + +export interface AccountContext { + /** + * Fetch a AccountInstance + * + * @param callback - Callback to handle processed record + * + * @returns Resolves to processed AccountInstance + */ + fetch( + callback?: (error: Error | null, item?: AccountInstance) => any + ): Promise; + + /** + * Provide a user-friendly representation + */ + toJSON(): any; + [inspect.custom](_depth: any, options: InspectOptions): any; +} + +export interface AccountContextSolution { + organizationSid: string; + accountSid: string; +} + +export class AccountContextImpl implements AccountContext { + protected _solution: AccountContextSolution; + protected _uri: string; + + constructor( + protected _version: Versionless, + organizationSid: string, + accountSid: string + ) { + if (!isValidPathParam(organizationSid)) { + throw new Error("Parameter 'organizationSid' is not valid."); + } + + if (!isValidPathParam(accountSid)) { + throw new Error("Parameter 'accountSid' is not valid."); + } + + this._solution = { organizationSid, accountSid }; + this._uri = `/${organizationSid}/Accounts/${accountSid}`; + } + + fetch( + callback?: (error: Error | null, item?: AccountInstance) => any + ): Promise { + const headers: any = {}; + headers["Accept"] = "application/json"; + + const instance = this; + let operationVersion = instance._version, + operationPromise = operationVersion.fetch({ + uri: instance._uri, + method: "get", + headers, + }); + + operationPromise = operationPromise.then( + (payload) => + new AccountInstance( + operationVersion, + payload, + instance._solution.organizationSid, + instance._solution.accountSid + ) + ); + + operationPromise = instance._version.setPromiseCallback( + operationPromise, + callback + ); + return operationPromise; + } + + /** + * Provide a user-friendly representation + * + * @returns Object + */ + toJSON() { + return this._solution; + } + + [inspect.custom](_depth: any, options: InspectOptions) { + return inspect(this.toJSON(), options); + } +} + +interface AccountPayload extends TwilioResponsePayload { + content: AccountResource[]; +} + +interface AccountResource { + account_sid: string; + friendly_name: string; + status: string; + owner_sid: string; + date_created: Date; +} + +/** + * Page content + */ +export class AccountInstance { + protected _solution: AccountContextSolution; + protected _context?: AccountContext; + + constructor( + protected _version: Versionless, + payload: AccountResource, + organizationSid: string, + accountSid?: string + ) { + this.accountSid = payload.account_sid; + this.friendlyName = payload.friendly_name; + this.status = payload.status; + this.ownerSid = payload.owner_sid; + this.dateCreated = deserialize.iso8601DateTime(payload.date_created); + + this._solution = { + organizationSid, + accountSid: accountSid || this.accountSid, + }; + } + + /** + * Twilio account sid + */ + accountSid: string; + /** + * Account friendly name + */ + friendlyName: string; + /** + * Account status + */ + status: string; + /** + * Twilio account sid + */ + ownerSid: string; + /** + * The date and time when the account was created in the system + */ + dateCreated: Date; + + private get _proxy(): AccountContext { + this._context = + this._context || + new AccountContextImpl( + this._version, + this._solution.organizationSid, + this._solution.accountSid + ); + return this._context; + } + + /** + * Fetch a AccountInstance + * + * @param callback - Callback to handle processed record + * + * @returns Resolves to processed AccountInstance + */ + fetch( + callback?: (error: Error | null, item?: AccountInstance) => any + ): Promise { + return this._proxy.fetch(callback); + } + + /** + * Provide a user-friendly representation + * + * @returns Object + */ + toJSON() { + return { + accountSid: this.accountSid, + friendlyName: this.friendlyName, + status: this.status, + ownerSid: this.ownerSid, + dateCreated: this.dateCreated, + }; + } + + [inspect.custom](_depth: any, options: InspectOptions) { + return inspect(this.toJSON(), options); + } +} + +export interface AccountSolution { + organizationSid: string; +} + +export interface AccountListInstance { + _version: Versionless; + _solution: AccountSolution; + _uri: string; + + (accountSid: string): AccountContext; + get(accountSid: string): AccountContext; + + /** + * Streams AccountInstance records from the API. + * + * This operation lazily loads records as efficiently as possible until the limit + * is reached. + * + * The results are passed into the callback function, so this operation is memory + * efficient. + * + * If a function is passed as the first argument, it will be used as the callback + * function. + * + * @param { AccountListInstanceEachOptions } [params] - Options for request + * @param { function } [callback] - Function to process each record + */ + each( + callback?: (item: AccountInstance, done: (err?: Error) => void) => void + ): void; + each( + params: AccountListInstanceEachOptions, + callback?: (item: AccountInstance, done: (err?: Error) => void) => void + ): void; + /** + * Retrieve a single target page of AccountInstance records from the API. + * + * The request is executed immediately. + * + * @param { string } [targetUrl] - API-generated URL for the requested results page + * @param { function } [callback] - Callback to handle list of records + */ + getPage( + targetUrl: string, + callback?: (error: Error | null, items: AccountPage) => any + ): Promise; + /** + * Lists AccountInstance records from the API as a list. + * + * If a function is passed as the first argument, it will be used as the callback + * function. + * + * @param { AccountListInstanceOptions } [params] - Options for request + * @param { function } [callback] - Callback to handle list of records + */ + list( + callback?: (error: Error | null, items: AccountInstance[]) => any + ): Promise; + list( + params: AccountListInstanceOptions, + callback?: (error: Error | null, items: AccountInstance[]) => any + ): Promise; + /** + * Retrieve a single page of AccountInstance records from the API. + * + * The request is executed immediately. + * + * If a function is passed as the first argument, it will be used as the callback + * function. + * + * @param { AccountListInstancePageOptions } [params] - Options for request + * @param { function } [callback] - Callback to handle list of records + */ + page( + callback?: (error: Error | null, items: AccountPage) => any + ): Promise; + page( + params: AccountListInstancePageOptions, + callback?: (error: Error | null, items: AccountPage) => any + ): Promise; + + /** + * Provide a user-friendly representation + */ + toJSON(): any; + [inspect.custom](_depth: any, options: InspectOptions): any; +} + +export function AccountListInstance( + version: Versionless, + organizationSid: string +): AccountListInstance { + if (!isValidPathParam(organizationSid)) { + throw new Error("Parameter 'organizationSid' is not valid."); + } + + const instance = ((accountSid) => + instance.get(accountSid)) as AccountListInstance; + + instance.get = function get(accountSid): AccountContext { + return new AccountContextImpl(version, organizationSid, accountSid); + }; + + instance._version = version; + instance._solution = { organizationSid }; + instance._uri = `/${organizationSid}/Accounts`; + + instance.page = function page( + params?: + | AccountListInstancePageOptions + | ((error: Error | null, items: AccountPage) => any), + callback?: (error: Error | null, items: AccountPage) => any + ): Promise { + if (params instanceof Function) { + callback = params; + params = {}; + } else { + params = params || {}; + } + + let data: any = {}; + + if (params["pageSize"] !== undefined) data["PageSize"] = params["pageSize"]; + + if (params.pageNumber !== undefined) data["Page"] = params.pageNumber; + if (params.pageToken !== undefined) data["PageToken"] = params.pageToken; + + const headers: any = {}; + headers["Accept"] = "application/json"; + + let operationVersion = version, + operationPromise = operationVersion.page({ + uri: instance._uri, + method: "get", + params: data, + headers, + }); + + operationPromise = operationPromise.then( + (payload) => + new AccountPage(operationVersion, payload, instance._solution) + ); + + operationPromise = instance._version.setPromiseCallback( + operationPromise, + callback + ); + return operationPromise; + }; + instance.each = instance._version.each; + instance.list = instance._version.list; + + instance.getPage = function getPage( + targetUrl: string, + callback?: (error: Error | null, items: AccountPage) => any + ): Promise { + const operationPromise = instance._version._domain.twilio.request({ + method: "get", + uri: targetUrl, + }); + + let pagePromise = operationPromise.then( + (payload) => + new AccountPage(instance._version, payload, instance._solution) + ); + pagePromise = instance._version.setPromiseCallback(pagePromise, callback); + return pagePromise; + }; + + instance.toJSON = function toJSON() { + return instance._solution; + }; + + instance[inspect.custom] = function inspectImpl( + _depth: any, + options: InspectOptions + ) { + return inspect(instance.toJSON(), options); + }; + + return instance; +} + +export class AccountPage extends Page< + Versionless, + AccountPayload, + AccountResource, + AccountInstance +> { + /** + * Initialize the AccountPage + * + * @param version - Version of the resource + * @param response - Response from the API + * @param solution - Path solution + */ + constructor( + version: Versionless, + response: Response, + solution: AccountSolution + ) { + super(version, response, solution); + } + + /** + * Build an instance of AccountInstance + * + * @param payload - Payload response from the API + */ + getInstance(payload: AccountResource): AccountInstance { + return new AccountInstance( + this._version, + payload, + this._solution.organizationSid + ); + } + + [inspect.custom](depth: any, options: InspectOptions) { + return inspect(this.toJSON(), options); + } +} diff --git a/src/rest/previewIam/versionless/organization/roleAssignment.ts b/src/rest/previewIam/versionless/organization/roleAssignment.ts new file mode 100644 index 000000000..dff3be967 --- /dev/null +++ b/src/rest/previewIam/versionless/organization/roleAssignment.ts @@ -0,0 +1,578 @@ +/* + * This code was generated by + * ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + * | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + * | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + * + * Organization Public API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * NOTE: This class is auto generated by OpenAPI Generator. + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { inspect, InspectOptions } from "util"; +import Page, { TwilioResponsePayload } from "../../../../base/Page"; +import Response from "../../../../http/response"; +import Versionless from "../../Versionless"; +const deserialize = require("../../../../base/deserialize"); +const serialize = require("../../../../base/serialize"); +import { isValidPathParam } from "../../../../base/utility"; + +export class PublicApiCreateRoleAssignmentRequest { + /** + * Twilio Role Sid representing assigned role + */ + "role_sid": string; + /** + * Twilio Sid representing scope of this assignment + */ + "scope": string; + /** + * Twilio Sid representing identity of this assignment + */ + "identity": string; +} + +/** + * Options to pass to create a RoleAssignmentInstance + */ +export interface RoleAssignmentListInstanceCreateOptions { + /** */ + publicApiCreateRoleAssignmentRequest: PublicApiCreateRoleAssignmentRequest; +} +/** + * Options to pass to each + */ +export interface RoleAssignmentListInstanceEachOptions { + /** */ + pageSize?: number; + /** */ + identity?: string; + /** */ + scope?: string; + /** Function to process each record. If this and a positional callback are passed, this one will be used */ + callback?: ( + item: RoleAssignmentInstance, + done: (err?: Error) => void + ) => void; + /** Function to be called upon completion of streaming */ + done?: Function; + /** Upper limit for the number of records to return. each() guarantees never to return more than limit. Default is no limit */ + limit?: number; +} + +/** + * Options to pass to list + */ +export interface RoleAssignmentListInstanceOptions { + /** */ + pageSize?: number; + /** */ + identity?: string; + /** */ + scope?: string; + /** Upper limit for the number of records to return. list() guarantees never to return more than limit. Default is no limit */ + limit?: number; +} + +/** + * Options to pass to page + */ +export interface RoleAssignmentListInstancePageOptions { + /** */ + pageSize?: number; + /** */ + identity?: string; + /** */ + scope?: string; + /** Page Number, this value is simply for client state */ + pageNumber?: number; + /** PageToken provided by the API */ + pageToken?: string; +} + +export interface RoleAssignmentContext { + /** + * Remove a RoleAssignmentInstance + * + * @param callback - Callback to handle processed record + * + * @returns Resolves to processed boolean + */ + remove( + callback?: (error: Error | null, item?: boolean) => any + ): Promise; + + /** + * Provide a user-friendly representation + */ + toJSON(): any; + [inspect.custom](_depth: any, options: InspectOptions): any; +} + +export interface RoleAssignmentContextSolution { + organizationSid: string; + sid: string; +} + +export class RoleAssignmentContextImpl implements RoleAssignmentContext { + protected _solution: RoleAssignmentContextSolution; + protected _uri: string; + + constructor( + protected _version: Versionless, + organizationSid: string, + sid: string + ) { + if (!isValidPathParam(organizationSid)) { + throw new Error("Parameter 'organizationSid' is not valid."); + } + + if (!isValidPathParam(sid)) { + throw new Error("Parameter 'sid' is not valid."); + } + + this._solution = { organizationSid, sid }; + this._uri = `/${organizationSid}/RoleAssignments/${sid}`; + } + + remove( + callback?: (error: Error | null, item?: boolean) => any + ): Promise { + const headers: any = {}; + + const instance = this; + let operationVersion = instance._version, + operationPromise = operationVersion.remove({ + uri: instance._uri, + method: "delete", + headers, + }); + + operationPromise = instance._version.setPromiseCallback( + operationPromise, + callback + ); + return operationPromise; + } + + /** + * Provide a user-friendly representation + * + * @returns Object + */ + toJSON() { + return this._solution; + } + + [inspect.custom](_depth: any, options: InspectOptions) { + return inspect(this.toJSON(), options); + } +} + +interface RoleAssignmentPayload extends TwilioResponsePayload { + content: RoleAssignmentResource[]; +} + +interface RoleAssignmentResource { + sid: string; + role_sid: string; + scope: string; + identity: string; + code: number; + message: string; + moreInfo: string; + status: number; +} + +export class RoleAssignmentInstance { + protected _solution: RoleAssignmentContextSolution; + protected _context?: RoleAssignmentContext; + + constructor( + protected _version: Versionless, + payload: RoleAssignmentResource, + organizationSid: string, + sid?: string + ) { + this.sid = payload.sid; + this.roleSid = payload.role_sid; + this.scope = payload.scope; + this.identity = payload.identity; + this.code = payload.code; + this.message = payload.message; + this.moreInfo = payload.moreInfo; + this.status = payload.status; + + this._solution = { organizationSid, sid: sid || this.sid }; + } + + /** + * Twilio Role Assignment Sid representing this role assignment + */ + sid: string; + /** + * Twilio Role Sid representing assigned role + */ + roleSid: string; + /** + * Twilio Sid representing identity of this assignment + */ + scope: string; + /** + * Twilio Sid representing scope of this assignment + */ + identity: string; + /** + * Twilio-specific error code + */ + code: number; + /** + * Error message + */ + message: string; + /** + * Link to Error Code References + */ + moreInfo: string; + /** + * HTTP response status code + */ + status: number; + + private get _proxy(): RoleAssignmentContext { + this._context = + this._context || + new RoleAssignmentContextImpl( + this._version, + this._solution.organizationSid, + this._solution.sid + ); + return this._context; + } + + /** + * Remove a RoleAssignmentInstance + * + * @param callback - Callback to handle processed record + * + * @returns Resolves to processed boolean + */ + remove( + callback?: (error: Error | null, item?: boolean) => any + ): Promise { + return this._proxy.remove(callback); + } + + /** + * Provide a user-friendly representation + * + * @returns Object + */ + toJSON() { + return { + sid: this.sid, + roleSid: this.roleSid, + scope: this.scope, + identity: this.identity, + code: this.code, + message: this.message, + moreInfo: this.moreInfo, + status: this.status, + }; + } + + [inspect.custom](_depth: any, options: InspectOptions) { + return inspect(this.toJSON(), options); + } +} + +export interface RoleAssignmentSolution { + organizationSid: string; +} + +export interface RoleAssignmentListInstance { + _version: Versionless; + _solution: RoleAssignmentSolution; + _uri: string; + + (sid: string): RoleAssignmentContext; + get(sid: string): RoleAssignmentContext; + + /** + * Create a RoleAssignmentInstance + * + * @param params - Body for request + * @param headers - header params for request + * @param callback - Callback to handle processed record + * + * @returns Resolves to processed RoleAssignmentInstance + */ + create( + params: PublicApiCreateRoleAssignmentRequest, + headers?: any, + callback?: (error: Error | null, item?: RoleAssignmentInstance) => any + ): Promise; + + /** + * Streams RoleAssignmentInstance records from the API. + * + * This operation lazily loads records as efficiently as possible until the limit + * is reached. + * + * The results are passed into the callback function, so this operation is memory + * efficient. + * + * If a function is passed as the first argument, it will be used as the callback + * function. + * + * @param { RoleAssignmentListInstanceEachOptions } [params] - Options for request + * @param { function } [callback] - Function to process each record + */ + each( + callback?: ( + item: RoleAssignmentInstance, + done: (err?: Error) => void + ) => void + ): void; + each( + params: RoleAssignmentListInstanceEachOptions, + callback?: ( + item: RoleAssignmentInstance, + done: (err?: Error) => void + ) => void + ): void; + /** + * Retrieve a single target page of RoleAssignmentInstance records from the API. + * + * The request is executed immediately. + * + * @param { string } [targetUrl] - API-generated URL for the requested results page + * @param { function } [callback] - Callback to handle list of records + */ + getPage( + targetUrl: string, + callback?: (error: Error | null, items: RoleAssignmentPage) => any + ): Promise; + /** + * Lists RoleAssignmentInstance records from the API as a list. + * + * If a function is passed as the first argument, it will be used as the callback + * function. + * + * @param { RoleAssignmentListInstanceOptions } [params] - Options for request + * @param { function } [callback] - Callback to handle list of records + */ + list( + callback?: (error: Error | null, items: RoleAssignmentInstance[]) => any + ): Promise; + list( + params: RoleAssignmentListInstanceOptions, + callback?: (error: Error | null, items: RoleAssignmentInstance[]) => any + ): Promise; + /** + * Retrieve a single page of RoleAssignmentInstance records from the API. + * + * The request is executed immediately. + * + * If a function is passed as the first argument, it will be used as the callback + * function. + * + * @param { RoleAssignmentListInstancePageOptions } [params] - Options for request + * @param { function } [callback] - Callback to handle list of records + */ + page( + callback?: (error: Error | null, items: RoleAssignmentPage) => any + ): Promise; + page( + params: RoleAssignmentListInstancePageOptions, + callback?: (error: Error | null, items: RoleAssignmentPage) => any + ): Promise; + + /** + * Provide a user-friendly representation + */ + toJSON(): any; + [inspect.custom](_depth: any, options: InspectOptions): any; +} + +export function RoleAssignmentListInstance( + version: Versionless, + organizationSid: string +): RoleAssignmentListInstance { + if (!isValidPathParam(organizationSid)) { + throw new Error("Parameter 'organizationSid' is not valid."); + } + + const instance = ((sid) => instance.get(sid)) as RoleAssignmentListInstance; + + instance.get = function get(sid): RoleAssignmentContext { + return new RoleAssignmentContextImpl(version, organizationSid, sid); + }; + + instance._version = version; + instance._solution = { organizationSid }; + instance._uri = `/${organizationSid}/RoleAssignments`; + + instance.create = function create( + params: PublicApiCreateRoleAssignmentRequest, + headers?: any, + callback?: (error: Error | null, items: RoleAssignmentInstance) => any + ): Promise { + if (params === null || params === undefined) { + throw new Error('Required parameter "params" missing.'); + } + + let data: any = {}; + + data = params; + + if (headers === null || headers === undefined) { + headers = {}; + } + + headers["Content-Type"] = "application/json"; + headers["Accept"] = "application/json"; + + let operationVersion = version, + operationPromise = operationVersion.create({ + uri: instance._uri, + method: "post", + data, + headers, + }); + + operationPromise = operationPromise.then( + (payload) => + new RoleAssignmentInstance( + operationVersion, + payload, + instance._solution.organizationSid + ) + ); + + operationPromise = instance._version.setPromiseCallback( + operationPromise, + callback + ); + return operationPromise; + }; + + instance.page = function page( + params?: + | RoleAssignmentListInstancePageOptions + | ((error: Error | null, items: RoleAssignmentPage) => any), + callback?: (error: Error | null, items: RoleAssignmentPage) => any + ): Promise { + if (params instanceof Function) { + callback = params; + params = {}; + } else { + params = params || {}; + } + + let data: any = {}; + + if (params["pageSize"] !== undefined) data["PageSize"] = params["pageSize"]; + if (params["identity"] !== undefined) data["Identity"] = params["identity"]; + if (params["scope"] !== undefined) data["Scope"] = params["scope"]; + + if (params.pageNumber !== undefined) data["Page"] = params.pageNumber; + if (params.pageToken !== undefined) data["PageToken"] = params.pageToken; + + const headers: any = {}; + headers["Accept"] = "application/json"; + + let operationVersion = version, + operationPromise = operationVersion.page({ + uri: instance._uri, + method: "get", + params: data, + headers, + }); + + operationPromise = operationPromise.then( + (payload) => + new RoleAssignmentPage(operationVersion, payload, instance._solution) + ); + + operationPromise = instance._version.setPromiseCallback( + operationPromise, + callback + ); + return operationPromise; + }; + instance.each = instance._version.each; + instance.list = instance._version.list; + + instance.getPage = function getPage( + targetUrl: string, + callback?: (error: Error | null, items: RoleAssignmentPage) => any + ): Promise { + const operationPromise = instance._version._domain.twilio.request({ + method: "get", + uri: targetUrl, + }); + + let pagePromise = operationPromise.then( + (payload) => + new RoleAssignmentPage(instance._version, payload, instance._solution) + ); + pagePromise = instance._version.setPromiseCallback(pagePromise, callback); + return pagePromise; + }; + + instance.toJSON = function toJSON() { + return instance._solution; + }; + + instance[inspect.custom] = function inspectImpl( + _depth: any, + options: InspectOptions + ) { + return inspect(instance.toJSON(), options); + }; + + return instance; +} + +export class RoleAssignmentPage extends Page< + Versionless, + RoleAssignmentPayload, + RoleAssignmentResource, + RoleAssignmentInstance +> { + /** + * Initialize the RoleAssignmentPage + * + * @param version - Version of the resource + * @param response - Response from the API + * @param solution - Path solution + */ + constructor( + version: Versionless, + response: Response, + solution: RoleAssignmentSolution + ) { + super(version, response, solution); + } + + /** + * Build an instance of RoleAssignmentInstance + * + * @param payload - Payload response from the API + */ + getInstance(payload: RoleAssignmentResource): RoleAssignmentInstance { + return new RoleAssignmentInstance( + this._version, + payload, + this._solution.organizationSid + ); + } + + [inspect.custom](depth: any, options: InspectOptions) { + return inspect(this.toJSON(), options); + } +} diff --git a/src/rest/previewIam/versionless/organization/user.ts b/src/rest/previewIam/versionless/organization/user.ts new file mode 100644 index 000000000..5d8e3e970 --- /dev/null +++ b/src/rest/previewIam/versionless/organization/user.ts @@ -0,0 +1,851 @@ +/* + * This code was generated by + * ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + * | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + * | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + * + * Organization Public API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * NOTE: This class is auto generated by OpenAPI Generator. + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { inspect, InspectOptions } from "util"; +import Page, { TwilioResponsePayload } from "../../../../base/Page"; +import Response from "../../../../http/response"; +import Versionless from "../../Versionless"; +const deserialize = require("../../../../base/deserialize"); +const serialize = require("../../../../base/serialize"); +import { isValidPathParam } from "../../../../base/utility"; + +/** + * Email address list of the user. Primary email must be defined if there are more than 1 email. Primary email must match the username. + */ +export class ScimEmailAddress { + /** + * Indicates if this email address is the primary one + */ + "primary"?: boolean; + /** + * The actual email address value + */ + "value"?: string; + /** + * The type of email address (e.g., work, home, etc.) + */ + "type"?: string; +} + +/** + * Meta + */ +export class ScimMeta { + /** + * Indicates the type of the resource + */ + "resourceType"?: string; + /** + * The date and time when the resource was created in the system + */ + "created"?: Date; + /** + * The date and time when the resource was last modified + */ + "lastModified"?: Date; + /** + * A version identifier for the resource. This can be used to manage resource versioning and concurrency control. + */ + "version"?: string; +} + +/** + * User\'s name + */ +export class ScimName { + /** + * The user\'s first or given name + */ + "givenName"?: string; + /** + * The user\'s last or family name + */ + "familyName"?: string; +} + +export class ScimUser { + /** + * Unique Twilio user sid + */ + "id"?: string; + /** + * External unique resource id defined by provisioning client + */ + "externalId"?: string; + /** + * Unique username, MUST be same as primary email address + */ + "userName": string; + /** + * User friendly display name + */ + "displayName"?: string; + "name"?: ScimName; + /** + * Email address list of the user. Primary email must be defined if there are more than 1 email. Primary email must match the username. + */ + "emails"?: Array; + /** + * Indicates whether the user is active + */ + "active"?: boolean; + /** + * User\'s locale + */ + "locale"?: string; + /** + * User\'s time zone + */ + "timezone"?: string; + /** + * An array of URIs that indicate the schemas supported for this user resource + */ + "schemas"?: Array; + "meta"?: ScimMeta; + /** + * A human-readable description of the error + */ + "detail"?: string; + /** + * A scimType error code as defined in RFC7644 + */ + "scimType"?: string; + /** + * Http status code + */ + "status"?: string; + /** + * Twilio-specific error code + */ + "code"?: number; + /** + * Link to Error Code References + */ + "moreInfo"?: string; +} + +/** + * Options to pass to update a UserInstance + */ +export interface UserContextUpdateOptions { + /** */ + scimUser: ScimUser; + /** */ + ifMatch?: string; +} + +/** + * Options to pass to create a UserInstance + */ +export interface UserListInstanceCreateOptions { + /** */ + scimUser: ScimUser; +} +/** + * Options to pass to each + */ +export interface UserListInstanceEachOptions { + /** */ + filter?: string; + /** Function to process each record. If this and a positional callback are passed, this one will be used */ + callback?: (item: UserInstance, done: (err?: Error) => void) => void; + /** Function to be called upon completion of streaming */ + done?: Function; + /** Upper limit for the number of records to return. each() guarantees never to return more than limit. Default is no limit */ + limit?: number; +} + +/** + * Options to pass to list + */ +export interface UserListInstanceOptions { + /** */ + filter?: string; + /** Upper limit for the number of records to return. list() guarantees never to return more than limit. Default is no limit */ + limit?: number; +} + +/** + * Options to pass to page + */ +export interface UserListInstancePageOptions { + /** */ + filter?: string; + /** Page Number, this value is simply for client state */ + pageNumber?: number; + /** PageToken provided by the API */ + pageToken?: string; +} + +export interface UserContext { + /** + * Remove a UserInstance + * + * @param callback - Callback to handle processed record + * + * @returns Resolves to processed boolean + */ + remove( + callback?: (error: Error | null, item?: boolean) => any + ): Promise; + + /** + * Fetch a UserInstance + * + * @param callback - Callback to handle processed record + * + * @returns Resolves to processed UserInstance + */ + fetch( + callback?: (error: Error | null, item?: UserInstance) => any + ): Promise; + + /** + * Update a UserInstance + * + * @param params - Body for request + * @param headers - header params for request + * @param callback - Callback to handle processed record + * + * @returns Resolves to processed UserInstance + */ + update( + params: ScimUser, + headers?: any, + callback?: (error: Error | null, item?: UserInstance) => any + ): Promise; + + /** + * Provide a user-friendly representation + */ + toJSON(): any; + [inspect.custom](_depth: any, options: InspectOptions): any; +} + +export interface UserContextSolution { + organizationSid: string; + id: string; +} + +export class UserContextImpl implements UserContext { + protected _solution: UserContextSolution; + protected _uri: string; + + constructor( + protected _version: Versionless, + organizationSid: string, + id: string + ) { + if (!isValidPathParam(organizationSid)) { + throw new Error("Parameter 'organizationSid' is not valid."); + } + + if (!isValidPathParam(id)) { + throw new Error("Parameter 'id' is not valid."); + } + + this._solution = { organizationSid, id }; + this._uri = `/${organizationSid}/scim/Users/${id}`; + } + + remove( + callback?: (error: Error | null, item?: boolean) => any + ): Promise { + const headers: any = {}; + + const instance = this; + let operationVersion = instance._version, + operationPromise = operationVersion.remove({ + uri: instance._uri, + method: "delete", + headers, + }); + + operationPromise = instance._version.setPromiseCallback( + operationPromise, + callback + ); + return operationPromise; + } + + fetch( + callback?: (error: Error | null, item?: UserInstance) => any + ): Promise { + const headers: any = {}; + headers["Accept"] = "application/scim+json"; + + const instance = this; + let operationVersion = instance._version, + operationPromise = operationVersion.fetch({ + uri: instance._uri, + method: "get", + headers, + }); + + operationPromise = operationPromise.then( + (payload) => + new UserInstance( + operationVersion, + payload, + instance._solution.organizationSid, + instance._solution.id + ) + ); + + operationPromise = instance._version.setPromiseCallback( + operationPromise, + callback + ); + return operationPromise; + } + + update( + params: ScimUser, + headers?: any, + callback?: (error: Error | null, item?: UserInstance) => any + ): Promise { + if (params === null || params === undefined) { + throw new Error('Required parameter "params" missing.'); + } + + let data: any = {}; + + data = params; + + if (headers === null || headers === undefined) { + headers = {}; + } + + headers["Content-Type"] = "application/json"; + headers["Accept"] = "application/scim+json"; + + const instance = this; + let operationVersion = instance._version, + operationPromise = operationVersion.update({ + uri: instance._uri, + method: "put", + data, + headers, + }); + + operationPromise = operationPromise.then( + (payload) => + new UserInstance( + operationVersion, + payload, + instance._solution.organizationSid, + instance._solution.id + ) + ); + + operationPromise = instance._version.setPromiseCallback( + operationPromise, + callback + ); + return operationPromise; + } + + /** + * Provide a user-friendly representation + * + * @returns Object + */ + toJSON() { + return this._solution; + } + + [inspect.custom](_depth: any, options: InspectOptions) { + return inspect(this.toJSON(), options); + } +} + +interface UserPayload extends TwilioResponsePayload { + Resources: UserResource[]; +} + +interface UserResource { + id: string; + externalId: string; + userName: string; + displayName: string; + name: ScimName; + emails: Array; + active: boolean; + locale: string; + timezone: string; + schemas: Array; + meta: ScimMeta; + detail: string; + scimType: string; + status: string; + code: number; + moreInfo: string; +} + +export class UserInstance { + protected _solution: UserContextSolution; + protected _context?: UserContext; + + constructor( + protected _version: Versionless, + payload: UserResource, + organizationSid: string, + id?: string + ) { + this.id = payload.id; + this.externalId = payload.externalId; + this.userName = payload.userName; + this.displayName = payload.displayName; + this.name = payload.name; + this.emails = payload.emails; + this.active = payload.active; + this.locale = payload.locale; + this.timezone = payload.timezone; + this.schemas = payload.schemas; + this.meta = payload.meta; + this.detail = payload.detail; + this.scimType = payload.scimType; + this.status = payload.status; + this.code = payload.code; + this.moreInfo = payload.moreInfo; + + this._solution = { organizationSid, id: id || this.id }; + } + + /** + * Unique Twilio user sid + */ + id: string; + /** + * External unique resource id defined by provisioning client + */ + externalId: string; + /** + * Unique username, MUST be same as primary email address + */ + userName: string; + /** + * User friendly display name + */ + displayName: string; + name: ScimName; + /** + * Email address list of the user. Primary email must be defined if there are more than 1 email. Primary email must match the username. + */ + emails: Array; + /** + * Indicates whether the user is active + */ + active: boolean; + /** + * User\'s locale + */ + locale: string; + /** + * User\'s time zone + */ + timezone: string; + /** + * An array of URIs that indicate the schemas supported for this user resource + */ + schemas: Array; + meta: ScimMeta; + /** + * A human-readable description of the error + */ + detail: string; + /** + * A scimType error code as defined in RFC7644 + */ + scimType: string; + /** + * Http status code + */ + status: string; + /** + * Twilio-specific error code + */ + code: number; + /** + * Link to Error Code References + */ + moreInfo: string; + + private get _proxy(): UserContext { + this._context = + this._context || + new UserContextImpl( + this._version, + this._solution.organizationSid, + this._solution.id + ); + return this._context; + } + + /** + * Remove a UserInstance + * + * @param callback - Callback to handle processed record + * + * @returns Resolves to processed boolean + */ + remove( + callback?: (error: Error | null, item?: boolean) => any + ): Promise { + return this._proxy.remove(callback); + } + + /** + * Fetch a UserInstance + * + * @param callback - Callback to handle processed record + * + * @returns Resolves to processed UserInstance + */ + fetch( + callback?: (error: Error | null, item?: UserInstance) => any + ): Promise { + return this._proxy.fetch(callback); + } + + /** + * Update a UserInstance + * + * @param params - Body for request + * @param headers - header params for request + * @param callback - Callback to handle processed record + * + * @returns Resolves to processed UserInstance + */ + update( + params: ScimUser, + headers?: any, + callback?: (error: Error | null, item?: UserInstance) => any + ): Promise; + + update( + params?: any, + callback?: (error: Error | null, item?: UserInstance) => any + ): Promise { + return this._proxy.update(params, callback); + } + + /** + * Provide a user-friendly representation + * + * @returns Object + */ + toJSON() { + return { + id: this.id, + externalId: this.externalId, + userName: this.userName, + displayName: this.displayName, + name: this.name, + emails: this.emails, + active: this.active, + locale: this.locale, + timezone: this.timezone, + schemas: this.schemas, + meta: this.meta, + detail: this.detail, + scimType: this.scimType, + status: this.status, + code: this.code, + moreInfo: this.moreInfo, + }; + } + + [inspect.custom](_depth: any, options: InspectOptions) { + return inspect(this.toJSON(), options); + } +} + +export interface UserSolution { + organizationSid: string; +} + +export interface UserListInstance { + _version: Versionless; + _solution: UserSolution; + _uri: string; + + (id: string): UserContext; + get(id: string): UserContext; + + /** + * Create a UserInstance + * + * @param params - Body for request + * @param headers - header params for request + * @param callback - Callback to handle processed record + * + * @returns Resolves to processed UserInstance + */ + create( + params: ScimUser, + headers?: any, + callback?: (error: Error | null, item?: UserInstance) => any + ): Promise; + + /** + * Streams UserInstance records from the API. + * + * This operation lazily loads records as efficiently as possible until the limit + * is reached. + * + * The results are passed into the callback function, so this operation is memory + * efficient. + * + * If a function is passed as the first argument, it will be used as the callback + * function. + * + * @param { UserListInstanceEachOptions } [params] - Options for request + * @param { function } [callback] - Function to process each record + */ + each( + callback?: (item: UserInstance, done: (err?: Error) => void) => void + ): void; + each( + params: UserListInstanceEachOptions, + callback?: (item: UserInstance, done: (err?: Error) => void) => void + ): void; + /** + * Retrieve a single target page of UserInstance records from the API. + * + * The request is executed immediately. + * + * @param { string } [targetUrl] - API-generated URL for the requested results page + * @param { function } [callback] - Callback to handle list of records + */ + getPage( + targetUrl: string, + callback?: (error: Error | null, items: UserPage) => any + ): Promise; + /** + * Lists UserInstance records from the API as a list. + * + * If a function is passed as the first argument, it will be used as the callback + * function. + * + * @param { UserListInstanceOptions } [params] - Options for request + * @param { function } [callback] - Callback to handle list of records + */ + list( + callback?: (error: Error | null, items: UserInstance[]) => any + ): Promise; + list( + params: UserListInstanceOptions, + callback?: (error: Error | null, items: UserInstance[]) => any + ): Promise; + /** + * Retrieve a single page of UserInstance records from the API. + * + * The request is executed immediately. + * + * If a function is passed as the first argument, it will be used as the callback + * function. + * + * @param { UserListInstancePageOptions } [params] - Options for request + * @param { function } [callback] - Callback to handle list of records + */ + page( + callback?: (error: Error | null, items: UserPage) => any + ): Promise; + page( + params: UserListInstancePageOptions, + callback?: (error: Error | null, items: UserPage) => any + ): Promise; + + /** + * Provide a user-friendly representation + */ + toJSON(): any; + [inspect.custom](_depth: any, options: InspectOptions): any; +} + +export function UserListInstance( + version: Versionless, + organizationSid: string +): UserListInstance { + if (!isValidPathParam(organizationSid)) { + throw new Error("Parameter 'organizationSid' is not valid."); + } + + const instance = ((id) => instance.get(id)) as UserListInstance; + + instance.get = function get(id): UserContext { + return new UserContextImpl(version, organizationSid, id); + }; + + instance._version = version; + instance._solution = { organizationSid }; + instance._uri = `/${organizationSid}/scim/Users`; + + instance.create = function create( + params: ScimUser, + headers?: any, + callback?: (error: Error | null, items: UserInstance) => any + ): Promise { + if (params === null || params === undefined) { + throw new Error('Required parameter "params" missing.'); + } + + let data: any = {}; + + data = params; + + if (headers === null || headers === undefined) { + headers = {}; + } + + headers["Content-Type"] = "application/json"; + headers["Accept"] = "application/scim+json"; + + let operationVersion = version, + operationPromise = operationVersion.create({ + uri: instance._uri, + method: "post", + data, + headers, + }); + + operationPromise = operationPromise.then( + (payload) => + new UserInstance( + operationVersion, + payload, + instance._solution.organizationSid + ) + ); + + operationPromise = instance._version.setPromiseCallback( + operationPromise, + callback + ); + return operationPromise; + }; + + instance.page = function page( + params?: + | UserListInstancePageOptions + | ((error: Error | null, items: UserPage) => any), + callback?: (error: Error | null, items: UserPage) => any + ): Promise { + if (params instanceof Function) { + callback = params; + params = {}; + } else { + params = params || {}; + } + + let data: any = {}; + + if (params["filter"] !== undefined) data["filter"] = params["filter"]; + + if (params.pageNumber !== undefined) data["Page"] = params.pageNumber; + if (params.pageToken !== undefined) data["PageToken"] = params.pageToken; + + const headers: any = {}; + headers["Accept"] = "application/scim+json"; + + let operationVersion = version, + operationPromise = operationVersion.page({ + uri: instance._uri, + method: "get", + params: data, + headers, + }); + + operationPromise = operationPromise.then( + (payload) => new UserPage(operationVersion, payload, instance._solution) + ); + + operationPromise = instance._version.setPromiseCallback( + operationPromise, + callback + ); + return operationPromise; + }; + instance.each = instance._version.each; + instance.list = instance._version.list; + + instance.getPage = function getPage( + targetUrl: string, + callback?: (error: Error | null, items: UserPage) => any + ): Promise { + const operationPromise = instance._version._domain.twilio.request({ + method: "get", + uri: targetUrl, + }); + + let pagePromise = operationPromise.then( + (payload) => new UserPage(instance._version, payload, instance._solution) + ); + pagePromise = instance._version.setPromiseCallback(pagePromise, callback); + return pagePromise; + }; + + instance.toJSON = function toJSON() { + return instance._solution; + }; + + instance[inspect.custom] = function inspectImpl( + _depth: any, + options: InspectOptions + ) { + return inspect(instance.toJSON(), options); + }; + + return instance; +} + +export class UserPage extends Page< + Versionless, + UserPayload, + UserResource, + UserInstance +> { + /** + * Initialize the UserPage + * + * @param version - Version of the resource + * @param response - Response from the API + * @param solution - Path solution + */ + constructor( + version: Versionless, + response: Response, + solution: UserSolution + ) { + super(version, response, solution); + } + + /** + * Build an instance of UserInstance + * + * @param payload - Payload response from the API + */ + getInstance(payload: UserResource): UserInstance { + return new UserInstance( + this._version, + payload, + this._solution.organizationSid + ); + } + + [inspect.custom](depth: any, options: InspectOptions) { + return inspect(this.toJSON(), options); + } +} From 013178d7cb3107242b3d0bd1c6438bbd0a9eb838 Mon Sep 17 00:00:00 2001 From: Shubham Tiwari Date: Wed, 4 Dec 2024 19:22:18 +0530 Subject: [PATCH 02/15] chore: add authStrategy --- src/auth_strategy/AuthStrategy.ts | 8 ++++++++ src/auth_strategy/BasicAuthStrategy.ts | 23 +++++++++++++++++++++++ src/auth_strategy/NoAuthStrategy.ts | 15 +++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 src/auth_strategy/AuthStrategy.ts create mode 100644 src/auth_strategy/BasicAuthStrategy.ts create mode 100644 src/auth_strategy/NoAuthStrategy.ts diff --git a/src/auth_strategy/AuthStrategy.ts b/src/auth_strategy/AuthStrategy.ts new file mode 100644 index 000000000..ac140f02c --- /dev/null +++ b/src/auth_strategy/AuthStrategy.ts @@ -0,0 +1,8 @@ +export default abstract class AuthStrategy { + private authType: string; + protected constructor(authType: string) { + this.authType = authType; + } + abstract getAuthString(): Promise; + abstract requiresAuthentication(): boolean; +} diff --git a/src/auth_strategy/BasicAuthStrategy.ts b/src/auth_strategy/BasicAuthStrategy.ts new file mode 100644 index 000000000..ecee933fb --- /dev/null +++ b/src/auth_strategy/BasicAuthStrategy.ts @@ -0,0 +1,23 @@ +import AuthStrategy from "./AuthStrategy"; + +export default class BasicAuthStrategy extends AuthStrategy { + private username: string; + private password: string; + + constructor(username: string, password: string) { + super("basic"); + this.username = username; + this.password = password; + } + + getAuthString(): Promise { + const auth = Buffer.from(this.username + ":" + this.password).toString( + "base64" + ); + return Promise.resolve(`Basic ${auth}`); + } + + requiresAuthentication(): boolean { + return true; + } +} diff --git a/src/auth_strategy/NoAuthStrategy.ts b/src/auth_strategy/NoAuthStrategy.ts new file mode 100644 index 000000000..069a8844a --- /dev/null +++ b/src/auth_strategy/NoAuthStrategy.ts @@ -0,0 +1,15 @@ +import AuthStrategy from "./AuthStrategy"; + +export default class NoAuthStrategy extends AuthStrategy { + constructor() { + super("noauth"); + } + + getAuthString(): Promise { + return Promise.resolve(""); + } + + requiresAuthentication(): boolean { + return false; + } +} From a9826baa2c275705d57c4b28d8de58ebe67fd245 Mon Sep 17 00:00:00 2001 From: Shubham Tiwari Date: Wed, 4 Dec 2024 19:23:42 +0530 Subject: [PATCH 03/15] chore: add no auth client credential provider --- src/credential_provider/CredentialProvider.ts | 9 +++++++++ .../NoAuthCredentialProvider.ts | 17 +++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 src/credential_provider/CredentialProvider.ts create mode 100644 src/credential_provider/NoAuthCredentialProvider.ts diff --git a/src/credential_provider/CredentialProvider.ts b/src/credential_provider/CredentialProvider.ts new file mode 100644 index 000000000..fc5a747d4 --- /dev/null +++ b/src/credential_provider/CredentialProvider.ts @@ -0,0 +1,9 @@ +import AuthStrategy from "../auth_strategy/AuthStrategy"; + +export default abstract class CredentialProvider { + private authType: string; + protected constructor(authType: string) { + this.authType = authType; + } + abstract toAuthStrategy(): AuthStrategy; +} diff --git a/src/credential_provider/NoAuthCredentialProvider.ts b/src/credential_provider/NoAuthCredentialProvider.ts new file mode 100644 index 000000000..bd2415e5a --- /dev/null +++ b/src/credential_provider/NoAuthCredentialProvider.ts @@ -0,0 +1,17 @@ +import CredentialProvider from "./CredentialProvider"; +import AuthStrategy from "../auth_strategy/AuthStrategy"; +import NoAuthStrategy from "../auth_strategy/NoAuthStrategy"; + +namespace NoAuthCredentialProvider { + export class NoAuthCredentialProvider extends CredentialProvider { + constructor() { + super("client-credentials"); + } + + public toAuthStrategy(): AuthStrategy { + return new NoAuthStrategy(); + } + } +} + +export = NoAuthCredentialProvider; From 4916098306964d91b8a16b971034345ff9ef4aca Mon Sep 17 00:00:00 2001 From: Shubham Tiwari Date: Wed, 4 Dec 2024 19:24:34 +0530 Subject: [PATCH 04/15] chore: add token auth strategy and client credential provider --- src/auth_strategy/TokenAuthStrategy.ts | 67 +++++++++++++++++++ .../ClientCredentialProvider.ts | 66 ++++++++++++++++++ src/http/bearer_token/ApiTokenManager.ts | 36 ++++++++++ src/http/bearer_token/TokenManager.ts | 3 + 4 files changed, 172 insertions(+) create mode 100644 src/auth_strategy/TokenAuthStrategy.ts create mode 100644 src/credential_provider/ClientCredentialProvider.ts create mode 100644 src/http/bearer_token/ApiTokenManager.ts create mode 100644 src/http/bearer_token/TokenManager.ts diff --git a/src/auth_strategy/TokenAuthStrategy.ts b/src/auth_strategy/TokenAuthStrategy.ts new file mode 100644 index 000000000..0f82d8365 --- /dev/null +++ b/src/auth_strategy/TokenAuthStrategy.ts @@ -0,0 +1,67 @@ +import AuthStrategy from "./AuthStrategy"; +import TokenManager from "../http/bearer_token/TokenManager"; +import jwt, { JwtPayload } from "jsonwebtoken"; + +export default class TokenAuthStrategy extends AuthStrategy { + private token: string; + private tokenManager: TokenManager; + + constructor(tokenManager: TokenManager) { + super("token"); + this.token = ""; + this.tokenManager = tokenManager; + } + + async getAuthString(): Promise { + return this.fetchToken() + .then((token) => { + this.token = token; + return `Bearer ${this.token}`; + }) + .catch((error) => { + throw new Error(`Failed to fetch access token: ${error}`); + }); + } + + requiresAuthentication(): boolean { + return true; + } + + async fetchToken(): Promise { + if ( + this.token == null || + this.token.length === 0 || + this.isTokenExpired(this.token) + ) { + return this.tokenManager.fetchToken(); + } + return Promise.resolve(this.token); + } + + /** + * Function to check if the token is expired with a buffer of 30 seconds. + * @param token - The JWT token as a string. + * @returns Boolean indicating if the token is expired. + */ + isTokenExpired(token: string): boolean { + try { + // Decode the token without verifying the signature, as we only want to read the expiration for this check + const decoded = jwt.decode(token) as JwtPayload; + + if (!decoded || !decoded.exp) { + // If the token doesn't have an expiration, consider it expired + return true; + } + + const expiresAt = decoded.exp * 1000; + const bufferMilliseconds = 30 * 1000; + const bufferExpiresAt = expiresAt - bufferMilliseconds; + + // Return true if the current time is after the expiration time with buffer + return Date.now() > bufferExpiresAt; + } catch (error) { + // If there's an error decoding the token, consider it expired + return true; + } + } +} diff --git a/src/credential_provider/ClientCredentialProvider.ts b/src/credential_provider/ClientCredentialProvider.ts new file mode 100644 index 000000000..c1253b090 --- /dev/null +++ b/src/credential_provider/ClientCredentialProvider.ts @@ -0,0 +1,66 @@ +import CredentialProvider from "./CredentialProvider"; +import TokenManager from "../http/bearer_token/TokenManager"; +import AuthStrategy from "../auth_strategy/AuthStrategy"; +import ApiTokenManager from "../http/bearer_token/ApiTokenManager"; +import TokenAuthStrategy from "../auth_strategy/TokenAuthStrategy"; + +class ClientCredentialProvider extends CredentialProvider { + grantType: string; + clientId: string; + clientSecret: string; + tokenManager: TokenManager | null; + + constructor() { + super("client-credentials"); + this.grantType = "client_credentials"; + this.clientId = ""; + this.clientSecret = ""; + this.tokenManager = null; + } + + public toAuthStrategy(): AuthStrategy { + if (this.tokenManager == null) { + this.tokenManager = new ApiTokenManager({ + grantType: this.grantType, + clientId: this.clientId, + clientSecret: this.clientSecret, + }); + } + return new TokenAuthStrategy(this.tokenManager); + } +} + +namespace ClientCredentialProvider { + export class ClientCredentialProviderBuilder { + private readonly instance: ClientCredentialProvider; + + constructor() { + this.instance = new ClientCredentialProvider(); + } + + public setClientId(clientId: string): ClientCredentialProviderBuilder { + this.instance.clientId = clientId; + return this; + } + + public setClientSecret( + clientSecret: string + ): ClientCredentialProviderBuilder { + this.instance.clientSecret = clientSecret; + return this; + } + + public setTokenManager( + tokenManager: TokenManager + ): ClientCredentialProviderBuilder { + this.instance.tokenManager = tokenManager; + return this; + } + + public build(): ClientCredentialProvider { + return this.instance; + } + } +} + +export = ClientCredentialProvider; diff --git a/src/http/bearer_token/ApiTokenManager.ts b/src/http/bearer_token/ApiTokenManager.ts new file mode 100644 index 000000000..e16aadabe --- /dev/null +++ b/src/http/bearer_token/ApiTokenManager.ts @@ -0,0 +1,36 @@ +import TokenManager from "./TokenManager"; +import { + TokenListInstance, + TokenListInstanceCreateOptions, +} from "../../rest/previewIam/v1/token"; +import PreviewIamBase from "../../rest/PreviewIamBase"; +import V1 from "../../rest/previewIam/V1"; +import NoAuthCredentialProvider from "../../credential_provider/NoAuthCredentialProvider"; +import { Client } from "../../base/BaseTwilio"; + +export default class ApiTokenManager implements TokenManager { + private params: TokenListInstanceCreateOptions; + + constructor(params: TokenListInstanceCreateOptions) { + this.params = params; + } + + async fetchToken(): Promise { + const noAuthCredentialProvider = + new NoAuthCredentialProvider.NoAuthCredentialProvider(); + const client = new Client(); + client.setCredentialProvider(noAuthCredentialProvider); + + const tokenListInstance = TokenListInstance( + new V1(new PreviewIamBase(client)) + ); + return tokenListInstance + .create(this.params) + .then((token) => { + return token.accessToken; + }) + .catch((error) => { + throw new Error(`Failed to fetch access token: ${error}`); + }); + } +} diff --git a/src/http/bearer_token/TokenManager.ts b/src/http/bearer_token/TokenManager.ts new file mode 100644 index 000000000..4fa904f8c --- /dev/null +++ b/src/http/bearer_token/TokenManager.ts @@ -0,0 +1,3 @@ +export default abstract class TokenManager { + abstract fetchToken(): Promise; +} From 54b5178c5c2ca30bc0413ba336cb8d06a3fe83b1 Mon Sep 17 00:00:00 2001 From: Shubham Tiwari Date: Wed, 4 Dec 2024 19:24:56 +0530 Subject: [PATCH 05/15] chore: modify client for oauth --- src/base/BaseTwilio.ts | 76 +++++++++++++++++++++++++++++---------- src/base/Page.ts | 2 ++ src/base/RequestClient.ts | 12 +++++-- src/index.ts | 13 +++++++ 4 files changed, 83 insertions(+), 20 deletions(-) diff --git a/src/base/BaseTwilio.ts b/src/base/BaseTwilio.ts index 8cac1826d..3020bc9c9 100644 --- a/src/base/BaseTwilio.ts +++ b/src/base/BaseTwilio.ts @@ -1,6 +1,8 @@ import RequestClient from "./RequestClient"; /* jshint ignore:line */ import { HttpMethod } from "../interfaces"; /* jshint ignore:line */ import { Headers } from "../http/request"; /* jshint ignore:line */ +import AuthStrategy from "../auth_strategy/AuthStrategy"; /* jshint ignore:line */ +import CredentialProvider from "../credential_provider/CredentialProvider"; /* jshint ignore:line */ const os = require("os"); /* jshint ignore:line */ const url = require("url"); /* jshint ignore:line */ @@ -40,6 +42,7 @@ namespace Twilio { uri?: string; username?: string; password?: string; + authStrategy?: AuthStrategy; headers?: Headers; params?: object; data?: object; @@ -56,9 +59,10 @@ namespace Twilio { /* jshint ignore:end */ export class Client { - username: string; - password: string; + username?: string; + password?: string; accountSid: string; + credentialProvider?: CredentialProvider; opts?: ClientOpts; env?: NodeJS.ProcessEnv; edge?: string; @@ -101,23 +105,23 @@ namespace Twilio { /* jshint ignore:end */ constructor(username?: string, password?: string, opts?: ClientOpts) { - this.opts = opts || {}; - this.env = this.opts.env || {}; + this.setOpts(opts); this.username = username ?? - this.env.TWILIO_ACCOUNT_SID ?? - process.env.TWILIO_ACCOUNT_SID ?? - (() => { - throw new Error("username is required"); - })(); + this.env?.TWILIO_ACCOUNT_SID ?? + process.env.TWILIO_ACCOUNT_SID; this.password = password ?? - this.env.TWILIO_AUTH_TOKEN ?? - process.env.TWILIO_AUTH_TOKEN ?? - (() => { - throw new Error("password is required"); - })(); - this.accountSid = this.opts.accountSid || this.username; + this.env?.TWILIO_AUTH_TOKEN ?? + process.env.TWILIO_AUTH_TOKEN; + this.accountSid = ""; + this.setAccountSid(this.opts?.accountSid || this.username); + this.invalidateOAuth(); + } + + setOpts(opts?: ClientOpts) { + this.opts = opts || {}; + this.env = this.opts.env || {}; this.edge = this.opts.edge ?? this.env.TWILIO_EDGE ?? process.env.TWILIO_EDGE; this.region = @@ -144,9 +148,13 @@ namespace Twilio { if (this.opts.lazyLoading === false) { this._httpClient = this.httpClient; } + } - if (!this.accountSid.startsWith("AC")) { - const apiKeyMsg = this.accountSid.startsWith("SK") + setAccountSid(accountSid?: string) { + this.accountSid = accountSid || ""; + + if (this.accountSid && !this.accountSid?.startsWith("AC")) { + const apiKeyMsg = this.accountSid?.startsWith("SK") ? ". The given SID indicates an API Key which requires the accountSid to be passed as an additional option" : ""; @@ -154,6 +162,21 @@ namespace Twilio { } } + setCredentialProvider(credentialProvider: CredentialProvider) { + this.credentialProvider = credentialProvider; + this.accountSid = ""; + this.invalidateBasicAuth(); + } + + invalidateBasicAuth() { + this.username = undefined; + this.password = undefined; + } + + invalidateOAuth() { + this.credentialProvider = undefined; + } + get httpClient() { if (!this._httpClient) { this._httpClient = new RequestClient({ @@ -196,6 +219,22 @@ namespace Twilio { const username = opts.username || this.username; const password = opts.password || this.password; + const authStrategy = + opts.authStrategy || this.credentialProvider?.toAuthStrategy(); + + if (!authStrategy) { + if (!username) { + (() => { + throw new Error("username is required"); + })(); + } + + if (!password) { + (() => { + throw new Error("password is required"); + })(); + } + } const headers = opts.headers || {}; @@ -223,7 +262,7 @@ namespace Twilio { headers["Content-Type"] = "application/x-www-form-urlencoded"; } - if (!headers["Accept"]) { + if (opts.method !== "delete" && !headers["Accept"]) { headers["Accept"] = "application/json"; } @@ -235,6 +274,7 @@ namespace Twilio { uri: uri.href, username: username, password: password, + authStrategy: authStrategy, headers: headers, params: opts.params, data: opts.data, diff --git a/src/base/Page.ts b/src/base/Page.ts index 2230e4715..e2062e270 100644 --- a/src/base/Page.ts +++ b/src/base/Page.ts @@ -249,6 +249,8 @@ export default class Page< if (keys.length === 1) { return payload[keys[0]]; } + for (const key of keys) + if (Array.isArray(payload[key])) return payload[key]; throw new Error("Page Records cannot be deserialized"); } diff --git a/src/base/RequestClient.ts b/src/base/RequestClient.ts index 02c9134fe..4fb29bb53 100644 --- a/src/base/RequestClient.ts +++ b/src/base/RequestClient.ts @@ -6,9 +6,10 @@ import qs from "qs"; import * as https from "https"; import Response from "../http/response"; import Request, { - RequestOptions as LastRequestOptions, Headers, + RequestOptions as LastRequestOptions, } from "../http/request"; +import AuthStrategy from "../auth_strategy/AuthStrategy"; const DEFAULT_CONTENT_TYPE = "application/x-www-form-urlencoded"; const DEFAULT_TIMEOUT = 30000; @@ -149,6 +150,7 @@ class RequestClient { * @param opts.uri - The request uri * @param opts.username - The username used for auth * @param opts.password - The password used for auth + * @param opts.authStrategy - The authStrategy for API call * @param opts.headers - The request headers * @param opts.params - The request params * @param opts.data - The request data @@ -157,7 +159,7 @@ class RequestClient { * @param opts.forever - Set to true to use the forever-agent * @param opts.logLevel - Show debug logs */ - request( + async request( opts: RequestClient.RequestOptions ): Promise> { if (!opts.method) { @@ -180,6 +182,8 @@ class RequestClient { "base64" ); headers.Authorization = "Basic " + auth; + } else if (opts.authStrategy) { + headers.Authorization = await opts.authStrategy.getAuthString(); } const options: AxiosRequestConfig = { @@ -296,6 +300,10 @@ namespace RequestClient { * The password used for auth */ password?: string; + /** + * The AuthStrategy for API Call + */ + authStrategy?: AuthStrategy; /** * The request headers */ diff --git a/src/index.ts b/src/index.ts index 1d22dba6a..401c508d5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,6 +9,8 @@ import * as taskRouterUtil from "./jwt/taskrouter/util"; import IVoiceResponse from "./twiml/VoiceResponse"; import IMessagingResponse from "./twiml/MessagingResponse"; import IFaxResponse from "./twiml/FaxResponse"; +import IClientCredentialProvider from "./credential_provider/ClientCredentialProvider"; +import INoAuthCredentialProvider from "./credential_provider/NoAuthCredentialProvider"; // Shorthand to automatically create a RestClient function TwilioSDK( @@ -44,6 +46,17 @@ namespace TwilioSDK { } export type RequestClient = IRequestClient; export const RequestClient = IRequestClient; + + export type ClientCredentialProviderBuilder = + IClientCredentialProvider.ClientCredentialProviderBuilder; + export const ClientCredentialProviderBuilder = + IClientCredentialProvider.ClientCredentialProviderBuilder; + + export type NoAuthCredentialProvider = + INoAuthCredentialProvider.NoAuthCredentialProvider; + export const NoAuthCredentialProvider = + INoAuthCredentialProvider.NoAuthCredentialProvider; + // Setup webhook helper functionality export type validateBody = typeof webhooks.validateBody; export const validateBody = webhooks.validateBody; From 28d9d1d2274dea2b79d130eb8bbe18acdbcd7672 Mon Sep 17 00:00:00 2001 From: Shubham Tiwari Date: Thu, 5 Dec 2024 14:33:33 +0530 Subject: [PATCH 06/15] chore: add orgs credential provider --- .../NoAuthCredentialProvider.ts | 2 +- .../OrgsCredentialProvider.ts | 66 +++++++++++++++++++ src/http/bearer_token/OrgsTokenManager.ts | 40 +++++++++++ 3 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 src/credential_provider/OrgsCredentialProvider.ts create mode 100644 src/http/bearer_token/OrgsTokenManager.ts diff --git a/src/credential_provider/NoAuthCredentialProvider.ts b/src/credential_provider/NoAuthCredentialProvider.ts index bd2415e5a..c36d9013f 100644 --- a/src/credential_provider/NoAuthCredentialProvider.ts +++ b/src/credential_provider/NoAuthCredentialProvider.ts @@ -5,7 +5,7 @@ import NoAuthStrategy from "../auth_strategy/NoAuthStrategy"; namespace NoAuthCredentialProvider { export class NoAuthCredentialProvider extends CredentialProvider { constructor() { - super("client-credentials"); + super("noauth"); } public toAuthStrategy(): AuthStrategy { diff --git a/src/credential_provider/OrgsCredentialProvider.ts b/src/credential_provider/OrgsCredentialProvider.ts new file mode 100644 index 000000000..c1dfe3124 --- /dev/null +++ b/src/credential_provider/OrgsCredentialProvider.ts @@ -0,0 +1,66 @@ +import CredentialProvider from "./CredentialProvider"; +import TokenManager from "../http/bearer_token/TokenManager"; +import AuthStrategy from "../auth_strategy/AuthStrategy"; +import OrgsTokenManager from "../http/bearer_token/OrgsTokenManager"; +import TokenAuthStrategy from "../auth_strategy/TokenAuthStrategy"; + +class OrgsCredentialProvider extends CredentialProvider { + grantType: string; + clientId: string; + clientSecret: string; + tokenManager: TokenManager | null; + + constructor() { + super("client-credentials"); + this.grantType = "client_credentials"; + this.clientId = ""; + this.clientSecret = ""; + this.tokenManager = null; + } + + public toAuthStrategy(): AuthStrategy { + if (this.tokenManager == null) { + this.tokenManager = new OrgsTokenManager({ + grantType: this.grantType, + clientId: this.clientId, + clientSecret: this.clientSecret, + }); + } + return new TokenAuthStrategy(this.tokenManager); + } +} + +namespace OrgsCredentialProvider { + export class OrgsCredentialProviderBuilder { + private readonly instance: OrgsCredentialProvider; + + constructor() { + this.instance = new OrgsCredentialProvider(); + } + + public setClientId(clientId: string): OrgsCredentialProviderBuilder { + this.instance.clientId = clientId; + return this; + } + + public setClientSecret( + clientSecret: string + ): OrgsCredentialProviderBuilder { + this.instance.clientSecret = clientSecret; + return this; + } + + public setTokenManager( + tokenManager: TokenManager + ): OrgsCredentialProviderBuilder { + this.instance.tokenManager = tokenManager; + return this; + } + + public build(): OrgsCredentialProvider { + return this.instance; + } + } +} + +export = OrgsCredentialProvider; diff --git a/src/http/bearer_token/OrgsTokenManager.ts b/src/http/bearer_token/OrgsTokenManager.ts new file mode 100644 index 000000000..b620ad2e8 --- /dev/null +++ b/src/http/bearer_token/OrgsTokenManager.ts @@ -0,0 +1,40 @@ +import TokenManager from "./TokenManager"; +import { + TokenListInstance, + TokenListInstanceCreateOptions, +} from "../../rest/previewIam/v1/token"; +import PreviewIamBase from "../../rest/PreviewIamBase"; +import V1 from "../../rest/previewIam/V1"; +import NoAuthCredentialProvider from "../../credential_provider/NoAuthCredentialProvider"; +import { Client } from "../../base/BaseTwilio"; + +export default class OrgsTokenManager implements TokenManager { + private readonly params: TokenListInstanceCreateOptions; + + constructor(params: TokenListInstanceCreateOptions) { + this.params = params; + } + + getParams(): TokenListInstanceCreateOptions { + return this.params; + } + + async fetchToken(): Promise { + const noAuthCredentialProvider = + new NoAuthCredentialProvider.NoAuthCredentialProvider(); + const client = new Client(); + client.setCredentialProvider(noAuthCredentialProvider); + + const tokenListInstance = TokenListInstance( + new V1(new PreviewIamBase(client)) + ); + return tokenListInstance + .create(this.params) + .then((token) => { + return token.accessToken; + }) + .catch((error) => { + throw new Error(`Failed to fetch access token: ${error}`); + }); + } +} From c62e52437b9d0c8a5ea6cc5fdfdf163edbe0edd6 Mon Sep 17 00:00:00 2001 From: Shubham Tiwari Date: Thu, 5 Dec 2024 14:33:52 +0530 Subject: [PATCH 07/15] chore: add orgs credential provider --- src/index.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/index.ts b/src/index.ts index 401c508d5..85deed246 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,7 @@ import IMessagingResponse from "./twiml/MessagingResponse"; import IFaxResponse from "./twiml/FaxResponse"; import IClientCredentialProvider from "./credential_provider/ClientCredentialProvider"; import INoAuthCredentialProvider from "./credential_provider/NoAuthCredentialProvider"; +import IOrgsCredentialProvider from "./credential_provider/OrgsCredentialProvider"; // Shorthand to automatically create a RestClient function TwilioSDK( @@ -52,6 +53,11 @@ namespace TwilioSDK { export const ClientCredentialProviderBuilder = IClientCredentialProvider.ClientCredentialProviderBuilder; + export type OrgsCredentialProviderBuilder = + IOrgsCredentialProvider.OrgsCredentialProviderBuilder; + export const OrgsCredentialProviderBuilder = + IOrgsCredentialProvider.OrgsCredentialProviderBuilder; + export type NoAuthCredentialProvider = INoAuthCredentialProvider.NoAuthCredentialProvider; export const NoAuthCredentialProvider = From 3f8023db347599fbffc9a7c44ec784193fbf0b2e Mon Sep 17 00:00:00 2001 From: Shubham Tiwari Date: Thu, 5 Dec 2024 14:38:20 +0530 Subject: [PATCH 08/15] chore: run prettier --- src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index 85deed246..d3f10b167 100644 --- a/src/index.ts +++ b/src/index.ts @@ -54,9 +54,9 @@ namespace TwilioSDK { IClientCredentialProvider.ClientCredentialProviderBuilder; export type OrgsCredentialProviderBuilder = - IOrgsCredentialProvider.OrgsCredentialProviderBuilder; + IOrgsCredentialProvider.OrgsCredentialProviderBuilder; export const OrgsCredentialProviderBuilder = - IOrgsCredentialProvider.OrgsCredentialProviderBuilder; + IOrgsCredentialProvider.OrgsCredentialProviderBuilder; export type NoAuthCredentialProvider = INoAuthCredentialProvider.NoAuthCredentialProvider; From 086984f2e1f450e8e6611ec10631cc4d93a80e33 Mon Sep 17 00:00:00 2001 From: Shubham Tiwari Date: Thu, 5 Dec 2024 20:33:22 +0530 Subject: [PATCH 09/15] chore: added getters and error messages --- src/auth_strategy/AuthStrategy.ts | 3 +++ src/auth_strategy/TokenAuthStrategy.ts | 2 +- src/credential_provider/CredentialProvider.ts | 3 +++ src/http/bearer_token/ApiTokenManager.ts | 6 +++++- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/auth_strategy/AuthStrategy.ts b/src/auth_strategy/AuthStrategy.ts index ac140f02c..44568ac69 100644 --- a/src/auth_strategy/AuthStrategy.ts +++ b/src/auth_strategy/AuthStrategy.ts @@ -3,6 +3,9 @@ export default abstract class AuthStrategy { protected constructor(authType: string) { this.authType = authType; } + getAuthType(): string { + return this.authType; + } abstract getAuthString(): Promise; abstract requiresAuthentication(): boolean; } diff --git a/src/auth_strategy/TokenAuthStrategy.ts b/src/auth_strategy/TokenAuthStrategy.ts index 0f82d8365..147f8a9f0 100644 --- a/src/auth_strategy/TokenAuthStrategy.ts +++ b/src/auth_strategy/TokenAuthStrategy.ts @@ -19,7 +19,7 @@ export default class TokenAuthStrategy extends AuthStrategy { return `Bearer ${this.token}`; }) .catch((error) => { - throw new Error(`Failed to fetch access token: ${error}`); + throw new Error(`Failed to fetch access token: ${error.message}`); }); } diff --git a/src/credential_provider/CredentialProvider.ts b/src/credential_provider/CredentialProvider.ts index fc5a747d4..759f4d8f0 100644 --- a/src/credential_provider/CredentialProvider.ts +++ b/src/credential_provider/CredentialProvider.ts @@ -5,5 +5,8 @@ export default abstract class CredentialProvider { protected constructor(authType: string) { this.authType = authType; } + getAuthType(): string { + return this.authType; + } abstract toAuthStrategy(): AuthStrategy; } diff --git a/src/http/bearer_token/ApiTokenManager.ts b/src/http/bearer_token/ApiTokenManager.ts index e16aadabe..5d7406c39 100644 --- a/src/http/bearer_token/ApiTokenManager.ts +++ b/src/http/bearer_token/ApiTokenManager.ts @@ -15,6 +15,10 @@ export default class ApiTokenManager implements TokenManager { this.params = params; } + getParams(): TokenListInstanceCreateOptions { + return this.params; + } + async fetchToken(): Promise { const noAuthCredentialProvider = new NoAuthCredentialProvider.NoAuthCredentialProvider(); @@ -30,7 +34,7 @@ export default class ApiTokenManager implements TokenManager { return token.accessToken; }) .catch((error) => { - throw new Error(`Failed to fetch access token: ${error}`); + throw new Error(`Error Status Code: ${error.status}\nFailed to fetch access token: ${error.message}`); }); } } From 7fe5675b610692c0eb01bec5ce1877943bcd009f Mon Sep 17 00:00:00 2001 From: Shubham Tiwari Date: Thu, 5 Dec 2024 20:33:41 +0530 Subject: [PATCH 10/15] chore: added authStrategy tests --- .../auth_strategy/BasicAuthStrategy.spec.ts | 23 ++++ .../unit/auth_strategy/NoAuthStrategy.spec.ts | 20 +++ .../auth_strategy/TokenAuthStrategy.spec.ts | 126 ++++++++++++++++++ 3 files changed, 169 insertions(+) create mode 100644 spec/unit/auth_strategy/BasicAuthStrategy.spec.ts create mode 100644 spec/unit/auth_strategy/NoAuthStrategy.spec.ts create mode 100644 spec/unit/auth_strategy/TokenAuthStrategy.spec.ts diff --git a/spec/unit/auth_strategy/BasicAuthStrategy.spec.ts b/spec/unit/auth_strategy/BasicAuthStrategy.spec.ts new file mode 100644 index 000000000..ee4851393 --- /dev/null +++ b/spec/unit/auth_strategy/BasicAuthStrategy.spec.ts @@ -0,0 +1,23 @@ +import BasicAuthStrategy from "../../../src/auth_strategy/BasicAuthStrategy"; + +describe("NoAuthStrategy constructor", function () { + const username = "username"; + const password = "password"; + const basicAuthStrategy = new BasicAuthStrategy(username, password); + + it("Should have basic as its authType", function () { + expect(basicAuthStrategy.getAuthType()).toEqual("basic"); + }); + + it("Should return basic auth string", function (done) { + const auth = Buffer.from(username + ":" + password).toString("base64"); + basicAuthStrategy.getAuthString().then(function (authString) { + expect(authString).toEqual(`Basic ${auth}`); + done(); + }); + }); + + it("Should return true for requiresAuthentication", function () { + expect(basicAuthStrategy.requiresAuthentication()).toBe(true); + }); +}); diff --git a/spec/unit/auth_strategy/NoAuthStrategy.spec.ts b/spec/unit/auth_strategy/NoAuthStrategy.spec.ts new file mode 100644 index 000000000..08a31da11 --- /dev/null +++ b/spec/unit/auth_strategy/NoAuthStrategy.spec.ts @@ -0,0 +1,20 @@ +import NoAuthStrategy from "../../../src/auth_strategy/NoAuthStrategy"; + +describe("NoAuthStrategy constructor", function () { + const noAuthStrategy = new NoAuthStrategy(); + + it("Should have noauth as its authType", function () { + expect(noAuthStrategy.getAuthType()).toEqual("noauth"); + }); + + it("Should return an empty string for getAuthString", function (done) { + noAuthStrategy.getAuthString().then(function (authString) { + expect(authString).toEqual(""); + done(); + }); + }); + + it("Should return false for requiresAuthentication", function () { + expect(noAuthStrategy.requiresAuthentication()).toBe(false); + }); +}); diff --git a/spec/unit/auth_strategy/TokenAuthStrategy.spec.ts b/spec/unit/auth_strategy/TokenAuthStrategy.spec.ts new file mode 100644 index 000000000..b9e689e73 --- /dev/null +++ b/spec/unit/auth_strategy/TokenAuthStrategy.spec.ts @@ -0,0 +1,126 @@ +import TokenAuthStrategy from "../../../src/auth_strategy/TokenAuthStrategy"; +import ApiTokenManager from "../../../src/http/bearer_token/ApiTokenManager"; +import {jest} from "@jest/globals"; +import axios from "axios"; +import twilio from "../../../src"; + +function createMockAxios(promiseHandler: Promise) { + const instance = () => promiseHandler; + instance.defaults = { + headers: { + post: {}, + }, + }; + return instance; +} + +describe("NoAuthStrategy constructor", function () { + const clientId = "clientId"; + const clientSecret = "clientSecret"; + const grantType = "client_credentials"; + + const tokenManager = new ApiTokenManager({ + grantType: grantType, + clientId: clientId, + clientSecret: clientSecret, + }); + const tokenAuthStrategy = new TokenAuthStrategy(tokenManager); + + let createSpy: jest.Spied; + const initialHttpProxyValue = process.env.HTTP_PROXY; + + beforeEach(() => { + createSpy = jest.spyOn(axios, 'create'); + createSpy.mockReturnValue( + createMockAxios( + Promise.resolve({ + status: 200, + data: { + "access_token": "accessTokenValue", + "token_type": "Bearer" + }, + }), + ), + ); + }); + + afterEach(() => { + createSpy.mockRestore(); + + if (initialHttpProxyValue) { + process.env.HTTP_PROXY = initialHttpProxyValue; + } else { + delete process.env.HTTP_PROXY; + } + }); + + it("Should have token as its authType", function () { + expect(tokenAuthStrategy.getAuthType()).toEqual("token"); + }); + + it("Should check token expiry", function () { + const accountSid = "ACaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + const keySid = "SKb5aed9ca12bf5890f37930e63cad6d38"; + const token = + new twilio.jwt.AccessToken(accountSid, keySid, "secret", { + identity: "ID@example.com", + }); + expect(tokenAuthStrategy.isTokenExpired(token.toJwt())).toBe(false); + }); + + it("Should return token auth string", function (done) { + tokenAuthStrategy.getAuthString().then(function (authString) { + expect(authString).toEqual(`Bearer accessTokenValue`); + done(); + }); + }); + + it("Should return true for requiresAuthentication", function () { + expect(tokenAuthStrategy.requiresAuthentication()).toBe(true); + }); +}); + +describe("NoAuthStrategy error response", function () { + const clientId = "clientId"; + const clientSecret = "clientSecret"; + const grantType = "client_credentials"; + + const tokenManager = new ApiTokenManager({ + grantType: grantType, + clientId: clientId, + clientSecret: clientSecret, + }); + const tokenAuthStrategy = new TokenAuthStrategy(tokenManager); + + let createSpy: jest.Spied; + const initialHttpProxyValue = process.env.HTTP_PROXY; + + beforeEach(() => { + createSpy = jest.spyOn(axios, 'create'); + createSpy.mockReturnValue( + createMockAxios( + Promise.resolve({ + status: 403, + data: { + "message": "Invalid Credentials", + }, + }), + ), + ); + }); + + afterEach(() => { + createSpy.mockRestore(); + + if (initialHttpProxyValue) { + process.env.HTTP_PROXY = initialHttpProxyValue; + } else { + delete process.env.HTTP_PROXY; + } + }); + + it("Should return error", async function () { + await expect(tokenAuthStrategy.getAuthString()).rejects.toThrow("Failed to fetch access token: Invalid Credentials"); + }); + +}); From 07d1651b8fe084b66f9b7dad99ea51fbe4e451f6 Mon Sep 17 00:00:00 2001 From: Shubham Tiwari Date: Thu, 5 Dec 2024 20:34:33 +0530 Subject: [PATCH 11/15] chore: run prettier --- .../auth_strategy/TokenAuthStrategy.spec.ts | 52 +++++++++---------- src/http/bearer_token/ApiTokenManager.ts | 4 +- 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/spec/unit/auth_strategy/TokenAuthStrategy.spec.ts b/spec/unit/auth_strategy/TokenAuthStrategy.spec.ts index b9e689e73..01ffb11db 100644 --- a/spec/unit/auth_strategy/TokenAuthStrategy.spec.ts +++ b/spec/unit/auth_strategy/TokenAuthStrategy.spec.ts @@ -1,6 +1,6 @@ import TokenAuthStrategy from "../../../src/auth_strategy/TokenAuthStrategy"; import ApiTokenManager from "../../../src/http/bearer_token/ApiTokenManager"; -import {jest} from "@jest/globals"; +import { jest } from "@jest/globals"; import axios from "axios"; import twilio from "../../../src"; @@ -30,17 +30,17 @@ describe("NoAuthStrategy constructor", function () { const initialHttpProxyValue = process.env.HTTP_PROXY; beforeEach(() => { - createSpy = jest.spyOn(axios, 'create'); + createSpy = jest.spyOn(axios, "create"); createSpy.mockReturnValue( - createMockAxios( - Promise.resolve({ - status: 200, - data: { - "access_token": "accessTokenValue", - "token_type": "Bearer" - }, - }), - ), + createMockAxios( + Promise.resolve({ + status: 200, + data: { + access_token: "accessTokenValue", + token_type: "Bearer", + }, + }) + ) ); }); @@ -61,10 +61,9 @@ describe("NoAuthStrategy constructor", function () { it("Should check token expiry", function () { const accountSid = "ACaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; const keySid = "SKb5aed9ca12bf5890f37930e63cad6d38"; - const token = - new twilio.jwt.AccessToken(accountSid, keySid, "secret", { - identity: "ID@example.com", - }); + const token = new twilio.jwt.AccessToken(accountSid, keySid, "secret", { + identity: "ID@example.com", + }); expect(tokenAuthStrategy.isTokenExpired(token.toJwt())).toBe(false); }); @@ -96,16 +95,16 @@ describe("NoAuthStrategy error response", function () { const initialHttpProxyValue = process.env.HTTP_PROXY; beforeEach(() => { - createSpy = jest.spyOn(axios, 'create'); + createSpy = jest.spyOn(axios, "create"); createSpy.mockReturnValue( - createMockAxios( - Promise.resolve({ - status: 403, - data: { - "message": "Invalid Credentials", - }, - }), - ), + createMockAxios( + Promise.resolve({ + status: 403, + data: { + message: "Invalid Credentials", + }, + }) + ) ); }); @@ -120,7 +119,8 @@ describe("NoAuthStrategy error response", function () { }); it("Should return error", async function () { - await expect(tokenAuthStrategy.getAuthString()).rejects.toThrow("Failed to fetch access token: Invalid Credentials"); + await expect(tokenAuthStrategy.getAuthString()).rejects.toThrow( + "Failed to fetch access token: Invalid Credentials" + ); }); - }); diff --git a/src/http/bearer_token/ApiTokenManager.ts b/src/http/bearer_token/ApiTokenManager.ts index 5d7406c39..a6861f074 100644 --- a/src/http/bearer_token/ApiTokenManager.ts +++ b/src/http/bearer_token/ApiTokenManager.ts @@ -34,7 +34,9 @@ export default class ApiTokenManager implements TokenManager { return token.accessToken; }) .catch((error) => { - throw new Error(`Error Status Code: ${error.status}\nFailed to fetch access token: ${error.message}`); + throw new Error( + `Error Status Code: ${error.status}\nFailed to fetch access token: ${error.message}` + ); }); } } From e01b79646fe7ba5e8337e3884ab1ace23b1f5897 Mon Sep 17 00:00:00 2001 From: Shubham Tiwari Date: Thu, 5 Dec 2024 20:41:11 +0530 Subject: [PATCH 12/15] chore: fix test describe --- spec/unit/auth_strategy/BasicAuthStrategy.spec.ts | 2 +- spec/unit/auth_strategy/TokenAuthStrategy.spec.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/unit/auth_strategy/BasicAuthStrategy.spec.ts b/spec/unit/auth_strategy/BasicAuthStrategy.spec.ts index ee4851393..bb66d2f5d 100644 --- a/spec/unit/auth_strategy/BasicAuthStrategy.spec.ts +++ b/spec/unit/auth_strategy/BasicAuthStrategy.spec.ts @@ -1,6 +1,6 @@ import BasicAuthStrategy from "../../../src/auth_strategy/BasicAuthStrategy"; -describe("NoAuthStrategy constructor", function () { +describe("BasicAuthStrategy constructor", function () { const username = "username"; const password = "password"; const basicAuthStrategy = new BasicAuthStrategy(username, password); diff --git a/spec/unit/auth_strategy/TokenAuthStrategy.spec.ts b/spec/unit/auth_strategy/TokenAuthStrategy.spec.ts index 01ffb11db..909a1bcf9 100644 --- a/spec/unit/auth_strategy/TokenAuthStrategy.spec.ts +++ b/spec/unit/auth_strategy/TokenAuthStrategy.spec.ts @@ -14,7 +14,7 @@ function createMockAxios(promiseHandler: Promise) { return instance; } -describe("NoAuthStrategy constructor", function () { +describe("TokenAuthStrategy constructor", function () { const clientId = "clientId"; const clientSecret = "clientSecret"; const grantType = "client_credentials"; @@ -79,7 +79,7 @@ describe("NoAuthStrategy constructor", function () { }); }); -describe("NoAuthStrategy error response", function () { +describe("TokenAuthStrategy error response", function () { const clientId = "clientId"; const clientSecret = "clientSecret"; const grantType = "client_credentials"; From 4968d9ffd12d1041738e61dd24f0265d6aa8918b Mon Sep 17 00:00:00 2001 From: Shubham Tiwari Date: Thu, 5 Dec 2024 20:41:29 +0530 Subject: [PATCH 13/15] chore: add credential provider tests --- .../ClientCredentialProvider.spec.ts | 22 +++++++++++++++++++ .../NoAuthCredentialProvider.spec.ts | 17 ++++++++++++++ .../OrgsCredentialProvider.spec.ts | 22 +++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 spec/unit/credential_provider/ClientCredentialProvider.spec.ts create mode 100644 spec/unit/credential_provider/NoAuthCredentialProvider.spec.ts create mode 100644 spec/unit/credential_provider/OrgsCredentialProvider.spec.ts diff --git a/spec/unit/credential_provider/ClientCredentialProvider.spec.ts b/spec/unit/credential_provider/ClientCredentialProvider.spec.ts new file mode 100644 index 000000000..ad2a7e154 --- /dev/null +++ b/spec/unit/credential_provider/ClientCredentialProvider.spec.ts @@ -0,0 +1,22 @@ +import ClientCredentialProvider from "../../../src/credential_provider/ClientCredentialProvider"; +import TokenAuthStrategy from "../../../src/auth_strategy/TokenAuthStrategy"; + +describe("ClientCredentialProvider Constructor", () => { + const clientCredentialProvider = + new ClientCredentialProvider.ClientCredentialProviderBuilder() + .setClientId("clientId") + .setClientSecret("clientSecret") + .build(); + + it("Should have client-credentials as its authType", () => { + expect(clientCredentialProvider.getAuthType()).toEqual( + "client-credentials" + ); + }); + + it("Should return NoAuthStrategy as its auth strategy", () => { + expect(clientCredentialProvider.toAuthStrategy()).toBeInstanceOf( + TokenAuthStrategy + ); + }); +}); diff --git a/spec/unit/credential_provider/NoAuthCredentialProvider.spec.ts b/spec/unit/credential_provider/NoAuthCredentialProvider.spec.ts new file mode 100644 index 000000000..b86568361 --- /dev/null +++ b/spec/unit/credential_provider/NoAuthCredentialProvider.spec.ts @@ -0,0 +1,17 @@ +import NoAuthCredentialProvider from "../../../src/credential_provider/NoAuthCredentialProvider"; +import NoAuthStrategy from "../../../src/auth_strategy/NoAuthStrategy"; + +describe("NoAuthCredentialProvider Constructor", () => { + const noAuthCredentialProvider = + new NoAuthCredentialProvider.NoAuthCredentialProvider(); + + it("Should have client-credentials as its authType", () => { + expect(noAuthCredentialProvider.getAuthType()).toEqual("noauth"); + }); + + it("Should return NoAuthStrategy as its auth strategy", () => { + expect(noAuthCredentialProvider.toAuthStrategy()).toBeInstanceOf( + NoAuthStrategy + ); + }); +}); diff --git a/spec/unit/credential_provider/OrgsCredentialProvider.spec.ts b/spec/unit/credential_provider/OrgsCredentialProvider.spec.ts new file mode 100644 index 000000000..8fbed0d09 --- /dev/null +++ b/spec/unit/credential_provider/OrgsCredentialProvider.spec.ts @@ -0,0 +1,22 @@ +import OrgsCredentialProvider from "../../../src/credential_provider/OrgsCredentialProvider"; +import TokenAuthStrategy from "../../../src/auth_strategy/TokenAuthStrategy"; + +describe("OrgsCredentialProvider Constructor", () => { + const orgsCredentialProvider = + new OrgsCredentialProvider.OrgsCredentialProviderBuilder() + .setClientId("clientId") + .setClientSecret("clientSecret") + .build(); + + it("Should have client-credentials as its authType", () => { + expect(orgsCredentialProvider.getAuthType()).toEqual( + "client-credentials" + ); + }); + + it("Should return NoAuthStrategy as its auth strategy", () => { + expect(orgsCredentialProvider.toAuthStrategy()).toBeInstanceOf( + TokenAuthStrategy + ); + }); +}); From ffc42aad7a5af0afafa77e133727bbd493c16051 Mon Sep 17 00:00:00 2001 From: Shubham Tiwari Date: Thu, 5 Dec 2024 20:55:05 +0530 Subject: [PATCH 14/15] chore: add token manager tests --- .../OrgsCredentialProvider.spec.ts | 4 +- .../http/bearer_token/ApiTokenManager.spec.ts | 122 ++++++++++++++++++ .../bearer_token/OrgsTokenManager.spec.ts | 122 ++++++++++++++++++ src/http/bearer_token/OrgsTokenManager.ts | 4 +- 4 files changed, 248 insertions(+), 4 deletions(-) create mode 100644 spec/unit/http/bearer_token/ApiTokenManager.spec.ts create mode 100644 spec/unit/http/bearer_token/OrgsTokenManager.spec.ts diff --git a/spec/unit/credential_provider/OrgsCredentialProvider.spec.ts b/spec/unit/credential_provider/OrgsCredentialProvider.spec.ts index 8fbed0d09..4c0ca203e 100644 --- a/spec/unit/credential_provider/OrgsCredentialProvider.spec.ts +++ b/spec/unit/credential_provider/OrgsCredentialProvider.spec.ts @@ -9,9 +9,7 @@ describe("OrgsCredentialProvider Constructor", () => { .build(); it("Should have client-credentials as its authType", () => { - expect(orgsCredentialProvider.getAuthType()).toEqual( - "client-credentials" - ); + expect(orgsCredentialProvider.getAuthType()).toEqual("client-credentials"); }); it("Should return NoAuthStrategy as its auth strategy", () => { diff --git a/spec/unit/http/bearer_token/ApiTokenManager.spec.ts b/spec/unit/http/bearer_token/ApiTokenManager.spec.ts new file mode 100644 index 000000000..f470f2637 --- /dev/null +++ b/spec/unit/http/bearer_token/ApiTokenManager.spec.ts @@ -0,0 +1,122 @@ +import ApiTokenManager from "../../../../src/http/bearer_token/ApiTokenManager"; +import axios from "axios"; +import { jest } from "@jest/globals"; + +function createMockAxios(promiseHandler: Promise) { + const instance = () => promiseHandler; + instance.defaults = { + headers: { + post: {}, + }, + }; + return instance; +} + +describe("ApiTokenManager constructor", function () { + const clientId = "clientId"; + const clientSecret = "clientSecret"; + const grantType = "client_credentials"; + + const apiTokenManager = new ApiTokenManager({ + grantType: grantType, + clientId: clientId, + clientSecret: clientSecret, + }); + + const params = apiTokenManager.getParams(); + + let createSpy: jest.Spied; + const initialHttpProxyValue = process.env.HTTP_PROXY; + + beforeEach(() => { + createSpy = jest.spyOn(axios, "create"); + createSpy.mockReturnValue( + createMockAxios( + Promise.resolve({ + status: 200, + data: { + access_token: "accessTokenValue", + expires_in: 86400, + id_token: null, + refresh_token: null, + token_type: "Bearer", + }, + }) + ) + ); + }); + + afterEach(() => { + createSpy.mockRestore(); + + if (initialHttpProxyValue) { + process.env.HTTP_PROXY = initialHttpProxyValue; + } else { + delete process.env.HTTP_PROXY; + } + }); + + it("Should have client-credentials as its grantType", function () { + expect(params.grantType).toEqual(grantType); + }); + + it("Should have clientId as its clientId", function () { + expect(params.clientId).toEqual(clientId); + }); + + it("Should have clientSecret as its clientSecret", function () { + expect(params.clientSecret).toEqual(clientSecret); + }); + + it("Should return an access token", async function () { + const token = await apiTokenManager.fetchToken(); + expect(token).toEqual("accessTokenValue"); + }); +}); + +describe("ApiTokenManager with error response", function () { + const clientId = "clientId"; + const clientSecret = "clientSecret"; + const grantType = "client_credentials"; + + const apiTokenManager = new ApiTokenManager({ + grantType: grantType, + clientId: clientId, + clientSecret: clientSecret, + }); + + const params = apiTokenManager.getParams(); + + let createSpy: jest.Spied; + const initialHttpProxyValue = process.env.HTTP_PROXY; + + beforeEach(() => { + createSpy = jest.spyOn(axios, "create"); + createSpy.mockReturnValue( + createMockAxios( + Promise.resolve({ + status: 400, + data: { + message: "Token error", + }, + }) + ) + ); + }); + + afterEach(() => { + createSpy.mockRestore(); + + if (initialHttpProxyValue) { + process.env.HTTP_PROXY = initialHttpProxyValue; + } else { + delete process.env.HTTP_PROXY; + } + }); + + it("Should return error message", async function () { + await expect(apiTokenManager.fetchToken()).rejects.toThrow( + `Error Status Code: 400\nFailed to fetch access token: Token error` + ); + }); +}); diff --git a/spec/unit/http/bearer_token/OrgsTokenManager.spec.ts b/spec/unit/http/bearer_token/OrgsTokenManager.spec.ts new file mode 100644 index 000000000..c353d4402 --- /dev/null +++ b/spec/unit/http/bearer_token/OrgsTokenManager.spec.ts @@ -0,0 +1,122 @@ +import OrgsTokenManager from "../../../../src/http/bearer_token/OrgsTokenManager"; +import axios from "axios"; +import { jest } from "@jest/globals"; + +function createMockAxios(promiseHandler: Promise) { + const instance = () => promiseHandler; + instance.defaults = { + headers: { + post: {}, + }, + }; + return instance; +} + +describe("OrgsTokenManager constructor", function () { + const clientId = "clientId"; + const clientSecret = "clientSecret"; + const grantType = "client_credentials"; + + const orgsTokenManager = new OrgsTokenManager({ + grantType: grantType, + clientId: clientId, + clientSecret: clientSecret, + }); + + const params = orgsTokenManager.getParams(); + + let createSpy: jest.Spied; + const initialHttpProxyValue = process.env.HTTP_PROXY; + + beforeEach(() => { + createSpy = jest.spyOn(axios, "create"); + createSpy.mockReturnValue( + createMockAxios( + Promise.resolve({ + status: 200, + data: { + access_token: "accessTokenValue", + expires_in: 86400, + id_token: null, + refresh_token: null, + token_type: "Bearer", + }, + }) + ) + ); + }); + + afterEach(() => { + createSpy.mockRestore(); + + if (initialHttpProxyValue) { + process.env.HTTP_PROXY = initialHttpProxyValue; + } else { + delete process.env.HTTP_PROXY; + } + }); + + it("Should have client-credentials as its grantType", function () { + expect(params.grantType).toEqual(grantType); + }); + + it("Should have clientId as its clientId", function () { + expect(params.clientId).toEqual(clientId); + }); + + it("Should have clientSecret as its clientSecret", function () { + expect(params.clientSecret).toEqual(clientSecret); + }); + + it("Should return an access token", async function () { + const token = await orgsTokenManager.fetchToken(); + expect(token).toEqual("accessTokenValue"); + }); +}); + +describe("OrgsTokenManager with error response", function () { + const clientId = "clientId"; + const clientSecret = "clientSecret"; + const grantType = "client_credentials"; + + const orgsTokenManager = new OrgsTokenManager({ + grantType: grantType, + clientId: clientId, + clientSecret: clientSecret, + }); + + const params = orgsTokenManager.getParams(); + + let createSpy: jest.Spied; + const initialHttpProxyValue = process.env.HTTP_PROXY; + + beforeEach(() => { + createSpy = jest.spyOn(axios, "create"); + createSpy.mockReturnValue( + createMockAxios( + Promise.resolve({ + status: 400, + data: { + message: "Token error", + }, + }) + ) + ); + }); + + afterEach(() => { + createSpy.mockRestore(); + + if (initialHttpProxyValue) { + process.env.HTTP_PROXY = initialHttpProxyValue; + } else { + delete process.env.HTTP_PROXY; + } + }); + + it("Should return error message", async function () { + await expect(orgsTokenManager.fetchToken()).rejects.toThrow( + `Error Status Code: 400\nFailed to fetch access token: Token error` + ); + }); +}); diff --git a/src/http/bearer_token/OrgsTokenManager.ts b/src/http/bearer_token/OrgsTokenManager.ts index b620ad2e8..88da181cf 100644 --- a/src/http/bearer_token/OrgsTokenManager.ts +++ b/src/http/bearer_token/OrgsTokenManager.ts @@ -34,7 +34,9 @@ export default class OrgsTokenManager implements TokenManager { return token.accessToken; }) .catch((error) => { - throw new Error(`Failed to fetch access token: ${error}`); + throw new Error( + `Error Status Code: ${error.status}\nFailed to fetch access token: ${error.message}` + ); }); } } From 1297e263ededfd3ab6221b30f5d639f7027bc87f Mon Sep 17 00:00:00 2001 From: Shubham Tiwari Date: Fri, 6 Dec 2024 12:19:23 +0530 Subject: [PATCH 15/15] chore: shifting tests to other PR --- .../auth_strategy/BasicAuthStrategy.spec.ts | 23 ---- .../unit/auth_strategy/NoAuthStrategy.spec.ts | 20 --- .../auth_strategy/TokenAuthStrategy.spec.ts | 126 ------------------ .../ClientCredentialProvider.spec.ts | 22 --- .../NoAuthCredentialProvider.spec.ts | 17 --- .../OrgsCredentialProvider.spec.ts | 20 --- .../http/bearer_token/ApiTokenManager.spec.ts | 122 ----------------- .../bearer_token/OrgsTokenManager.spec.ts | 122 ----------------- 8 files changed, 472 deletions(-) delete mode 100644 spec/unit/auth_strategy/BasicAuthStrategy.spec.ts delete mode 100644 spec/unit/auth_strategy/NoAuthStrategy.spec.ts delete mode 100644 spec/unit/auth_strategy/TokenAuthStrategy.spec.ts delete mode 100644 spec/unit/credential_provider/ClientCredentialProvider.spec.ts delete mode 100644 spec/unit/credential_provider/NoAuthCredentialProvider.spec.ts delete mode 100644 spec/unit/credential_provider/OrgsCredentialProvider.spec.ts delete mode 100644 spec/unit/http/bearer_token/ApiTokenManager.spec.ts delete mode 100644 spec/unit/http/bearer_token/OrgsTokenManager.spec.ts diff --git a/spec/unit/auth_strategy/BasicAuthStrategy.spec.ts b/spec/unit/auth_strategy/BasicAuthStrategy.spec.ts deleted file mode 100644 index bb66d2f5d..000000000 --- a/spec/unit/auth_strategy/BasicAuthStrategy.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import BasicAuthStrategy from "../../../src/auth_strategy/BasicAuthStrategy"; - -describe("BasicAuthStrategy constructor", function () { - const username = "username"; - const password = "password"; - const basicAuthStrategy = new BasicAuthStrategy(username, password); - - it("Should have basic as its authType", function () { - expect(basicAuthStrategy.getAuthType()).toEqual("basic"); - }); - - it("Should return basic auth string", function (done) { - const auth = Buffer.from(username + ":" + password).toString("base64"); - basicAuthStrategy.getAuthString().then(function (authString) { - expect(authString).toEqual(`Basic ${auth}`); - done(); - }); - }); - - it("Should return true for requiresAuthentication", function () { - expect(basicAuthStrategy.requiresAuthentication()).toBe(true); - }); -}); diff --git a/spec/unit/auth_strategy/NoAuthStrategy.spec.ts b/spec/unit/auth_strategy/NoAuthStrategy.spec.ts deleted file mode 100644 index 08a31da11..000000000 --- a/spec/unit/auth_strategy/NoAuthStrategy.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import NoAuthStrategy from "../../../src/auth_strategy/NoAuthStrategy"; - -describe("NoAuthStrategy constructor", function () { - const noAuthStrategy = new NoAuthStrategy(); - - it("Should have noauth as its authType", function () { - expect(noAuthStrategy.getAuthType()).toEqual("noauth"); - }); - - it("Should return an empty string for getAuthString", function (done) { - noAuthStrategy.getAuthString().then(function (authString) { - expect(authString).toEqual(""); - done(); - }); - }); - - it("Should return false for requiresAuthentication", function () { - expect(noAuthStrategy.requiresAuthentication()).toBe(false); - }); -}); diff --git a/spec/unit/auth_strategy/TokenAuthStrategy.spec.ts b/spec/unit/auth_strategy/TokenAuthStrategy.spec.ts deleted file mode 100644 index 909a1bcf9..000000000 --- a/spec/unit/auth_strategy/TokenAuthStrategy.spec.ts +++ /dev/null @@ -1,126 +0,0 @@ -import TokenAuthStrategy from "../../../src/auth_strategy/TokenAuthStrategy"; -import ApiTokenManager from "../../../src/http/bearer_token/ApiTokenManager"; -import { jest } from "@jest/globals"; -import axios from "axios"; -import twilio from "../../../src"; - -function createMockAxios(promiseHandler: Promise) { - const instance = () => promiseHandler; - instance.defaults = { - headers: { - post: {}, - }, - }; - return instance; -} - -describe("TokenAuthStrategy constructor", function () { - const clientId = "clientId"; - const clientSecret = "clientSecret"; - const grantType = "client_credentials"; - - const tokenManager = new ApiTokenManager({ - grantType: grantType, - clientId: clientId, - clientSecret: clientSecret, - }); - const tokenAuthStrategy = new TokenAuthStrategy(tokenManager); - - let createSpy: jest.Spied; - const initialHttpProxyValue = process.env.HTTP_PROXY; - - beforeEach(() => { - createSpy = jest.spyOn(axios, "create"); - createSpy.mockReturnValue( - createMockAxios( - Promise.resolve({ - status: 200, - data: { - access_token: "accessTokenValue", - token_type: "Bearer", - }, - }) - ) - ); - }); - - afterEach(() => { - createSpy.mockRestore(); - - if (initialHttpProxyValue) { - process.env.HTTP_PROXY = initialHttpProxyValue; - } else { - delete process.env.HTTP_PROXY; - } - }); - - it("Should have token as its authType", function () { - expect(tokenAuthStrategy.getAuthType()).toEqual("token"); - }); - - it("Should check token expiry", function () { - const accountSid = "ACaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; - const keySid = "SKb5aed9ca12bf5890f37930e63cad6d38"; - const token = new twilio.jwt.AccessToken(accountSid, keySid, "secret", { - identity: "ID@example.com", - }); - expect(tokenAuthStrategy.isTokenExpired(token.toJwt())).toBe(false); - }); - - it("Should return token auth string", function (done) { - tokenAuthStrategy.getAuthString().then(function (authString) { - expect(authString).toEqual(`Bearer accessTokenValue`); - done(); - }); - }); - - it("Should return true for requiresAuthentication", function () { - expect(tokenAuthStrategy.requiresAuthentication()).toBe(true); - }); -}); - -describe("TokenAuthStrategy error response", function () { - const clientId = "clientId"; - const clientSecret = "clientSecret"; - const grantType = "client_credentials"; - - const tokenManager = new ApiTokenManager({ - grantType: grantType, - clientId: clientId, - clientSecret: clientSecret, - }); - const tokenAuthStrategy = new TokenAuthStrategy(tokenManager); - - let createSpy: jest.Spied; - const initialHttpProxyValue = process.env.HTTP_PROXY; - - beforeEach(() => { - createSpy = jest.spyOn(axios, "create"); - createSpy.mockReturnValue( - createMockAxios( - Promise.resolve({ - status: 403, - data: { - message: "Invalid Credentials", - }, - }) - ) - ); - }); - - afterEach(() => { - createSpy.mockRestore(); - - if (initialHttpProxyValue) { - process.env.HTTP_PROXY = initialHttpProxyValue; - } else { - delete process.env.HTTP_PROXY; - } - }); - - it("Should return error", async function () { - await expect(tokenAuthStrategy.getAuthString()).rejects.toThrow( - "Failed to fetch access token: Invalid Credentials" - ); - }); -}); diff --git a/spec/unit/credential_provider/ClientCredentialProvider.spec.ts b/spec/unit/credential_provider/ClientCredentialProvider.spec.ts deleted file mode 100644 index ad2a7e154..000000000 --- a/spec/unit/credential_provider/ClientCredentialProvider.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import ClientCredentialProvider from "../../../src/credential_provider/ClientCredentialProvider"; -import TokenAuthStrategy from "../../../src/auth_strategy/TokenAuthStrategy"; - -describe("ClientCredentialProvider Constructor", () => { - const clientCredentialProvider = - new ClientCredentialProvider.ClientCredentialProviderBuilder() - .setClientId("clientId") - .setClientSecret("clientSecret") - .build(); - - it("Should have client-credentials as its authType", () => { - expect(clientCredentialProvider.getAuthType()).toEqual( - "client-credentials" - ); - }); - - it("Should return NoAuthStrategy as its auth strategy", () => { - expect(clientCredentialProvider.toAuthStrategy()).toBeInstanceOf( - TokenAuthStrategy - ); - }); -}); diff --git a/spec/unit/credential_provider/NoAuthCredentialProvider.spec.ts b/spec/unit/credential_provider/NoAuthCredentialProvider.spec.ts deleted file mode 100644 index b86568361..000000000 --- a/spec/unit/credential_provider/NoAuthCredentialProvider.spec.ts +++ /dev/null @@ -1,17 +0,0 @@ -import NoAuthCredentialProvider from "../../../src/credential_provider/NoAuthCredentialProvider"; -import NoAuthStrategy from "../../../src/auth_strategy/NoAuthStrategy"; - -describe("NoAuthCredentialProvider Constructor", () => { - const noAuthCredentialProvider = - new NoAuthCredentialProvider.NoAuthCredentialProvider(); - - it("Should have client-credentials as its authType", () => { - expect(noAuthCredentialProvider.getAuthType()).toEqual("noauth"); - }); - - it("Should return NoAuthStrategy as its auth strategy", () => { - expect(noAuthCredentialProvider.toAuthStrategy()).toBeInstanceOf( - NoAuthStrategy - ); - }); -}); diff --git a/spec/unit/credential_provider/OrgsCredentialProvider.spec.ts b/spec/unit/credential_provider/OrgsCredentialProvider.spec.ts deleted file mode 100644 index 4c0ca203e..000000000 --- a/spec/unit/credential_provider/OrgsCredentialProvider.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import OrgsCredentialProvider from "../../../src/credential_provider/OrgsCredentialProvider"; -import TokenAuthStrategy from "../../../src/auth_strategy/TokenAuthStrategy"; - -describe("OrgsCredentialProvider Constructor", () => { - const orgsCredentialProvider = - new OrgsCredentialProvider.OrgsCredentialProviderBuilder() - .setClientId("clientId") - .setClientSecret("clientSecret") - .build(); - - it("Should have client-credentials as its authType", () => { - expect(orgsCredentialProvider.getAuthType()).toEqual("client-credentials"); - }); - - it("Should return NoAuthStrategy as its auth strategy", () => { - expect(orgsCredentialProvider.toAuthStrategy()).toBeInstanceOf( - TokenAuthStrategy - ); - }); -}); diff --git a/spec/unit/http/bearer_token/ApiTokenManager.spec.ts b/spec/unit/http/bearer_token/ApiTokenManager.spec.ts deleted file mode 100644 index f470f2637..000000000 --- a/spec/unit/http/bearer_token/ApiTokenManager.spec.ts +++ /dev/null @@ -1,122 +0,0 @@ -import ApiTokenManager from "../../../../src/http/bearer_token/ApiTokenManager"; -import axios from "axios"; -import { jest } from "@jest/globals"; - -function createMockAxios(promiseHandler: Promise) { - const instance = () => promiseHandler; - instance.defaults = { - headers: { - post: {}, - }, - }; - return instance; -} - -describe("ApiTokenManager constructor", function () { - const clientId = "clientId"; - const clientSecret = "clientSecret"; - const grantType = "client_credentials"; - - const apiTokenManager = new ApiTokenManager({ - grantType: grantType, - clientId: clientId, - clientSecret: clientSecret, - }); - - const params = apiTokenManager.getParams(); - - let createSpy: jest.Spied; - const initialHttpProxyValue = process.env.HTTP_PROXY; - - beforeEach(() => { - createSpy = jest.spyOn(axios, "create"); - createSpy.mockReturnValue( - createMockAxios( - Promise.resolve({ - status: 200, - data: { - access_token: "accessTokenValue", - expires_in: 86400, - id_token: null, - refresh_token: null, - token_type: "Bearer", - }, - }) - ) - ); - }); - - afterEach(() => { - createSpy.mockRestore(); - - if (initialHttpProxyValue) { - process.env.HTTP_PROXY = initialHttpProxyValue; - } else { - delete process.env.HTTP_PROXY; - } - }); - - it("Should have client-credentials as its grantType", function () { - expect(params.grantType).toEqual(grantType); - }); - - it("Should have clientId as its clientId", function () { - expect(params.clientId).toEqual(clientId); - }); - - it("Should have clientSecret as its clientSecret", function () { - expect(params.clientSecret).toEqual(clientSecret); - }); - - it("Should return an access token", async function () { - const token = await apiTokenManager.fetchToken(); - expect(token).toEqual("accessTokenValue"); - }); -}); - -describe("ApiTokenManager with error response", function () { - const clientId = "clientId"; - const clientSecret = "clientSecret"; - const grantType = "client_credentials"; - - const apiTokenManager = new ApiTokenManager({ - grantType: grantType, - clientId: clientId, - clientSecret: clientSecret, - }); - - const params = apiTokenManager.getParams(); - - let createSpy: jest.Spied; - const initialHttpProxyValue = process.env.HTTP_PROXY; - - beforeEach(() => { - createSpy = jest.spyOn(axios, "create"); - createSpy.mockReturnValue( - createMockAxios( - Promise.resolve({ - status: 400, - data: { - message: "Token error", - }, - }) - ) - ); - }); - - afterEach(() => { - createSpy.mockRestore(); - - if (initialHttpProxyValue) { - process.env.HTTP_PROXY = initialHttpProxyValue; - } else { - delete process.env.HTTP_PROXY; - } - }); - - it("Should return error message", async function () { - await expect(apiTokenManager.fetchToken()).rejects.toThrow( - `Error Status Code: 400\nFailed to fetch access token: Token error` - ); - }); -}); diff --git a/spec/unit/http/bearer_token/OrgsTokenManager.spec.ts b/spec/unit/http/bearer_token/OrgsTokenManager.spec.ts deleted file mode 100644 index c353d4402..000000000 --- a/spec/unit/http/bearer_token/OrgsTokenManager.spec.ts +++ /dev/null @@ -1,122 +0,0 @@ -import OrgsTokenManager from "../../../../src/http/bearer_token/OrgsTokenManager"; -import axios from "axios"; -import { jest } from "@jest/globals"; - -function createMockAxios(promiseHandler: Promise) { - const instance = () => promiseHandler; - instance.defaults = { - headers: { - post: {}, - }, - }; - return instance; -} - -describe("OrgsTokenManager constructor", function () { - const clientId = "clientId"; - const clientSecret = "clientSecret"; - const grantType = "client_credentials"; - - const orgsTokenManager = new OrgsTokenManager({ - grantType: grantType, - clientId: clientId, - clientSecret: clientSecret, - }); - - const params = orgsTokenManager.getParams(); - - let createSpy: jest.Spied; - const initialHttpProxyValue = process.env.HTTP_PROXY; - - beforeEach(() => { - createSpy = jest.spyOn(axios, "create"); - createSpy.mockReturnValue( - createMockAxios( - Promise.resolve({ - status: 200, - data: { - access_token: "accessTokenValue", - expires_in: 86400, - id_token: null, - refresh_token: null, - token_type: "Bearer", - }, - }) - ) - ); - }); - - afterEach(() => { - createSpy.mockRestore(); - - if (initialHttpProxyValue) { - process.env.HTTP_PROXY = initialHttpProxyValue; - } else { - delete process.env.HTTP_PROXY; - } - }); - - it("Should have client-credentials as its grantType", function () { - expect(params.grantType).toEqual(grantType); - }); - - it("Should have clientId as its clientId", function () { - expect(params.clientId).toEqual(clientId); - }); - - it("Should have clientSecret as its clientSecret", function () { - expect(params.clientSecret).toEqual(clientSecret); - }); - - it("Should return an access token", async function () { - const token = await orgsTokenManager.fetchToken(); - expect(token).toEqual("accessTokenValue"); - }); -}); - -describe("OrgsTokenManager with error response", function () { - const clientId = "clientId"; - const clientSecret = "clientSecret"; - const grantType = "client_credentials"; - - const orgsTokenManager = new OrgsTokenManager({ - grantType: grantType, - clientId: clientId, - clientSecret: clientSecret, - }); - - const params = orgsTokenManager.getParams(); - - let createSpy: jest.Spied; - const initialHttpProxyValue = process.env.HTTP_PROXY; - - beforeEach(() => { - createSpy = jest.spyOn(axios, "create"); - createSpy.mockReturnValue( - createMockAxios( - Promise.resolve({ - status: 400, - data: { - message: "Token error", - }, - }) - ) - ); - }); - - afterEach(() => { - createSpy.mockRestore(); - - if (initialHttpProxyValue) { - process.env.HTTP_PROXY = initialHttpProxyValue; - } else { - delete process.env.HTTP_PROXY; - } - }); - - it("Should return error message", async function () { - await expect(orgsTokenManager.fetchToken()).rejects.toThrow( - `Error Status Code: 400\nFailed to fetch access token: Token error` - ); - }); -});