Skip to content

Commit 2b71564

Browse files
committed
feat: rate limit requests using aperture
1 parent 5397704 commit 2b71564

File tree

2 files changed

+80
-11
lines changed

2 files changed

+80
-11
lines changed

contributors.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ exports.getRepoContributors = getRepoContributors;
1010

1111
const path = require('path');
1212

13-
const { makeRequest } = require('./network.js');
13+
const { makeRequestWithRateLimit } = require('./network.js');
1414

1515
// Configurations (Optional)
1616
// Repo owner that you want to analyze
@@ -43,7 +43,7 @@ async function getAllRepos(owner=REPO_OWNER, options) {
4343
GITHUB_REQUEST_OPTIONS.headers["Authorization"] = "token "+options.GITHUB_PERSONAL_TOKEN;
4444
}
4545
let url = `https://api.github.com/orgs/${owner}/repos?per_page=100&page=${pageNo}`;
46-
const { res, data } = await makeRequest('GET', url, Object.assign({},GITHUB_REQUEST_OPTIONS));
46+
const { res, data } = await makeRequestWithRateLimit('GET', url, Object.assign({},GITHUB_REQUEST_OPTIONS));
4747
console.log("Repo list request finished");
4848
console.log('HTTP status: ', res.statusCode);
4949
// console.log(data)
@@ -71,7 +71,7 @@ async function getAllRepos(owner=REPO_OWNER, options) {
7171
async function getRepoContributors(fullRepoName, pageNo = 1) {
7272
let url = `https://api.github.com/repos/${fullRepoName}/contributors?per_page=100&page=${pageNo}`;
7373
console.log(url);
74-
const { res, data } = await makeRequest('GET', url, Object.assign({},GITHUB_REQUEST_OPTIONS));
74+
const { res, data } = await makeRequestWithRateLimit('GET', url, Object.assign({},GITHUB_REQUEST_OPTIONS));
7575
console.log("Contributors request finished for " + fullRepoName)
7676
// console.log(data)
7777
let dataJsonArray = JSON.parse(data);

network.js

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,29 @@
11
const https = require('https');
2+
const { ApertureClient } = require("@fluxninja/aperture-js");
23

34
exports.makeRequest = makeRequest;
5+
exports.makeRequestWithRateLimit = makeRequestWithRateLimit;
6+
7+
var apertureClient;
8+
9+
function getApertureClient(){
10+
if(!apertureClient) {
11+
apertureClient = new ApertureClient({
12+
address: process.env.APERTURE_SERVICE_ADDRESS,
13+
apiKey: process.env.APERTURE_API_KEY,
14+
});
15+
}
16+
return apertureClient;
17+
}
418

519
/**
620
* Sends an HTTPS request based on the specified method and options
721
* This function returns a Promise that resolves with the response and data received from the server
822
* @param {string} method - The HTTP method to use (e.g., 'GET', 'POST').
923
* @param {string} url - The URL to which the request is sent.
10-
* @param {Object} [options] - The options for the request. This includes headers, request body (for POST/PUT), etc.
24+
* @param {Object} [requestOptions] - The options for the request. This includes headers, request body (for POST/PUT), etc.
25+
* @param {Object} [requestOptions.body] - The data that needs to be sent with the request, used for POST/PUT requests
26+
* @param {Object} [requestOptions.headers] - The headers that needs to be sent with the request
1127
* @returns {Promise<{res: https.IncomingMessage, data: string}>} A Promise that resolves with the response object and the body data as a string.
1228
* @throws {Error} Throws an error if the request cannot be completed
1329
*
@@ -23,13 +39,13 @@ exports.makeRequest = makeRequest;
2339
* }
2440
* }
2541
* */
26-
async function makeRequest(method, url, options) {
42+
async function makeRequest(method, url, requestOptions) {
2743
return new Promise((resolve, reject) => {
2844
// Ensure options is an object and set the method
29-
options = typeof options === 'object' ? options : {};
30-
options.method = method;
45+
requestOptions = typeof requestOptions === 'object' ? requestOptions : {};
46+
requestOptions.method = method;
3147

32-
const req = https.request(url, options, res => {
48+
const req = https.request(url, requestOptions, res => {
3349
// Handle HTTP response stream
3450
let data = '';
3551
res.on('data', chunk => data += chunk);
@@ -42,10 +58,63 @@ async function makeRequest(method, url, options) {
4258
});
4359

4460
// Handle POST/PUT data if provided
45-
if (options.data) {
46-
req.write(options.data);
61+
if (requestOptions.data) {
62+
req.write(requestOptions.data);
4763
}
4864

4965
req.end();
5066
});
51-
}
67+
}
68+
69+
70+
/**
71+
* Sends an HTTPS request with rate limiting. The function checks if the request is allowed by the rate limiter,
72+
* and if so, sends an HTTPS request based on the specified method and options. This function returns a Promise
73+
* that resolves with the response and data received from the server or rejects if an error occurs or the rate limit is exceeded.
74+
*
75+
* @param {string} method - The HTTP method to use (e.g., 'GET', 'POST').
76+
* @param {string} url - The URL to which the request is sent.
77+
* @param {Object} [options] - The options for the request. This includes headers, request body (for POST/PUT), etc.
78+
* @param {Object} [options.rateLimitOptions] - Options related to rate limits
79+
* @param {Object} [options.body] - The data that needs to be sent with the request, used for POST/PUT requests
80+
* @param {Object} [options.headers] - The headers that needs to be sent with the request
81+
* @returns {Promise<{res: https.IncomingMessage, data: string}>} A Promise that resolves with the response object and the body data as a string.
82+
* @throws {Error} Throws an error if the rate limit is exceeded or if an invalid argument is passed.
83+
*
84+
* @example
85+
* // Example usage for a GET request within an async function
86+
* async function getExample() {
87+
* try {
88+
* const { res, data } = await makeRequest('GET', 'https://example.com');
89+
* console.log('Status Code:', res.statusCode);
90+
* console.log('Body:', data);
91+
* } catch (error) {
92+
* console.error('Error:', error.message);
93+
* }
94+
* }
95+
* */
96+
async function makeRequestWithRateLimit(method, url, requestOptions){
97+
const flow = await apertureClient.startFlow("external-api-request", {
98+
labels: {
99+
url: url,
100+
},
101+
grpcCallOptions: {
102+
deadline: Date.now() + 300, // ms
103+
},
104+
});
105+
106+
if (flow.shouldRun()) {
107+
// Add business logic to process incoming request
108+
console.log("Request accepted. Processing...");
109+
const {res, data} = await makeRequest(...arguments)
110+
return { res, data}
111+
} else {
112+
console.log("Request rate-limited. Try again later.");
113+
// Handle flow rejection
114+
flow.setStatus(FlowStatus.Error);
115+
}
116+
117+
if (flow) {
118+
flow.end();
119+
}
120+
}

0 commit comments

Comments
 (0)