Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace clients host and scheme properties to cater to S3-Compatible backends #61

Merged
merged 8 commits into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build/aws.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/aws.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/index.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/kinesis.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/kinesis.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/kms.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/kms.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/s3.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/s3.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/secrets-manager.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/secrets-manager.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/signature.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/signature.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/sqs.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/sqs.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/ssm.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/ssm.js.map

Large diffs are not rendered by default.

60 changes: 34 additions & 26 deletions src/internal/client.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { AWSConfig } from './config'
import { Endpoint } from './endpoint'
import { HTTPHeaders } from './http'
import { HTTPScheme } from './http'

/**
* Class allowing to build requests targeting AWS APIs
*
Expand All @@ -12,8 +13,8 @@ export class AWSClient {
awsConfig: AWSConfig
serviceName: string

private _host?: string
private _scheme?: HTTPScheme
private _endpoint?: Endpoint

/**
* @param {AWSConfig} awsConfig - configuration attributes to use when interacting with AWS' APIs
* @param {string} serviceName - name of the service to target.
Expand All @@ -22,41 +23,48 @@ export class AWSClient {
constructor(awsConfig: AWSConfig, serviceName: string) {
this.awsConfig = awsConfig
this.serviceName = serviceName
}

/**
* Property computing the URL to send the requests to when interacting with
* the specific AWS service the child class implements the functionalities of.
*/
public get host() {
if (this._host == undefined) {
this._host = `${this.serviceName}.${this.awsConfig.region}.${this.awsConfig.endpoint}`
// If an endpoint is provided in the config, set it
// to ensure the default endpoint is not used.
if (awsConfig.endpoint != undefined) {
this._endpoint = awsConfig.endpoint
}
return this._host
}

public set host(host: string) {
this._host = host
}

/**
* Property computing the scheme to use http or https. Defaults to https as per AWSConfig Defaults
* the specific AWS service the child class implements the functionalities of.
/**
* Represents the endpoint URL of the AWS service.
*
* If no custom endpoint is set, a default endpoint will be constructed
* using the service name and region provided in the AWS config.
*
* @type {Endpoint}
* @public
*/

public get scheme() {

if (this._scheme == undefined) {
this._scheme = this.awsConfig.scheme;
}
return this._scheme
public get endpoint() {
if (this._endpoint == undefined) {
this._endpoint = new Endpoint(
`https://${this.serviceName}.${this.awsConfig.region}.amazonaws.com`
)
}
return this._endpoint
}

// Validatiuon should be done by the type declaration
public set scheme(scheme: HTTPScheme) {
this._scheme = scheme
}

/**
* Updates the endpoint URL of the AWS service.
*
* This can be used to override the default AWS service endpoint or set a custom endpoint.
*
* @param {Endpoint} endpoint - The new endpoint to set for the AWS service.
* @public
*/
public set endpoint(endpoint: Endpoint) {
this._endpoint = endpoint
}
}

/**
Expand Down
36 changes: 13 additions & 23 deletions src/internal/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { HTTPScheme } from './http'
import { Endpoint } from './endpoint'

/** Class holding an AWS connection information */
export class AWSConfig {
Expand Down Expand Up @@ -30,21 +31,12 @@ export class AWSConfig {
*/
sessionToken?: string

/**
* The HTTP scheme to use when connecting to AWS.
*
* @type {HTTPScheme} ['https']
*/
scheme: HTTPScheme = 'https'

// FIXME: Should really be called "host" instead. When used
// with localstack we pass a complete host (hostname:port) here.
/**
* The AWS hostname to connect to.
*
* @type {string} ['amazonaws.com']
*/
endpoint: string = 'amazonaws.com'
endpoint?: Endpoint

/**
* fromEnvironment creates an AWSConfig from the environment variables.
Expand All @@ -64,19 +56,17 @@ export class AWSConfig {
* @returns
*/
static fromEnvironment(options?: AWSConnectionOptions): AWSConfig {
const region = __ENV.AWS_REGION;
const accessKeyId = __ENV.AWS_ACCESS_KEY_ID;
const secretAccessKey = __ENV.AWS_SECRET_ACCESS_KEY;
const sessionToken: string | undefined = __ENV.AWS_SESSION_TOKEN;
const scheme: HTTPScheme | undefined = options?.scheme;
const endpoint: string | undefined = options?.endpoint;
const region = __ENV.AWS_REGION
const accessKeyId = __ENV.AWS_ACCESS_KEY_ID
const secretAccessKey = __ENV.AWS_SECRET_ACCESS_KEY
const sessionToken: string | undefined = __ENV.AWS_SESSION_TOKEN
const endpoint: Endpoint | string | undefined = options?.endpoint

return new AWSConfig({
region,
accessKeyId,
secretAccessKey,
sessionToken,
scheme: scheme,
endpoint: endpoint,
})
}
Expand Down Expand Up @@ -126,12 +116,12 @@ export class AWSConfig {
this.sessionToken = options.sessionToken
}

if (options.scheme !== undefined) {
this.scheme = options.scheme
}

if (options.endpoint !== undefined) {
this.endpoint = options.endpoint
if (typeof options.endpoint === 'string') {
this.endpoint = new Endpoint(options.endpoint)
} else {
this.endpoint = options.endpoint
}
}
}
}
Expand Down Expand Up @@ -185,7 +175,7 @@ export interface AWSConnectionOptions {
*
* @type {string}
*/
endpoint?: string
endpoint?: Endpoint | string
}

/** Class representing an invalid AWS configuration */
Expand Down
143 changes: 143 additions & 0 deletions src/internal/endpoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/**
* Represents an AWS service endpoint, providing utilities for parsing and handling URL details.
*/
export class Endpoint {
private _protocol: string
private _hostname: string
private _port?: number

// Default protocol, this can be globally changed as per application requirements
private static readonly DEFAULT_PROTOCOL = 'https' // Set this as per AWS.config

/**
// * Constructs a new Endpoint instance.
*
* @param {string} endpoint - The URL to construct an endpoint from. If the URL omits a protocol, the default protocol will be used.
*/
constructor(endpoint: string) {
const isDefaultProtocol =
oleiade marked this conversation as resolved.
Show resolved Hide resolved
!endpoint.startsWith('http://') && !endpoint.startsWith('https://')
const completeUrl = isDefaultProtocol
? `${Endpoint.DEFAULT_PROTOCOL}://${endpoint}`
: endpoint

const protocolMatch = completeUrl.match(/^https?:/)
const hostAndPath = completeUrl.replace(/^https?:\/\//, '')
const [hostnameWithPort] = hostAndPath.split('/')

this._protocol = protocolMatch ? protocolMatch[0].slice(0, -1) : Endpoint.DEFAULT_PROTOCOL
this._hostname = hostnameWithPort.split(':')[0]
this._port = hostnameWithPort.split(':')[1]
? parseInt(hostnameWithPort.split(':')[1])
: undefined
}

/**
* Creates a new Endpoint instance that is a copy of the current one.
*
* @returns {Endpoint} The copied Endpoint.
*/
public copy(): Endpoint {
return new Endpoint(this.href)
}

/**
* Gets the host portion of the endpoint including the port.
*
* @returns {string} The host portion of the endpoint including the port.
*/
public get host(): string {
return this._port ? `${this._hostname}:${this._port}` : this._hostname
}

/**
* Sets the host portion of the endpoint including the port.
*
* @param {string} value - The value to set for the host.
*/
public set host(value: string) {
const [hostname, port] = value.split(':')
this._hostname = hostname
this._port = port ? parseInt(port) : undefined
}

/**
* Gets the host portion of the endpoint without the port.
*
* @returns {string} The host portion of the endpoint.
*/
public get hostname(): string {
return this._hostname
}

/**
* Sets the host portion of the endpoint without the port.
*
* @param {string} value - The value to set for the hostname.
*/
public set hostname(value: string) {
this._hostname = value
}

/**
* Gets the full URL of the endpoint.
*
* @returns {string} The full URL of the endpoint.
*/
public get href(): string {
return `${this.protocol}://${this.host}`
}

/**
* Sets the full URL of the endpoint.
*
* @param {string} value - The value to set for the full URL.
*/
public set href(value: string) {
const protocolMatch = value.match(/^https?:/)
const withoutProtocol = value.replace(/^https?:\/\//, '')
const [hostnameWithPort] = withoutProtocol.split('/')

this._protocol = protocolMatch ? protocolMatch[0].slice(0, -1) : Endpoint.DEFAULT_PROTOCOL // remove the trailing colon
this._hostname = hostnameWithPort.split(':')[0]
this._port = hostnameWithPort.split(':')[1]
? parseInt(hostnameWithPort.split(':')[1])
: undefined
}

/**
* Gets the port of the endpoint.
*
* @returns {number|undefined} The port of the endpoint.
*/
public get port(): number | undefined {
return this._port
}

/**
* Sets the port of the endpoint.
*
* @param {number|undefined} value - The value to set for the port.
*/
public set port(value: number | undefined) {
this._port = value
}

/**
* Gets the protocol (http or https) of the endpoint URL.
*
* @returns {string} The protocol of the endpoint URL.
*/
public get protocol(): string {
return this._protocol
}

/**
* Sets the protocol (http or https) of the endpoint URL.
*
* @param {string} value - The value to set for the protocol.
*/
public set protocol(value: string) {
this._protocol = value
}
}
16 changes: 4 additions & 12 deletions src/internal/http.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Endpoint } from './endpoint'

/**
* Type representing HTTP schemes
*/
Expand Down Expand Up @@ -46,19 +48,9 @@ export interface HTTPRequest {
method: HTTPMethod

/**
* The protocol to use (http or https)
*/
protocol: HTTPScheme

/**
* The hostname (domain name or IP address) the request targets
*/
hostname: string

/**
* The port to the request targets
* Represents an AWS service endpoint, providing utilities for parsing and handling URL details.
*/
port?: number
endpoint: Endpoint

/**
* The path to the resource
Expand Down
3 changes: 1 addition & 2 deletions src/internal/kinesis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,7 @@ export class KinesisClient extends AWSClient {
const signedRequest = this.signature.sign(
{
method: 'POST',
protocol: this.awsConfig.scheme,
hostname: this.host,
endpoint: this.endpoint,
path: '/',
headers: {
...this.commonHeaders,
Expand Down
6 changes: 2 additions & 4 deletions src/internal/kms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,7 @@ export class KMSClient extends AWSClient {
const signedRequest = this.signature.sign(
{
method: this.method,
protocol: this.awsConfig.scheme,
hostname: this.host,
endpoint: this.endpoint,
path: '/',
headers: {
...this.commonHeaders,
Expand Down Expand Up @@ -100,8 +99,7 @@ export class KMSClient extends AWSClient {
const signedRequest = this.signature.sign(
{
method: this.method,
protocol: this.awsConfig.scheme,
hostname: this.host,
endpoint: this.endpoint,
path: '/',
headers: {
...this.commonHeaders,
Expand Down
Loading