Skip to content

Commit

Permalink
Merge pull request #13 from lemoncloud-io/feature/louis-update-azure-…
Browse files Browse the repository at this point in the history
…http

Feature/louis update azure http
  • Loading branch information
louis-lemon authored May 31, 2024
2 parents ff807e4 + de8d85a commit 4494afd
Show file tree
Hide file tree
Showing 8 changed files with 198 additions and 17 deletions.
14 changes: 7 additions & 7 deletions src/http/aws-http-request.builder.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
import { AWSHttpRequestData, Body, Headers, Params } from '../types';
import axios, { AxiosHeaders, AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
import { Body, Headers, HttpRequestData, Params } from '../types';
import { AWSStorageService, REGION_KEY, USE_X_LEMON_IDENTITY_KEY } from '../token-storage';
import * as AWS from 'aws-sdk/global.js';
import { sigV4Client } from '../vendor';
Expand Down Expand Up @@ -49,7 +49,7 @@ export class AWSHttpRequestBuilder {
}
this.config = { ...this.config, ...config };
this.axiosInstance = axios.create(this.config);
this.logger = new LoggerService();
this.logger = new LoggerService('AWSHttpBuilder');
}

/**
Expand Down Expand Up @@ -121,7 +121,7 @@ export class AWSHttpRequestBuilder {
async execute<T>(): Promise<AxiosResponse<T>> {
try {
const signedClient = await this.getSignedClient(this.config.baseURL);
const data: AWSHttpRequestData = {
const data: HttpRequestData = {
method: this.config.method?.toLowerCase() || 'get',
params: this.config.params,
body: this.config.data,
Expand Down Expand Up @@ -169,10 +169,10 @@ export class AWSHttpRequestBuilder {
* Gets the signed headers for the request.
* @private
* @param {any} signedClient - The signed AWS client.
* @param {AWSHttpRequestData} data - The request data.
* @param {HttpRequestData} data - The request data.
* @returns {Promise<any>} - The signed headers.
*/
private async getSignedHeader(signedClient: any, data: AWSHttpRequestData): Promise<any> {
private async getSignedHeader(signedClient: any, data: HttpRequestData): Promise<any> {
if (!signedClient) {
return {};
}
Expand Down Expand Up @@ -201,7 +201,7 @@ export class AWSHttpRequestBuilder {
* @param header The header to be added
* @returns The header with x-lemon-identity added
*/
private async addXLemonIdentityToHeader(header: any): Promise<any> {
private async addXLemonIdentityToHeader(header: any): Promise<AxiosHeaders> {
const useXLemonIdentity = await this.tokenStorage.getItem(USE_X_LEMON_IDENTITY_KEY);
if (!useXLemonIdentity || useXLemonIdentity === 'false') {
return header;
Expand Down
174 changes: 174 additions & 0 deletions src/http/azure-http-request.builder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
import { Body, Headers, Params } from '../types';
import { AzureStorageService, USE_X_LEMON_IDENTITY_KEY } from '../token-storage';

/**
* Class to build and execute HTTP requests with AWS signing
* @example
* ```ts
* const response: AxiosResponse<OAuthResponse> = await new AzureHttpRequestBuilder({
* method: 'GET',
* baseURL: `https://api.lemoncloud.io/v1/oauth`,
* })
* .addHeaders({ Cookie: this.cookie })
* .setParams({ page: 0 })
* .execute();
* ```
*/
export class AzureHttpRequestBuilder {
private axiosInstance: AxiosInstance;
private config: AxiosRequestConfig = {
headers: {
'Content-Type': 'application/json',
},
method: 'get',
};

/**
* Creates an instance of AWSHttpRequestBuilder.
* @param {AzureStorageService} tokenStorage - The AWS storage service for token management.
* @param {AxiosRequestConfig} config - The Axios request configuration.
* @throws {Error} If tokenStorage, method, or baseURL are not defined.
*/
constructor(
private readonly tokenStorage: AzureStorageService,
config: AxiosRequestConfig
) {
if (!tokenStorage) {
throw new Error('tokenStorage should be defined!');
}
if (!config.method) {
throw new Error('method should be defined!');
}
if (!config.baseURL) {
throw new Error('baseURL should be defined!');
}
this.config = { ...this.config, ...config };
this.axiosInstance = axios.create(this.config);
}

/**
* Sets the request headers.
* @param {Headers} headers - Headers to set.
* @returns {AWSHttpRequestBuilder} - Returns the current instance to allow method chaining.
*/
setHeaders(headers: Headers): AzureHttpRequestBuilder {
this.config.headers = headers;
return this;
}

/**
* Sets the request parameters.
* @param {Params} params - Parameters to set.
* @returns {AWSHttpRequestBuilder} - Returns the current instance to allow method chaining.
*/
setParams(params: Params): AzureHttpRequestBuilder {
this.config.params = params;
return this;
}

/**
* Sets the request body.
* @param {Body} data - Body data to set.
* @returns {AWSHttpRequestBuilder} - Returns the current instance to allow method chaining.
*/
setBody(data: Body): AzureHttpRequestBuilder {
this.config.data = data;
return this;
}

/**
* Sets the request method.
* @param {string} method - HTTP method to set.
* @returns {AWSHttpRequestBuilder} - Returns the current instance to allow method chaining.
*/
setMethod(method: string): AzureHttpRequestBuilder {
this.config.method = method;
return this;
}

/**
* Adds additional headers to the request.
* @param {Headers} headers - Headers to add.
* @returns {AWSHttpRequestBuilder} - Returns the current instance to allow method chaining.
*/
addHeaders(headers: Headers = {}): AzureHttpRequestBuilder {
this.config.headers = { ...this.config.headers, ...headers };
return this;
}

/**
* Adds additional Axios request configuration.
* @param {AxiosRequestConfig} config - The configuration to add.
* @returns {AWSHttpRequestBuilder} - Returns the current instance to allow method chaining.
*/
addAxiosRequestConfig(config: AxiosRequestConfig): AzureHttpRequestBuilder {
this.config = { ...this.config, ...config };
return this;
}

/**
* Executes the HTTP request.
* @template T
* @returns {Promise<AxiosResponse<T>>} - Promise containing the response.
* @throws {Error} If an error occurs during the request.
*/
async execute<T>(): Promise<AxiosResponse<T>> {
try {
await this.addCodeParams();
await this.addBearerTokenToHeader();
await this.addXLemonIdentityToHeader();
return await this.axiosInstance.request<T>(this.config);
} catch (error) {
throw error;
}
}
/**
* Adds code parameters to the request configuration.
* Retrieves the `hostKey` and `clientId` from the token storage and sets them as request parameters.
* @private
* @async
* @returns {Promise<void>} - A promise that resolves when the parameters are added.
*/
private async addCodeParams(): Promise<void> {
const code = (await this.tokenStorage.getItem('hostKey')) || '';
if (!code) {
return;
}
const clientId = (await this.tokenStorage.getItem('clientId')) || 'default';
const originParams = this.config.params || {};
this.setParams({ ...originParams, code, clientId });
}

/**
* Adds a Bearer token to the request headers.
* Retrieves the `identityToken` from the token storage and sets it as the `Authorization` header.
* @private
* @async
* @returns {Promise<void>} - A promise that resolves when the token is added.
*/
private async addBearerTokenToHeader(): Promise<void> {
const identityToken = (await this.tokenStorage.getItem('identityToken')) || '';
if (!identityToken) {
return;
}
this.addHeaders({ Authorization: `Bearer ${identityToken}` });
}

/**
* Adds the x-lemon-identity token to the request headers if required.
* Checks if the `USE_X_LEMON_IDENTITY_KEY` is set in the token storage and, if true,
* retrieves the `identityToken` and sets it as the `x-lemon-identity` header.
* @private
* @async
* @returns {Promise<void>} - A promise that resolves when the token is added.
*/
private async addXLemonIdentityToHeader(): Promise<void> {
const useXLemonIdentity = await this.tokenStorage.getItem(USE_X_LEMON_IDENTITY_KEY);
if (!useXLemonIdentity || useXLemonIdentity === 'false') {
return;
}
const identityToken = await this.tokenStorage.getItem('identityToken');
this.addHeaders({ 'x-lemon-identity': identityToken });
}
}
1 change: 1 addition & 0 deletions src/http/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './http-request.builder';
export * from './aws-http-request.builder';
export * from './azure-http-request.builder';
5 changes: 3 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './utils';
export * from './types';
export * from './core';
export * from './http';
export * from './token-storage';
export * from './types';
export * from './utils';
export * from './vendor';
2 changes: 1 addition & 1 deletion src/token-storage/aws-storage.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export class AWSStorageService extends TokenStorageService {

constructor(readonly config: WebCoreConfig<'aws'>) {
super(config);
this.initLemonConfig();
this.initLemonConfig().then(() => {});
}

async initLemonConfig() {
Expand Down
9 changes: 5 additions & 4 deletions src/token-storage/azure-storage.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export class AzureStorageService extends TokenStorageService {
/**
* The list of keys used to store credentials in the storage.
*/
private credentialKeys = ['accountId', 'authId', 'identityId', 'identityToken', 'accessToken', 'hostKey', 'expiredTime'];
private credentialKeys = ['accountId', 'authId', 'identityId', 'identityToken', 'accessToken', 'hostKey', 'expiredTime', 'clientId'];

constructor(readonly config: WebCoreConfig<'azure'>) {
super(config);
Expand Down Expand Up @@ -64,8 +64,8 @@ export class AzureStorageService extends TokenStorageService {
return Promise.resolve(tmp);
}, Promise.resolve({}));

const HostKey = await this.storage.getItem(`${this.prefix}.hostKey`);
result.credential = { HostKey };
const hostKey = await this.storage.getItem(`${this.prefix}.hostKey`);
result.credential = { HostKey: hostKey };

delete result.hostKey;
delete result.expiredTime;
Expand All @@ -80,7 +80,7 @@ export class AzureStorageService extends TokenStorageService {
*/
async saveOAuthToken(token: LemonOAuthToken): Promise<void> {
const { accountId, authId, credential, identityId, identityToken, accessToken } = token;
const { hostKey } = credential;
const { hostKey, clientId } = credential;

this.storage.setItem(`${this.prefix}.accountId`, accountId || '');
this.storage.setItem(`${this.prefix}.authId`, authId || '');
Expand All @@ -89,6 +89,7 @@ export class AzureStorageService extends TokenStorageService {

this.storage.setItem(`${this.prefix}.hostKey`, hostKey || '');
this.storage.setItem(`${this.prefix}.accessToken`, accessToken || '');
this.storage.setItem(`${this.prefix}.clientId`, clientId || 'default');

// Set the expiration time for the token.
const TIME_DELAY = 0.5; // 0.5 = 30 minutes, 1 = 1 hour
Expand Down
4 changes: 2 additions & 2 deletions src/types/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ export declare type Params = {
};

/**
* Defines the structure of an AWS HTTP request.
* Defines the structure of an HTTP request.
*/
export interface AWSHttpRequestData {
export interface HttpRequestData {
/**
* The HTTP method (e.g., GET, POST, PUT, etc.).
*/
Expand Down
6 changes: 5 additions & 1 deletion src/types/lemon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,13 @@ export interface LemonCredentials {
*/
SessionToken?: string;
/**
* The host key associated with the credentials (optional).
* The host key associated with the credentials for Azure (optional).
*/
hostKey?: string;
/**
* The client id for Azure (optional).
*/
clientId?: string;
}

/**
Expand Down

0 comments on commit 4494afd

Please sign in to comment.