Skip to content
This repository has been archived by the owner on Dec 10, 2022. It is now read-only.

Commit

Permalink
Continue linting fixes; refactor exit() func
Browse files Browse the repository at this point in the history
  • Loading branch information
tdmalone committed Jan 28, 2018
1 parent 1fef97c commit 3b0769a
Showing 1 changed file with 98 additions and 79 deletions.
177 changes: 98 additions & 79 deletions index.js
Expand Up @@ -23,98 +23,40 @@ const FIRST_ITEM = 0,

exports.handler = ( event, context, callback ) => {

// Prepare the API response.
const response = {
isBase64Encoded: false,
statusCode: HTTP_OK,
headers: {}
};

// Put together the event data we want.
let geoEventData;
try {
geoEventData = parseInput( event.body, event.headers['content-type']);
} catch ( error ) {
exitWithError( 'Error: Could not parse input. ' + error + '. ' + event.body, callback );
exit( 'Error: Could not parse input. ' + error + '. ' + event.body, null, callback );
return;
}

geoEventData.person = event.queryStringParameters.person;

// Normalize the data that Proximity Events sends with its occasional form-data requests.
// We want to use the same fields that we have available for the usual JSON requests.
/* eslint-disable camelcase */
if ( 'application/x-www-form-urlencoded' === event.headers['content-type']) {

geoEventData.comments = 'Converted from form-data';

geoEventData.event_latitude = geoEventData.latitude;
geoEventData.event_longitude = geoEventData.longitude;
geoEventData.event_accuracy_m = '0.0'; // Unavailable.
geoEventData.event_address = 'Unknown location'; // Unavailable, although we could look it up.
geoEventData.trigger_name = geoEventData.id;
geoEventData.trigger_type = 'Geofence';

// Generate eg. 'Geofence:Exit' from 'exit'.
geoEventData.event_type = (
'Geofence:' + geoEventData.trigger.replace( /^(\w)/, letter => letter.toUpperCase() )
);

// Convert eg. '1516705689.304757' to eg. '2018-01-09T21:38:30+11:00'.
// TODO: Need to add timezone handling to this.
geoEventData.event_date = new Date( parseInt( geoEventData.timestamp * TO_MILLISECONDS ) );
geoEventData.event_date = geoEventData.event_date.toISOString();

delete geoEventData.device;
delete geoEventData.device_model;
delete geoEventData.device_type;
delete geoEventData.latitude;
delete geoEventData.longitude;
delete geoEventData.id;
delete geoEventData.trigger;
delete geoEventData.timestamp;

}
/* eslint-enable camelcase */

// Drop Visit:Exit events, as they'll often supply an old address way too late.
// TODO: Make this configurable via an environment variable.
if ( 'Visit:Exit' === geoEventData.event_type ) {
console.log( 'Dropped unwanted Visit:Exit event.' );
response.body = JSON.stringify({ message: 'Dropped unwanted Visit:Exit event.' });
callback( null, response );
exit( null, { message: 'Dropped unwanted Visit:Exit event.' }, callback );
return;
}

// Add any optional GET parameters to the request.
// This will have the side-effect of overriding the POST params if any of them are the same.
Object.keys( event.queryStringParameters ).forEach( ( key ) => {
geoEventData[ key ] = event.queryStringParameters[ key ];
});

// Remove some data we don't need.
delete geoEventData.event_id;
delete geoEventData.trigger_id;

// Store an SNS message to process everything else we need, so we can return quickly.

const snsMessage = {
Message: JSON.stringify( geoEventData ),
TopicArn: SNS_QUEUE
};

aws.config.region = 'ap-southeast-2';
const sns = new aws.SNS();

sns.publish( snsMessage, ( error, data ) => {

if ( error ) {
exitWithError( error, callback );
return;
}

console.log( 'Queued.' );
console.log( data );
response.body = JSON.stringify({ message: 'Queued.' });
// Normalize the data that Proximity Events sends with its occasional form-data requests.
if ( 'application/x-www-form-urlencoded' === event.headers['content-type']) {
geoEventData = normalizeFormData( geoEventData );
}

callback( null, response );
return;
// Store an SNS message to process everything else we need, so we can return quickly.
sendSnsMessage( geoEventData, callback );

}); // Sns.publish.
}; // Exports.handler.

/**
Expand Down Expand Up @@ -156,21 +98,98 @@ function parseInput( input, contentType ) {
} // Function parseInput.

/**
* Exits the Lambda function, in an API Gateway compatible way, while passing an error back.
* Normalizes the data that Proximity Events sends with its occasional form-data requests. We
* want to use the same fields that we have available for the usual JSON requests.
*
* Form data requests are assumed to be Geofence enters or exits, as so far in our testing that
* is all they have appeared to be.
*/
function normalizeFormData( data ) {
/* eslint-disable camelcase */

data.comments = 'Converted from form-data';

data.event_latitude = data.latitude;
data.event_longitude = data.longitude;
data.event_accuracy_m = '0.0'; // Unavailable.
data.event_address = 'Unknown location'; // Unavailable, although we could look it up.
data.trigger_name = data.id;
data.trigger_type = 'Geofence';

// Generate eg. 'Geofence:Exit' from 'exit'.
data.event_type = (
'Geofence:' + data.trigger.replace( /^(\w)/, letter => letter.toUpperCase() )
);

// Convert eg. '1516705689.304757' to eg. '2018-01-09T21:38:30+11:00'.
// TODO: Need to add timezone handling to this.
data.event_date = new Date( parseInt( data.timestamp * TO_MILLISECONDS ) );
data.event_date = data.event_date.toISOString();

delete data.device;
delete data.device_model;
delete data.device_type;
delete data.latitude;
delete data.longitude;
delete data.id;
delete data.trigger;
delete data.timestamp;

return data;

/* eslint-enable camelcase */
} // Function normalizeFormData.

/**
* Sends an SNS messaage, calling a callback on completion.
*/
function sendSnsMessage( message, callback ) {

const snsMessage = {
Message: JSON.stringify( message ),
TopicArn: SNS_QUEUE
};

aws.config.region = 'ap-southeast-2';
const sns = new aws.SNS();

sns.publish( snsMessage, ( error ) => {

if ( error ) {
exit( error, null, callback );
return;
}

exit( null, { message: 'Queued.' }, callback );
return;

}); // Sns.publish.
} // Function sendSnsMessage.

/**
* Exits the Lambda function, in an API Gateway compatible way, while passing any errors back.
* If an HTTP status code is available in the error, that will be used, otherwise defaults to 500.
*
* If running in CI, errors will be passed back as errors, rather than API Gateway responses.
*
* @param {object|null} error
* @param {object|null} data
* @param {function} callback
*/
function exitWithError( error, callback ) {
function exit( error, data, callback ) {

// Prepare the API response.
const response = {
isBase64Encoded: false,
statusCode: error.statusCode || HTTP_SERVER_ERROR,
statusCode: error ? ( error.statusCode || HTTP_SERVER_ERROR ) : HTTP_OK,
headers: {},
body: JSON.stringify({ error: error })
body: JSON.stringify( error ? { error: error } : data )
};

console.error( error );
const logFunction = error ? console.error : console.info;
logFunction( error || data );

callback( CI ? error : null, response );
callback( error && CI ? error : null, response );
return;

} // Function exitWithError.
} // Function exit.

0 comments on commit 3b0769a

Please sign in to comment.