Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { flags, isAuthenticated, FlagInput, managementSDKClient, cliux } from '@contentstack/cli-utilities';
import { RateLimitHandler } from '../../../utils/rate-limit-handler';
import { BaseCommand } from '../../../base-command';
import { askOrgID } from '../../../utils/interactive';
import { SetRateLimitConfig } from '../../../interfaces';
import { limitNamesConfig } from '../../../utils/common-utilities';

export default class RateLimitSetCommand extends BaseCommand<typeof RateLimitSetCommand> {
static description = 'Set rate-limit for CLI';

static flags: FlagInput = {
org: flags.string({
description: 'Provide the organization UID',
}),

utilize: flags.string({
description: 'Provide the utilization percentages for rate limit, separated by commas',
default: '50',
}),

'limit-name': flags.string({
description: "[Optional] Provide the limit names separated by commas ['limit', 'getLimit', 'bulkLimit']",
multiple: true,
}),

default: flags.boolean({
default: false,
description: 'Reset to default rate limit',
}),
};

static examples = [
'$ csdx config:set:rate-limit --org <<org_uid>>',
'$ csdx config:set:rate-limit --org <<org_uid>> --utilize 70,80 --limit-name getLimit,limit',
'$ csdx config:set:rate-limit --org <<org_uid>> --default',
];

public async run(): Promise<void> {
if (!isAuthenticated()) {
const err = { errorMessage: 'You are not logged in. Please login with command $ csdx auth:login' };
cliux.print(err.errorMessage, { color: 'red' });
this.exit(1);
}

const { flags } = await this.parse(RateLimitSetCommand);
let { org, utilize, 'limit-name': limitName } = flags;
const config: SetRateLimitConfig = { org: '', limitName: limitNamesConfig };

if (!org) {
org = await askOrgID();
}
config.org = org;
config.default = flags.default;
if (utilize) {
const utilizeValues = utilize?.split(',')?.map((u: string) => Number(u.trim()));
if (utilizeValues.some((u: number) => isNaN(u) || u < 0 || u > 100)) {
cliux.error('Utilize percentages must be numbers between 0 and 100.');
return;
}
if (limitName?.length > 0 && limitName[0]?.split(',')?.length !== utilizeValues.length) {
cliux.error('The number of utilization percentages must match the number of limit names provided.');
return;
} else {
config.utilize = utilize.split(',').map((v: string) => v.trim());
}
}

if (limitName) {
const invalidLimitNames = limitName[0].split(',').map((name: string) => name.trim());

if (!limitNamesConfig.includes(invalidLimitNames)) {
cliux.error(`Invalid limit names provided: ${invalidLimitNames.join(', ')}`);
return;
} else {
config['limit-name'] = limitName[0].split(',').map((n) => n.trim());
}
}

const limitHandler = new RateLimitHandler();
const managementAPIClient = await managementSDKClient(config);
limitHandler.setClient(managementAPIClient);
cliux.success(`Rate limit has been set successfully for org: ${config.org}`);
try {
await limitHandler.setRateLimit(config);
} catch (error) {
cliux.error(`Error: Something went wrong while setting rate limit for org: ${org}`, error);
}
}
}
10 changes: 8 additions & 2 deletions packages/contentstack-config/src/interfaces/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export interface Region {
launchHubUrl: string;
}


export interface RateLimitConfig {
getLimit?: {
value: number;
Expand All @@ -34,4 +33,11 @@ export interface RateLimitConfig {
value: number;
utilize: number;
};
}
}

export interface SetRateLimitConfig {
org: string;
utilize?: number;
limitName?: string[];
default?: boolean;
}
7 changes: 7 additions & 0 deletions packages/contentstack-config/src/utils/common-utilities.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const limitNamesConfig = ['getLimit', 'limit', 'bulkLimit'];

export const defaultRalteLimitConfig = {
getLimit: { value: 10, utilize: 50 },
limit: { value: 10, utilize: 50 },
bulkLimit: { value: 1, utilize: 50 },
};
52 changes: 52 additions & 0 deletions packages/contentstack-config/src/utils/rate-limit-handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { cliux, configHandler } from '@contentstack/cli-utilities';
import { limitNamesConfig, defaultRalteLimitConfig } from '../utils/common-utilities';

let client: any;

export class RateLimitHandler {
setClient(managementSDKClient) {
client = managementSDKClient;
}

async setRateLimit(config) {
const rateLimit = configHandler.get('rateLimit');
rateLimit.default = { ...defaultRalteLimitConfig };

if (config.default) {
rateLimit[config.org] = { ...defaultRalteLimitConfig };
configHandler.set('rateLimit', rateLimit);
cliux.success(`Rate limit reset to default for org: ${config.org}`);
return;
}

if (!rateLimit[config.org]) {
rateLimit[config.org] = { ...rateLimit.default };
}
const limitNames = Array.isArray(config['limit-name']) ? config['limit-name'] : [];
const utilizeValues = Array.isArray(config.utilize) ? config.utilize.map((v) => Number(v)) : [];

try {
const organizations = await client.organization(config.org).fetch({ include_plan: true });
const features = organizations.plan?.features || [];

const limitsToUpdate = { ...rateLimit[config.org] };

limitNames.forEach((limitName, index) => {
if (limitNamesConfig.includes(limitName)) {
const feature = features.find((f: { uid: string }) => f.uid === limitName);
if (feature) {
limitsToUpdate[limitName] = {
value: rateLimit[config.org][limitName]?.value || rateLimit.default[limitName]?.value,
utilize: utilizeValues[index] || Number(config.utilize[0]),
};
}
}
});

rateLimit[config.org] = limitsToUpdate;
configHandler.set('rateLimit', rateLimit);
} catch (error) {
cliux.error(`Error: Unable to set the rate limit`, error?.errorMessage || error?.message || error);
}
}
}