Skip to content

Docs: Feedback for Fraud Detection with AWS and Hazelcast Cloud #3

@YAHYA280

Description

@YAHYA280

Feedback for Hazelcast Serverless Fraud Detection Guide

Hi Hazelcast Team,

I have some feedback regarding the validate.js file in your guide. The current implementation contains an issue where the await statement is used outside of an async function. Specifically, this line at the top of the file:

await users.set(userId, user);

This causes a runtime error. To resolve this, the await statement must be moved into the appropriate scope where it is inside an async function.

Corrected Code :

Here is the corrected version of validate.js:

const hazelcast = require('./hazelcast');
const haversine = require('haversine');
const moment = require('moment');

exports.handle = async (request, context, callback) => {
    console.log('Got request: ' + JSON.stringify(request));
    // This is an Amazon Lambda-specific setting to prevent waiting until the Node.js runtime event loop is empty.
    // You can find more info about this setting in the AWS docs: https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html
    context.callbackWaitsForEmptyEventLoop = false;

    let userId = request.userId;
    let requestTimestampMillis = moment(request.transactionTimestamp).utc().valueOf();

    let hazelcastClient = await hazelcast.getClient();
    let airports = await hazelcastClient.getMap('airports');
    if (await airports.isEmpty()) {
        return callback('Airports data is not initialized', null);
    }
    let users = await hazelcastClient.getMap('users');

    let user = await users.get(userId);
    if (!user) {
        user = {
            userId: userId,
            lastCardUsePlace: request.airportCode,
            lastCardUseTimestamp: requestTimestampMillis
        };
        await users.set(userId, user); // Moved into the correct scope
        // Check whether any data exists for a user associated with the incoming request.
        // If it’s a new user, save it for the future validations and return a corresponding result.
        return callback(null, {valid: true, message: 'User data saved for future validations'});
    }

    // If there is available data about the previous transaction, get details about the current and prior airports.
    // Skip the validation if the airports are the same.
    let [lastAirport, nextAirport] = await Promise.all([
        airports.get(user.lastCardUsePlace),
        airports.get(request.airportCode)
    ]);
    if (lastAirport.code === nextAirport.code) {
        return callback(null, {valid: true, message: 'Transaction performed from the same location'});
    }

    let speed = getSpeed(lastAirport, user.lastCardUseTimestamp, nextAirport, request.transactionTimestamp);
    // Use the haversine formula to calculate a “user speed” between two transactions.
    // If it’s faster than an average plane’s speed, 800 km/hr == ~13000 m/min, the transaction is suspicious.
    let valid = speed <= 13000;
    let message = valid ? 'Transaction is OK' : 'Transaction is suspicious';

    // Store the data from the request for the future validations.
    user.lastCardUsePlace = request.airportCode;
    user.lastCardUseTimestamp = requestTimestampMillis;
    await users.set(userId, user);

    return callback(null, {valid: valid, message: message});
};

let getSpeed = (lastAirport, lastUseTimestamp, nextAirport, requestTimestamp) => {
    // Time
    let minutes = moment(requestTimestamp).diff(lastUseTimestamp, 'minutes');
    // Distance
    let meters = haversine(nextAirport, lastAirport, {unit: 'meter'});
    // Speed
    return meters / minutes;
};

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions