pink-bears
Version:
Intelligent rate limiting middleware with MongoDB integration and caching for Node.js applications
180 lines (155 loc) • 4.86 kB
JavaScript
const constants = await require("../constants/index");
const { initHandlers } = require("../appnest-backup/sparrowapps-helper/index");
const { hasExpired } = require("../helpers/index");
const { $Storage, $Fetch } = initHandlers();
const { baseUrl, endPoints, grantType, httpMethods } = constants;
class BackendTemplate {
constructor(appnestUserId, productId, userId, clientId, clientSecret, redirectUri, IntegrationName) {
Object.assign(this, {
appnestUserId,
productId,
userId,
clientId,
clientSecret,
redirectUri,
IntegrationName
});
}
async checkHasFeature(featureName) {
try {
const feature = await this.makeRequest({
appnestUserId: this.appnestUserId,
userId: this.userId,
method: httpMethods.GET,
endPoint: endPoints.featureEndpoint,
queryString: `feature_name=${featureName}`,
});
return feature?.data.data.feature_exists;
} catch (error) {
this.logError('Error while checking feature', error);
throw error;
}
}
async makeRequest({
method,
endPoint,
queryString = null,
payload,
apiKey,
}) {
try {
let accessToken = apiKey;
if (!apiKey) {
accessToken = await this.getAccessToken();
}
const url = `${baseUrl}${endPoint}${queryString ? `?${queryString}` : ''}`;
const apiConfig = {
headers: { Authorization: `Bearer ${accessToken}` },
isOAuth: false,
maxAttempts: 5,
};
this.logApiRequest(method, url, payload);
return await this.executeRequest(method, url, payload, apiConfig);
} catch (error) {
this.logError('Error while making request', error);
throw error;
}
}
async getAccessToken() {
const storage = await $Storage.getV2(this.userId);
let { accessToken, refreshToken, expiresIn } = storage;
if (hasExpired(expiresIn)) {
const refreshedToken = await this.refreshAccessToken(refreshToken);
accessToken = refreshedToken.data.access_token;
await $Storage.setV2(this.userId, {
...storage,
accessToken,
expiresIn: new Date(
(Math.floor(Date.now() / 1000) + refreshedToken.data.expires_in) * 1000
),
});
}
return accessToken;
}
async refreshAccessToken(refreshToken) {
const refreshPayload = {
client_id: this.clientId,
client_secret: this.clientSecret,
grant_type: grantType.refreshToken,
refresh_token: refreshToken,
};
return await $Fetch.post(
`${baseUrl}${endPoints.tokenEndpoint}`,
refreshPayload
);
}
executeRequest(method, url, payload, apiConfig) {
const methodMap = {
[httpMethods.GET]: () => $Fetch.get(url, apiConfig),
[httpMethods.POST]: () => $Fetch.post(url, payload, apiConfig),
[httpMethods.PUT]: () => $Fetch.put(url, payload, apiConfig),
[httpMethods.DELETE]: () => $Fetch.delete(url, apiConfig),
};
const requestMethod = methodMap[method];
if (!requestMethod) {
throw new Error(`Unsupported method: ${method}`);
}
return requestMethod();
}
logApiRequest(method, url, payload) {
console.log(
`<------------- [${this.IntegrationName}] Api request => ${method} Url => ${url} ${
payload ? `Payload => ${JSON.stringify(payload)}` : ''
} ------------->`
);
}
logError(message, error) {
console.error(
`[${this.IntegrationName}] ${message} for userId ${this.appnestUserId}`,
error
);
}
async setStorageV2({ key, data }) {
try {
const userData = (await $Storage.getV2(this.userId)) || {};
userData[key] = data;
return await $Storage.setV2(this.userId, { ...userData });
} catch (error) {
this.logError('Error while setting storage', error);
throw error;
}
}
async getStorageV2({ key }) {
try {
const userData = (await $Storage.getV2(this.userId)) || {};
return userData[key];
} catch (error) {
this.logError('Error while getting storage', error);
throw error;
}
}
async getUser() {
try {
const user = await this.makeRequest({
appnestUserId: this.appnestUserId,
method: httpMethods.GET,
endPoint: endPoints.usersEndpoint,
});
return user?.data.data.filter(user => user.current_user);
} catch (error) {
this.logError('Error while getting user', error);
throw error;
}
}
async exchangeAuthCode(code) {
const payload = {
grant_type: grantType.authorizationCode,
code,
redirect_uri: this.redirectUri,
client_id: this.clientId,
client_secret: this.clientSecret,
};
return await $Fetch.post(`${baseUrl}${endPoints.tokenEndpoint}`, payload, {});
}
}
module.exports = BackendTemplate;