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

feat: parallel upload of test report #920

Merged
merged 10 commits into from
Nov 27, 2024
Merged
4 changes: 2 additions & 2 deletions .github/workflows/run_js_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:

- name: Upload `coverage` folder to R2
if: ${{ always() }}
uses: ryand56/r2-upload-action@latest
uses: himanshu-dixit/r2-upload-action-parallel@v1.3
with:
r2-account-id: ${{ secrets.R2_ACCOUNT_ID }}
r2-access-key-id: ${{ secrets.R2_ACCESS_KEY_ID }}
Expand All @@ -53,7 +53,7 @@ jobs:

- name: Upload jest html-reporters folder to R2
if: ${{ always() }}
uses: ryand56/r2-upload-action@latest
uses: himanshu-dixit/r2-upload-action-parallel@v1.3
with:
r2-account-id: ${{ secrets.R2_ACCOUNT_ID }}
r2-access-key-id: ${{ secrets.R2_ACCESS_KEY_ID }}
Expand Down
9 changes: 8 additions & 1 deletion js/src/env/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,14 @@ export class WorkspaceFactory {
if (this.workspace) {
return;
}
logger.debug(`Creating workspace with env=${this.workspaceConfig.env} and kwargs=${JSON.stringify(this.workspaceConfig.config)}`);

const sanitizedConfig = {
...this.workspaceConfig,
host: this.workspaceConfig.config.composioBaseURL,
composioAPIKey: this.workspaceConfig.config.composioAPIKey ? "REDACTED" : "NOT DEFINED"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chore: Let's use constant values for "REDACTED" and "NOT DEFINED". It would be easy to reuse and change in future, if required

};
logger.debug("Creating workspace with config", sanitizedConfig);

let workspace: Workspace | null = null;
switch (this.workspaceConfig.env) {
case ExecEnv.DOCKER:
Expand Down
1 change: 0 additions & 1 deletion js/src/sdk/base.toolset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,6 @@ export class ComposioToolSet {
}
return true;
}).map((action: any) => {
console.log("Action is", action);
return action.schema;
});

Expand Down
4 changes: 3 additions & 1 deletion js/src/sdk/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { CEG, ERROR } from './utils/error';
import { ActionProxyRequestConfigDTO } from './client';
import apiClient from '../sdk/client/client';
import { GetConnectorInfoResDTO } from './client';
import logger from '../utils/logger';

export class Composio {
/**
Expand Down Expand Up @@ -44,7 +45,8 @@ export class Composio {
// // Parse the base URL and API key, falling back to environment variables or defaults if not provided.
const { baseURL: baseURLParsed, apiKey: apiKeyParsed } = getClientBaseConfig(baseUrl, apiKey);

console.log("Using API Key: ", apiKeyParsed , "and baseURL: ", baseURLParsed);

logger.info(`Using API Key: [REDACTED] and baseURL: ${baseURLParsed}`);
if(!apiKeyParsed){

CEG.throwCustomError(ERROR.COMMON.API_KEY_UNAVAILABLE,{});
Expand Down
5 changes: 3 additions & 2 deletions js/src/sdk/models/Entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ConnectedAccounts } from "./connectedAccounts";
import { BackendClient } from "./backendClient";
import { Triggers } from "./triggers";
import { CEG } from "../utils/error";
import logger from "../../utils/logger";

const LABELS = {
PRIMARY: "primary",
Expand Down Expand Up @@ -243,13 +244,13 @@ export class Entity {
if (!isTestConnectorAvailable && app.no_auth === false) {
if (!authMode) {
// @ts-ignore
console.log(
logger.info(
"Auth schemes not provided, available auth schemes and authConfig"
);
// @ts-ignore
for (const authScheme of app.auth_schemes) {
// @ts-ignore
console.log(
logger.info(
"autheScheme:",
authScheme.name,
"\n",
Expand Down
4 changes: 3 additions & 1 deletion js/src/sdk/models/backendClient.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import apiClient from "../client/client"
import { client as axiosClient, client } from "../client/services.gen"
import { client as axiosClient } from "../client/services.gen"
import { setAxiosClientConfig } from "../utils/config";
import { CEG } from "../utils/error";

/**
Expand Down Expand Up @@ -74,5 +75,6 @@ export class BackendClient {
throwOnError: true
});

setAxiosClientConfig(axiosClient.instance);
}
}
36 changes: 34 additions & 2 deletions js/src/sdk/utils/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,43 @@ import { getEnvVariable } from "../../utils/shared";

import { client as axiosClient } from "../client/services.gen"
import apiClient from "../client/client"
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
import logger from '../../utils/logger';


// Constants
const LOCAL_CACHE_DIRECTORY_NAME = '.composio';
const USER_DATA_FILE_NAME = 'user_data.json';
const DEFAULT_BASE_URL = "https://backend.composio.dev";

export const setAxiosClientConfig = (axiosClientInstance: AxiosInstance) => {

axiosClientInstance.interceptors.request.use(
(request) => {
logger.debug(`API Req [${request.method?.toUpperCase()}] ${request.url}`, {
data: request.data
});
return request;
}
);
axiosClientInstance.interceptors.response.use(
(response) => {
const responseSize = Math.round(JSON.stringify(response.data).length / 1024);
logger.debug(`API Res [${response.status}] ${response.config.method?.toUpperCase()} ${response.config.url} ${responseSize} KB`);
return response;
},
(error) => {
logger.debug(`API Error [${error.response?.status || 'Unknown'}] ${error.config?.method?.toUpperCase()} ${error.config?.url}`, {
headers: error.response?.headers,
data: error.response?.data,
error: error.message
});
return Promise.reject(error);
}
);

}

export const userDataPath = () => path.join(os.homedir(), LOCAL_CACHE_DIRECTORY_NAME, USER_DATA_FILE_NAME);

export const getUserDataJson = () => {
Expand Down Expand Up @@ -46,6 +76,8 @@ export function getAPISDK(baseUrl?: string, apiKey?: string) {
throwOnError: true
});

setAxiosClientConfig(axiosClient.instance);

return apiClient;
}

Expand All @@ -65,8 +97,8 @@ export const writeToFile = (filePath: string, data: any) => {
// Write the data to the file as a formatted JSON string
fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
}catch(error){
console.error("Oops! We couldn't save your settings. Here's why:", (error as Error).message);
console.log("Need help? Check file permissions for file:", filePath);
logger.error("Oops! We couldn't save your settings. Here's why:", (error as Error).message);
logger.info("Need help? Check file permissions for file:", filePath);
}
}

Expand Down
89 changes: 41 additions & 48 deletions js/src/utils/logger.ts
Original file line number Diff line number Diff line change
@@ -1,65 +1,58 @@
import { getEnvVariable } from './shared';
import winston from 'winston';

const levels = {
error: 'ERROR',
warn: 'WARN',
info: 'INFO',
debug: 'DEBUG'
error: 0,
warn: 1,
info: 2,
debug: 3
} as const;

const colors = {
red: (str: string) => `\x1b[31m${str}\x1b[0m`,
yellow: (str: string) => `\x1b[33m${str}\x1b[0m`,
blue: (str: string) => `\x1b[34m${str}\x1b[0m`,
green: (str: string) => `\x1b[32m${str}\x1b[0m`,
gray: (str: string) => `\x1b[90m${str}\x1b[0m`
};

/**
* Colorize log level and timestamp.
* @param {string} level - The log level.
* @param {string} timestamp - The timestamp.
* @returns {Object} - Object containing colored level and timestamp.
*/
const colorize = (level: keyof typeof levels, timestamp: string): { level: string; timestamp: string } => {
switch (level) {
case 'error':
return { level: colors.red(levels[level]), timestamp: colors.gray(timestamp) };
case 'warn':
return { level: colors.yellow(levels[level]), timestamp: colors.gray(timestamp) };
case 'info':
return { level: colors.blue(levels[level]), timestamp: colors.gray(timestamp) };
case 'debug':
return { level: colors.green(levels[level]), timestamp: colors.gray(timestamp) };
default:
return { level, timestamp };
}
error: 'red',
warn: 'yellow',
info: 'blue',
debug: 'green'
};

/**
* Get the current log level from environment variables.
* @returns {string} - The current log level.
* @returns {keyof typeof levels} - The current log level.
*/
const getLogLevel = (): keyof typeof levels => {
const envLevel = getEnvVariable("COMPOSIO_DEBUG", "0");
return envLevel === "1" ? 'debug' : 'info';
const envLevel = getEnvVariable("COMPOSIO_LOGGING_LEVEL", "info");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use constants for this as well

if (!envLevel || !(envLevel in levels)) {
return 'info'; // Default to info instead of warn to match default param
}
return envLevel as keyof typeof levels;
};

/**
* Logger utility to log messages with different levels.
*/
const logger = {
winston.addColors(colors);

const format = winston.format.combine(
winston.format.timestamp(),
winston.format.colorize(),
winston.format.printf(({ timestamp, level, message, ...meta }) => {
let metaString = '';
if (Object.keys(meta).length) {
try {
metaString = ` - ${JSON.stringify(meta)}`;
} catch (err) {
metaString = ` - [Meta object contains circular reference]`;
}
}
const parsedTimestamp = timestamp.slice(0, 19).replace('T', ' ');
return `[${level}] : ${parsedTimestamp} - ${message}${metaString}`;
})
);

const logger = winston.createLogger({
level: getLogLevel(),
log: (level: keyof typeof levels, message: string, meta?: any): void => {
const timestamp = new Date().toLocaleString();
const { level: coloredLevel, timestamp: coloredTimestamp } = colorize(level, timestamp);
const metaInfo = meta ? ` - ${JSON.stringify(meta)}` : '';
console.log(`[${coloredLevel}] ${coloredTimestamp} ${message}${metaInfo}`);
},
error: (message: string, meta?: any): void => logger.log('error', message, meta),
warn: (message: string, meta?: any): void => logger.log('warn', message, meta),
info: (message: string, meta?: any): void => logger.log('info', message, meta),
debug: (message: string, meta?: any): void => logger.log('debug', message, meta)
};
levels,
format,
transports: [
new winston.transports.Console()
]
});

export default logger;
Loading