Skip to content

Commit

Permalink
refactor(chore): fix sonar code smells (#150)
Browse files Browse the repository at this point in the history
fix sonar code smells related to magic numbers and cognitive complexity

GH-148
  • Loading branch information
sf-sahil-jassal committed Jan 8, 2024
1 parent f369cb4 commit 85e18e4
Show file tree
Hide file tree
Showing 11 changed files with 88 additions and 78 deletions.
1 change: 1 addition & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"arrowParens": "avoid",
"bracketSpacing": false,
"singleQuote": true,
"printWidth": 80,
Expand Down
3 changes: 2 additions & 1 deletion src/__tests__/acceptance/in-memory-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ export class InMemoryStore implements Store {

constructor(options?: Options) {
this.hits = {};
this.windowMs = options?.windowMs ?? 60000;
const DEFAULT_WINDOW_MS = 60000;
this.windowMs = options?.windowMs ?? DEFAULT_WINDOW_MS;
// Then calculate the reset time using that
this.resetTime = calculateNextResetTime(this.windowMs);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import {Client} from '@loopback/testlab';
import {memoryStore} from '../store.provider';
import {TestApplication} from './fixtures/application';
import {setUpApplication} from './helper';

const OK_STATUS_CODE = 200;
const TOO_MANY_REQS_CODE = 429;

describe('Acceptance Test Cases', () => {
let app: TestApplication;
let client: Client;
Expand All @@ -15,48 +19,50 @@ describe('Acceptance Test Cases', () => {

after(async () => app.stop());

const MAX_REQUESTS = 5;
it('should hit end point when number of requests is less than max requests allowed', async () => {
//Max request is set to 5 while binding
for (let i = 0; i < 4; i++) {
await client.get('/test').expect(200);
for (let i = 0; i < MAX_REQUESTS; i++) {
await client.get('/test').expect(OK_STATUS_CODE);
}
});

it('should hit end point when number of requests is equal to max requests allowed', async () => {
//Max request is set to 5 while binding
for (let i = 0; i < 5; i++) {
await client.get('/test').expect(200);
for (let i = 0; i < MAX_REQUESTS; i++) {
await client.get('/test').expect(OK_STATUS_CODE);
}
});

it('should give error when number of requests is greater than max requests allowed', async () => {
//Max request is set to 5 while binding
for (let i = 0; i < 5; i++) {
await client.get('/test').expect(200);
for (let i = 0; i < MAX_REQUESTS; i++) {
await client.get('/test').expect(OK_STATUS_CODE);
}
await client.get('/test').expect(429);
await client.get('/test').expect(TOO_MANY_REQS_CODE);
});

it('should overwrite the default behaviour when rate limit decorator is applied', async () => {
//Max request is set to 1 in decorator
await client.get('/testDecorator').expect(200);
await client.get('/testDecorator').expect(429);
await client.get('/testDecorator').expect(OK_STATUS_CODE);
await client.get('/testDecorator').expect(TOO_MANY_REQS_CODE);
});

it('should throw no error if requests more than max are sent after window resets', async () => {
//Max request is set to 5 while binding
for (let i = 0; i < 5; i++) {
await client.get('/test').expect(200);
for (let i = 0; i < MAX_REQUESTS; i++) {
await client.get('/test').expect(OK_STATUS_CODE);
}
const TIMEOUT = 2000;
setTimeout(() => {
client
.get('/test')
.expect(200)
.expect(OK_STATUS_CODE)
.then(
() => {},
() => {},
);
}, 2000);
}, TIMEOUT);
});

async function clearStore() {
Expand Down
3 changes: 2 additions & 1 deletion src/__tests__/acceptance/store.provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ export class StoreProvider implements Provider<Store> {
private readonly config?: RateLimitOptions,
) {}
value(): ValueOrPromise<Store> {
const windowMs = this.config?.windowMs ?? 60000;
const DEFAULT_WINDOW_MS = 60000;
const windowMs = this.config?.windowMs ?? DEFAULT_WINDOW_MS;
memoryStore.setInterval(windowMs);
return memoryStore;
}
Expand Down
1 change: 0 additions & 1 deletion src/__tests__/acceptance/test.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {get} from '@loopback/rest';
import {ratelimit} from '../..';

export class TestController {
constructor() {}
@get('/test', {
responses: {
'200': {
Expand Down
31 changes: 18 additions & 13 deletions src/__tests__/unit/ratelimit-action.provider.unit.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {RestApplication, Request, Response} from '@loopback/rest';
import {RatelimitActionProvider} from '../../providers';
import * as RateLimit from 'express-rate-limit';
import {Constructor} from '@loopback/core';
import sinon from 'sinon';
import proxyquire from 'proxyquire';
import {Request, Response, RestApplication} from '@loopback/rest';
import {expect} from '@loopback/testlab';
import * as RateLimit from 'express-rate-limit';
import {IncrementResponse} from 'express-rate-limit';
import proxyquire from 'proxyquire';
import sinon from 'sinon';
import {RatelimitActionProvider} from '../../providers';

describe('Rate Limit action Service', () => {
let RatelimitActionMockProvider: Constructor<RatelimitActionProvider>;
Expand Down Expand Up @@ -45,34 +45,39 @@ describe('Rate Limit action Service', () => {
it('returns promise if metadata is not enabled', async () => {
const result = new RatelimitActionProvider(
() => {
return new Promise((resolve, reject) => {
resolve(dataStore);
});
return Promise.resolve(dataStore);
},
rateLimitMetadataFalse,
restApplication,
).action({} as Request, {} as Response);
await expect(result).to.be.fulfilled();
});

const EXPECT_TIMEOUT = 5000;
it('returns promise if metadata is enabled', async () => {
const result = new RatelimitActionMockProvider(
dataStore,
rateLimitMetadataTrue,
restApplication,
)
.action({} as Request, {} as Response)
.catch((err) => err.message);
.catch(err => err.message);
await expect(result).to.be.fulfilled();
}).timeout(5000);
}).timeout(EXPECT_TIMEOUT);
});

function increment(key: string): IncrementResponse {
return {totalHits: 0, resetTime: new Date()};
}
function decrement(key: string): void {}
function resetKey(key: string): void {}
function resetAll(): void {}
function decrement(key: string): void {
return;
}
function resetKey(key: string): void {
return;
}
function resetAll(): void {
return;
}

function setupMockRatelimitAction() {
const mockExpressRatelimit = sinon.stub().returns({
Expand Down
18 changes: 6 additions & 12 deletions src/__tests__/unit/ratelimit-datasource.provider.unit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,13 @@ describe('Rate Limit datasource Service', () => {
};
const ratelimitDatasourceProvider = new RatelimitDatasourceProvider(
() => {
return new Promise((resolve) => {
resolve({enabled: true});
});
return Promise.resolve({enabled: true});
},
restApplication,
config,
).value();
let result;
await ratelimitDatasourceProvider.then((value) => {
await ratelimitDatasourceProvider.then(value => {
result = value;
});
expect(result).to.have.properties(['expiration', 'prefix', 'client']);
Expand All @@ -37,15 +35,13 @@ describe('Rate Limit datasource Service', () => {
};
const ratelimitDatasourceProvider = new RatelimitDatasourceProvider(
() => {
return new Promise((resolve) => {
resolve({enabled: true});
});
return Promise.resolve({enabled: true});
},
restApplication,
config,
).value();
let result;
await ratelimitDatasourceProvider.then((value) => {
await ratelimitDatasourceProvider.then(value => {
result = value;
});
expect(result).to.have.properties(['dbOptions', 'expireTimeMs']);
Expand All @@ -57,15 +53,13 @@ describe('Rate Limit datasource Service', () => {
};
const ratelimitDatasourceProvider = await new RatelimitDatasourceProvider(
() => {
return new Promise((resolve) => {
resolve({enabled: true});
});
return Promise.resolve({enabled: true});
},
restApplication,
config,
)
.value()
.catch((err) => err.msg);
.catch(err => err.msg);
expect(ratelimitDatasourceProvider).to.eql(undefined);
});
});
Expand Down
59 changes: 31 additions & 28 deletions src/providers/ratelimit-datasource.provider.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import {CoreBindings, inject, Provider} from '@loopback/core';
import {Getter, juggler} from '@loopback/repository';
import {RateLimitMetadata, RateLimitOptions, Store} from '../types';
import {RateLimitSecurityBindings} from '../keys';
import RedisStore, {RedisReply} from 'rate-limit-redis';
import {HttpErrors, RestApplication} from '@loopback/rest';
import MemcachedStore from 'rate-limit-memcached';
import MongoStore from 'rate-limit-mongo';
import {HttpErrors, RestApplication} from '@loopback/rest';
import RedisStore, {RedisReply} from 'rate-limit-redis';
import {TextDecoder} from 'util';
import {RateLimitSecurityBindings} from '../keys';
import {RateLimitMetadata, RateLimitOptions, Store} from '../types';

const decoder = new TextDecoder('utf-8');
export class RatelimitDatasourceProvider implements Provider<Store> {
constructor(
Expand Down Expand Up @@ -35,38 +36,40 @@ export class RatelimitDatasourceProvider implements Provider<Store> {
if (this.config?.type === 'MemcachedStore') {
const expiration = (opts.windowMs ?? var1 * var2) / var2;
return new MemcachedStore({client: this.config?.client, expiration});
} else if (this.config?.type === 'MongoStore') {
}
if (this.config?.type === 'MongoStore') {
const expireTimeMs = (opts.windowMs ?? var1 * var2) / var2;
return new MongoStore({
uri: this.config?.uri,
collectionName: this.config?.collectionName,
expireTimeMs,
});
} else {
const redisDS = (await this.application.get(
`datasources.${this.config?.name}`,
)) as juggler.DataSource;
if (!redisDS?.connector) {
throw new HttpErrors.InternalServerError('Invalid Datasource');
}
}

return new RedisStore({
sendCommand: async (...args: string[]) => {
const command = `${args[0]}`;
args.splice(0, 1);
let res;
try {
res = await this.executeRedisCommand(redisDS, command, args);
if (command.toLocaleLowerCase() === 'script') {
res = decoder.decode(res as ArrayBuffer);
}
} catch (err) {
throw new Error(`Could not execute redis command ${err}`);
}
return res as RedisReply;
},
});
const redisDS = (await this.application.get(
`datasources.${this.config?.name}`,
)) as juggler.DataSource;

if (!redisDS?.connector) {
throw new HttpErrors.InternalServerError('Invalid Datasource');
}

return new RedisStore({
sendCommand: async (...args: string[]) => {
const command = `${args[0]}`;
args.splice(0, 1);
let res;
try {
res = await this.executeRedisCommand(redisDS, command, args);
if (command.toLocaleLowerCase() === 'script') {
res = decoder.decode(res as ArrayBuffer);
}
} catch (err) {
throw new Error(`Could not execute redis command ${err}`);
}
return res as RedisReply;
},
});
}

// returns promisified execute function
Expand Down
12 changes: 6 additions & 6 deletions src/release_notes/post-processing.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@ module.exports = async function (data, callback) {
const commitTitle = commit.title;
commit.title = commitTitle.substring(0, commitTitle.indexOf('#') - 1);

commit.messageLines = commit.messageLines.filter((message) => {
commit.messageLines = commit.messageLines.filter(message => {
if (message.indexOf('efs/remotes/origin') === -1) return message;
});

commit.messageLines.forEach((message) => {
commit.messageLines.forEach(message => {
commit.issueno = message.includes('GH-')
? message.replace('GH-', '').trim()
: null;
});

const issueDesc = await getIssueDesc(commit.issueno).then((res) => {
const issueDesc = await getIssueDesc(commit.issueno).then(res => {
return res;
});
commit.issueTitle = issueDesc;
Expand Down Expand Up @@ -48,9 +48,9 @@ function getIssueDesc(issueNo) {
`https://github.com/sourcefuse/loopback4-ratelimiter/issues/${encodeURIComponent(
issueNo,
)}`,
(res) => {
res => {
res.setEncoding('utf8');
res.on('data', (chunk) => {
res.on('data', chunk => {
result = result + chunk;
});
res.on('end', () => {
Expand All @@ -69,7 +69,7 @@ function getIssueDesc(issueNo) {
});
},
);
req.on('error', (e) => {
req.on('error', e => {
reject(e);
});
req.end();
Expand Down
2 changes: 1 addition & 1 deletion src/release_notes/release-notes.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ async function addAndCommit() {
await git.push('origin', 'master');
}

generateReleaseNotes().catch((ex) => {
generateReleaseNotes().catch(ex => {
console.error(ex);
process.exit(1);
});
4 changes: 2 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {Request, Response} from '@loopback/rest';
import {Options} from 'express-rate-limit';
import {RedisClient} from 'redis';
import IORedis = require('ioredis');
import MemcachedStore from 'rate-limit-memcached';
import MongoStore from 'rate-limit-mongo';
import RedisStore from 'rate-limit-redis';
import {RedisClient} from 'redis';
import IORedis = require('ioredis');

export type RedisClientType = IORedis.Redis | RedisClient;

Expand Down

0 comments on commit 85e18e4

Please sign in to comment.