atlas-app-services-admin-api
Version:
OpenAPI client for Atlas App Services Admin API
454 lines (402 loc) • 15.4 kB
text/typescript
// Copyright (c) 2023 Gregory Fay
// This software is licensed under the MIT License.
import * as API from '.';
import { AdminLoginRequest, Configuration, ConfigurationParameters } from '.';
/**
* Logger interface for custom logging.
*/
export interface Logger {
log: (message: string, ...optionalParams: any[]) => void;
error: (message: string, ...optionalParams: any[]) => void;
debug: (message: string, ...optionalParams: any[]) => void;
// ... other log levels as needed
}
/**
* No operation logger for default logging.
*/
class NoOpLogger implements Logger {
log(message: string, ...optionalParams: any[]): void { }
error(message: string, ...optionalParams: any[]): void { }
debug(message: string, ...optionalParams: any[]): void { }
}
/**
* Custom error for App ID retrieval failures.
*/
class AppIdRetrievalError extends Error {
constructor(message: string) {
super(message);
this.name = 'AppIdRetrievalError';
}
}
/**
* Custom error for unauthorized access.
*/
class UnauthorizedException extends Error {
constructor(message: string) {
super(message);
this.name = 'UnauthorizedException';
}
}
/**
* AtlasAppServicesClient class for interacting with Atlas App Services.
*/
export class AtlasAppServicesClient {
private adminApi: API.AdminApi;
private appsApi: API.AppsApi;
public groupId: string;
public appId: string;
public clientAppId: string;
private userId: string;
private configParams: ConfigurationParameters;
private accessToken: string;
private refreshToken: string;
private tokenExpiration: Date;
private logger: Logger;
private apis: { [key: string]: any } = {}; // Cache of instantiated APIs
/**
* Constructor for AtlasAppServicesClient.
* @param config - Configuration object containing publicKey, privateKey, baseUrl, and groupId.
* @param logger - Optional logger for custom logging.
*/
constructor(
config: {
publicKey: string,
privateKey: string,
baseUrl: string,
groupId: string,
},
logger?: Logger,
) {
this.logger = logger || new NoOpLogger();
this.configParams = {
username: config.publicKey,
apiKey: config.privateKey,
basePath: config.baseUrl,
baseOptions: {
headers: {
'Content-Type': 'application/json',
},
},
};
this.groupId = config.groupId;
}
/**
* Initialize the client by logging in and setting up the app.
*/
async initialize() {
await this.loginAndAppSetup();
}
private async loginAndAppSetup() {
try {
const loginResponse = await this.doAdminLogin();
this.appsApi = new API.AppsApi(new Configuration(this.configParams));
const appsResponse = await this.appsApi.adminListApplications(this.groupId, "atlas");
if (!appsResponse.data || !appsResponse.data[0] || !appsResponse.data[0]._id) {
this.logger.error(`Failed to get appID App Services Admin API: ${JSON.stringify(appsResponse.data, null, 2)}`);
throw new AppIdRetrievalError('Failed to retrieve appID from App Services Admin API.');
}
this.appId = appsResponse.data[0]._id;
this.clientAppId = appsResponse.data[0].client_app_id;
this.groupId = appsResponse.data[0].group_id;
} catch (error) {
throw error;
}
}
private async doAdminLogin() {
const adminLoginRequest: AdminLoginRequest = {
username: this.configParams.username,
apiKey: this.configParams.apiKey
};
this.adminApi = new API.AdminApi(new Configuration(this.configParams));
const loginResponse = await this.adminApi.adminLogin("mongodb-cloud", adminLoginRequest);
if (!loginResponse.data || !loginResponse.data.access_token || !loginResponse.data.refresh_token) {
throw new UnauthorizedException('Failed to Login to Atlas App Services Admin API');
}
this.setAccessToken(loginResponse.data.access_token);
this.refreshToken = loginResponse.data.refresh_token;
this.userId = loginResponse.data.user_id;
this.logger.log('Successfully completed login to Atlas App Services Admin API');
return loginResponse;
}
private setAccessToken(token: string) {
this.tokenExpiration = new Date(Date.now() + 30 * 60 * 1000); // 30 minutes from now
this.accessToken = token;
this.configParams.accessToken = token;
}
private isAccessTokenValid(): boolean {
if (!this.tokenExpiration) {
return false;
}
return Date.now() < this.tokenExpiration.getTime() - 30 * 1000; // 30 seconds before expiration
}
private async ensureValidAccessToken(): Promise<void> {
if (this.isAccessTokenValid()) {
return;
}
this.configParams.accessToken = this.refreshToken;
this.adminApi = new API.AdminApi(new Configuration(this.configParams));
try {
const refreshResponse = await this.adminApi.adminCreateSession();
if (!refreshResponse || !refreshResponse.data || !refreshResponse.data.access_token) {
this.logger.error(`Failed to refresh access token: ${JSON.stringify(refreshResponse.data, null, 2)}`);
await this.loginAndAppSetup(); // Re-login and re-initialize
} else {
this.logger.log('Successfully refreshed access token');
this.setAccessToken(refreshResponse.data.access_token);
}
} catch (error) {
this.logger.error(`Failed to refresh access token (error): ${JSON.stringify(error.response.data, null, 2)}`);
await this.loginAndAppSetup(); // Re-login and re-initialize
}
}
private async instantiateApi(apiName: string): Promise<any> {
await this.ensureValidAccessToken();
const ApiClass = (API as { [key: string]: any })[apiName];
if (typeof ApiClass !== 'function') {
throw new Error(`API ${apiName} does not exist`);
}
const apiInstance = new ApiClass(new API.Configuration(this.configParams));
return new Proxy(apiInstance, this.createApiProxyHandler());
}
private createApiProxyHandler(): ProxyHandler<any> {
return {
get: (target, propKey, receiver) => {
const origMethod = target[propKey];
if (typeof origMethod === 'function') {
return (...args: any[]) => {
this.logger.log(`Calling method: '${String(propKey)}' with arguments: ${this.stringifyArgs(args)}`);
return origMethod.apply(target, args);
};
} else {
return origMethod;
}
}
};
}
private stringifyArgs(args: any[], depth: number = 2): string {
const cache: any[] = [];
return JSON.stringify(args, (key, value) => {
if (typeof value === 'object' && value !== null) {
if (cache.indexOf(value) !== -1) {
// Circular reference found, discard key
return;
}
// Store value in our collection
cache.push(value);
}
return value;
}, depth);
}
/*** Getters for the APIs */
/*** Getters for the APIs */
/**
* Get an instance of the Admin API.
* @returns {Promise<API.AdminApi>} An instance of the Admin API.
*/
async atlasAdminApi(): Promise<API.AdminApi> {
return await this.instantiateApi('AdminApi');
}
/**
* Get an instance of the API Keys API.
* @returns {Promise<API.ApikeysApi>} An instance of the API Keys API.
*/
async atlasApikeysApi(): Promise<API.ApikeysApi> {
return await this.instantiateApi('ApikeysApi');
}
/**
* Get an instance of the Apps API.
* @returns {Promise<API.AppsApi>} An instance of the Apps API.
*/
async atlasAppsApi(): Promise<API.AppsApi> {
return await this.instantiateApi('AppsApi');
}
/**
* Get an instance of the Auth Providers API.
* @returns {Promise<API.AuthprovidersApi>} An instance of the Auth Providers API.
*/
async atlasAuthprovidersApi(): Promise<API.AuthprovidersApi> {
return await this.instantiateApi('AuthprovidersApi');
}
/**
* Get an instance of the Billing API.
* @returns {Promise<API.BillingApi>} An instance of the Billing API.
*/
async atlasBillingApi(): Promise<API.BillingApi> {
return await this.instantiateApi('BillingApi');
}
/**
* Get an instance of the Custom User Data API.
* @returns {Promise<API.CustomUserDataApi>} An instance of the Custom User Data API.
*/
async atlasCustomUserDataApi(): Promise<API.CustomUserDataApi> {
return await this.instantiateApi('CustomUserDataApi');
}
/**
* Get an instance of the Data API.
* @returns {Promise<API.DataApiApi>} An instance of the Data API.
*/
async atlasDataApiApi(): Promise<API.DataApiApi> {
return await this.instantiateApi('DataApiApi');
}
/**
* Get an instance of the Dependencies API.
* @returns {Promise<API.DependenciesApi>} An instance of the Dependencies API.
*/
async atlasDependenciesApi(): Promise<API.DependenciesApi> {
return await this.instantiateApi('DependenciesApi');
}
/**
* Get an instance of the Deploy API.
* @returns {Promise<API.DeployApi>} An instance of the Deploy API.
*/
async atlasDeployApi(): Promise<API.DeployApi> {
return await this.instantiateApi('DeployApi');
}
/**
* Get an instance of the Email API.
* @returns {Promise<API.EmailApi>} An instance of the Email API.
*/
async atlasEmailApi(): Promise<API.EmailApi> {
return await this.instantiateApi('EmailApi');
}
/**
* Get an instance of the Endpoints API.
* @returns {Promise<API.EndpointsApi>} An instance of the Endpoints API.
*/
async atlasEndpointsApi(): Promise<API.EndpointsApi> {
return await this.instantiateApi('EndpointsApi');
}
/**
* Get an instance of the Environments API.
* @returns {Promise<API.EnvironmentsApi>} An instance of the Environments API.
*/
async atlasEnvironmentsApi(): Promise<API.EnvironmentsApi> {
return await this.instantiateApi('EnvironmentsApi');
}
/**
* Get an instance of the Event Subscriptions API.
* @returns {Promise<API.EventSubscriptionsApi>} An instance of the Event Subscriptions API.
*/
async atlasEventSubscriptionsApi(): Promise<API.EventSubscriptionsApi> {
return await this.instantiateApi('EventSubscriptionsApi');
}
/**
* Get an instance of the Functions API.
* @returns {Promise<API.FunctionsApi>} An instance of the Functions API.
*/
async atlasFunctionsApi(): Promise<API.FunctionsApi> {
return await this.instantiateApi('FunctionsApi');
}
/**
* Get an instance of the GraphQL API.
* @returns {Promise<API.GraphqlApi>} An instance of the GraphQL API.
*/
async atlasGraphqlApi(): Promise<API.GraphqlApi> {
return await this.instantiateApi('GraphqlApi');
}
/**
* Get an instance of the Hosting API.
* @returns {Promise<API.HostingApi>} An instance of the Hosting API.
*/
async atlasHostingApi(): Promise<API.HostingApi> {
return await this.instantiateApi('HostingApi');
}
/**
* Get an instance of the Log Forwarders API.
* @returns {Promise<API.LogForwardersApi>} An instance of the Log Forwarders API.
*/
async atlasLogForwardersApi(): Promise<API.LogForwardersApi> {
return await this.instantiateApi('LogForwardersApi');
}
/**
* Get an instance of the Logs API.
* @returns {Promise<API.LogsApi>} An instance of the Logs API.
*/
async atlasLogsApi(): Promise<API.LogsApi> {
return await this.instantiateApi('LogsApi');
}
/**
* Get an instance of the Metrics API.
* @returns {Promise<API.MetricsApi>} An instance of the Metrics API.
*/
async atlasMetricsApi(): Promise<API.MetricsApi> {
return await this.instantiateApi('MetricsApi');
}
/**
* Get an instance of the Notifications API.
* @returns {Promise<API.NotificationsApi>} An instance of the Notifications API.
*/
async atlasNotificationsApi(): Promise<API.NotificationsApi> {
return await this.instantiateApi('NotificationsApi');
}
/**
* Get an instance of the Rules API.
* @returns {Promise<API.RulesApi>} An instance of the Rules API.
*/
async atlasRulesApi(): Promise<API.RulesApi> {
return await this.instantiateApi('RulesApi');
}
/**
* Get an instance of the Schemas API.
* @returns {Promise<API.SchemasApi>} An instance of the Schemas API.
*/
async atlasSchemasApi(): Promise<API.SchemasApi> {
return await this.instantiateApi('SchemasApi');
}
/**
* Get an instance of the Secrets API.
* @returns {Promise<API.SecretsApi>} An instance of the Secrets API.
*/
async atlasSecretsApi(): Promise<API.SecretsApi> {
return await this.instantiateApi('SecretsApi');
}
/**
* Get an instance of the Security API.
* @returns {Promise<API.SecurityApi>} An instance of the Security API.
*/
async atlasSecurityApi(): Promise<API.SecurityApi> {
return await this.instantiateApi('SecurityApi');
}
/**
* Get an instance of the Services API.
* @returns {Promise<API.ServicesApi>} An instance of the Services API.
*/
async atlasServicesApi(): Promise<API.ServicesApi> {
return await this.instantiateApi('ServicesApi');
}
/**
* Get an instance of the Sync API.
* @returns {Promise<API.SyncApi>} An instance of the Sync API.
*/
async atlasSyncApi(): Promise<API.SyncApi> {
return await this.instantiateApi('SyncApi');
}
/**
* Get an instance of the Triggers API.
* @returns {Promise<API.TriggersApi>} An instance of the Triggers API.
*/
async atlasTriggersApi(): Promise<API.TriggersApi> {
return await this.instantiateApi('TriggersApi');
}
/**
* Get an instance of the Users API.
* @returns {Promise<API.UsersApi>} An instance of the Users API.
*/
async atlasUsersApi(): Promise<API.UsersApi> {
return await this.instantiateApi('UsersApi');
}
/**
* Get an instance of the Values API.
* @returns {Promise<API.ValuesApi>} An instance of the Values API.
*/
async atlasValuesApi(): Promise<API.ValuesApi> {
return await this.instantiateApi('ValuesApi');
}
/**
* Get an instance of the Webhooks API.
* @returns {Promise<API.WebhooksApi>} An instance of the Webhooks API.
*/
async atlasWebhooksApi(): Promise<API.WebhooksApi> {
return await this.instantiateApi('WebhooksApi');
}
}