Skip to content

Commit

Permalink
feat: add js client to send JSON data to server
Browse files Browse the repository at this point in the history
  • Loading branch information
prakharmathur82 committed Aug 7, 2023
1 parent 8a6c348 commit c5e24b5
Show file tree
Hide file tree
Showing 12 changed files with 4,046 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ coverage
*.idea/
raccoon
.temp
node_modules
1 change: 1 addition & 0 deletions clients/js/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Raccoon js client
113 changes: 113 additions & 0 deletions clients/js/lib/rest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
const axios = require('axios');
const uuid = require('uuid');

const createJsonSerializer = require('./serializer/json_serializer');
const createProtobufSerializer = require('./serializer/proto_serializer');
const retry = require('./retry/retry')

const NANOSECONDS_PER_MILLISECOND = 1e6;


class RaccoonClient {
/**
* Creates a new instance of the RaccoonClient.
*
* @constructor
* @param {Object} config - Configuration options for the RaccoonClient.
* @param {string} [config.serializationType='json'] - The serialization type to use, either 'protobuf' or 'json'.
* @param {Object} [config.wire] - The wire configuration, containing options like ContentType.
* @param {Object} [config.headers] - Custom headers to be included in the HTTP requests.
* @param {number} [config.retryMax=3] - The maximum number of retry attempts for failed requests.
* @param {number} [config.retryWait=1000] - The time in milliseconds to wait between retry attempts.
* @param {string} [config.url=''] - The base URL for the API requests.
* @returns {RestApiClient} A new instance of the RaccoonClient.
*/
constructor(config = {}, httpClient) {
this.serializer = config.serializationType === 'protobuf'
? createProtobufSerializer()
: createJsonSerializer();
this.wire = config.wire || { ContentType: 'application/json' };
this.httpClient = httpClient || axios.create();
this.headers = config.headers || {};
this.retryMax = config.retryMax || 3;
this.retryWait = config.retryWait || 5000;
this.url = config.url || '';
}


/**
* Sends a batch of events to the server.
*
* @async
* @param {Array} events - An array of event objects to send.
* @returns {Promise} A promise that resolves to an object containing the request ID, response, and error details (if any).
* @throws {Error} Throws an error if the event is invalid or if there's an issue with the request.
*/
async send(events) {
const requestId = uuid.v4();
console.info(`started request, url: ${this.url}, req-id: ${requestId}`);
const eventsToSend = [];
for (const event of events) {
if (event && event.type && event.data) {
eventsToSend.push({
type: event.type,
event_bytes: this.serializer.serialize(event.data),
});
} else {
throw new Error(`Invalid event: ${JSON.stringify(event)}`);
}
}

const sentTime = this.getCurrentTimestamp();

const raccoonRequest = {
req_guid: requestId,
events: eventsToSend,
sent_time: sentTime,
};

try {
const response = await retry(
async () => this.executeRequest(JSON.stringify(raccoonRequest)),
this.retryMax,
this.retryWait
);

const status = parseInt(response.status, 10);
const code = parseInt(response.code, 10);

console.info(`ended request, url: ${this.url}, req-id: ${requestId}`);
return {
reqId: requestId,
response: {
Status: status,
Code: code,
SentTime: response.sent_time,
Data: response.data,
},
error: null,
};
} catch (error) {
console.error(`error, url: ${this.url}, req-id: ${requestId}, ${error}`);
return { reqId: requestId, response: {}, error };
}
}

async executeRequest(raccoonRequest) {
this.headers['Content-Type'] = this.wire.ContentType;
const response = await this.httpClient.post(this.url, raccoonRequest, {
headers: this.headers,
});
return response.data;
}

getCurrentTimestamp() {
const now = Date.now();
return ({
seconds: Math.floor(now / 1000),
nanos: (now % 1000) * NANOSECONDS_PER_MILLISECOND
});
}
}

module.exports = RaccoonClient;
16 changes: 16 additions & 0 deletions clients/js/lib/retry/retry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
async function retry(callback, maxAttempts, waitTime) {
for (let attempt = 0; attempt < maxAttempts; attempt++) {
try {
return await callback();
} catch (error) {
if (attempt === maxAttempts - 1) {
throw error;
}
await new Promise(resolve => setTimeout(resolve, waitTime));
console.info(`[Retry ${attempt}]: Retrying after ${waitTime} due to the Error: ${error}`);
}
}
throw new Error('Retry limit exceeded');
}

module.exports = retry;
12 changes: 12 additions & 0 deletions clients/js/lib/serializer/json_serializer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
function createJsonSerializer() {
function serialize(data) {

const jsonString = JSON.stringify(data);

return Array.from(Buffer.from(jsonString));
}

return { serialize };
}

module.exports = createJsonSerializer;
10 changes: 10 additions & 0 deletions clients/js/lib/serializer/proto_serializer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
function createProtobufSerializer() {

function serialize(data) {
// TODO://Return the serialized protobuf data
}

return { serialize };
}

module.exports = createProtobufSerializer;
5 changes: 5 additions & 0 deletions clients/js/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const RaccoonClient = require('./lib/rest');

module.exports = {
RaccoonClient
};
Loading

0 comments on commit c5e24b5

Please sign in to comment.