Skip to content

Commit

Permalink
decaffeinate wip
Browse files Browse the repository at this point in the history
  • Loading branch information
jgrayson676 committed Oct 14, 2024
1 parent fe4d7df commit 04e1e8e
Show file tree
Hide file tree
Showing 13 changed files with 499 additions and 8 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
lib
.eslintrc.js
node_modules
*.tgz
.idea
.DS_Store
.DS_Store
1 change: 0 additions & 1 deletion Cakefile

This file was deleted.

30 changes: 30 additions & 0 deletions lib/http-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* decaffeinate suggestions:
* DS002: Fix invalid constructor
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
class HttpError extends Error {
constructor(status, headers, body) {
super(...arguments);
this.status = status;
this.headers = headers;
this.body = body;
this.message = 'integration terminated early';
this.name = 'HttpError';
Error.captureStackTrace(this, arguments.callee);
}
}


module.exports = HttpError;
//HttpError = (status, headers, body) ->


// Error.call(@)
// Error.captureStackTrace(@, arguments.callee)
// @status = status
// @headers = headers
// @body = body
// @name = 'HttpError'
//
//HttpError.prototype.__proto__ = Error.prototype;
3 changes: 3 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
require('@activeprospect/indexer')(__dirname, module);

module.exports.HttpError = module.exports['http-error'];
38 changes: 38 additions & 0 deletions lib/strip-metadata.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const _ = require('lodash');
const flat = require('flat');

const excludePatterns = [
/^timestamp$/,
/^timeout_seconds$/,
/^random$/,
/^flow\./,
/^source\./,
/^account\./,
/^lead\.id/,
/^recipient\./,
/^submission\./,
/\.outcome$/,
/\.reason$/,
/\.billable$/
];

// given the `vars` "snowball", this will strip all the data that a user
// wouldn't want delivered as a "lead". used, for example, by email-delivery
module.exports = function(vars, additionalExcludes = []) {

for (var exclude of additionalExcludes) {
// \b = word-boundary, so, e.g., "cc" doesn't exclude "accept"
excludePatterns.push(_.isRegExp(exclude) ? exclude : RegExp(`\\b${exclude}\\b`));
}

const stripped = {};
const flatVars = flat.flatten(vars);
for (var key in flatVars) {
if (!excludePatterns.some(val => key.match(val))) {
// replace attribute, moving "lead." fields up a level
_.set(stripped, key.replace(/^lead\./, ''), flatVars[key]?.valueOf());
}
}

return stripped;
};
1 change: 1 addition & 0 deletions lib/test/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require('@activeprospect/indexer')(__dirname, module);
28 changes: 28 additions & 0 deletions lib/test/types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* DS102: Remove unnecessary code created because of implicit returns
*/
const types = require('leadconduit-types');
const flat = require('flat');

module.exports.parser = function(requestVars = []) {
const typeLookup = {};

for (let requestVar in requestVars) {
typeLookup[requestVar.name] = types[requestVar.type];
}

return function(vars) {
const parsed = {};
const object = flat.flatten(vars, {safe: true});
for (let name in object) {
let value = object[name];
let parse = typeLookup[name]?.parse;
if ((parse != null) && (value != null)) {
parsed[name] = parse(value);
} else {
parsed[name] = value;
}
}
return flat.unflatten(parsed);
};
};
67 changes: 67 additions & 0 deletions lib/util.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
const humanname = require('humanname');
const mimeparse = require('mimeparse');
const HttpError = require('../lib/http-error');
const stripMetadata = require('../lib/strip-metadata');

const selectMimeType = function(contentType, supportedTypes) {
if (contentType == null) { return; }
return mimeparse.bestMatch(supportedTypes, contentType);
};

module.exports = {

// given a string containing a person's full name, return a two-element array containing firstName and lastName
parseFullname(fullname) {
const parsed = humanname.parse(fullname);

return {
salutation: parsed.salutation,
first_name: parsed.firstName,
middle_name: parsed.initials,
last_name: parsed.lastName,
suffix: parsed.suffix
};
},

validateRequest(req, mimeTypes, methods) {

//ensure supported method
const method = req.method?.toLowerCase();
let supportedMethods = methods || ['POST'];
supportedMethods = supportedMethods.map(val => val.toLowerCase());
if (!(supportedMethods.indexOf(method) > -1)) {
throw new HttpError(415, { 'Content-Type': 'text/plain', Allow: 'POST' }, `The ${method.toUpperCase()} method is not allowed`);
}

//ensure a content-type header
const contentType = req.headers['Content-Type'];
if (!contentType) {
throw new HttpError(415, { 'Content-Type': 'text/plain' }, 'Content-Type header is required');
}

//ensure a valid mime type
if (!(mimeTypes.indexOf(selectMimeType(req.headers['Content-Type'], mimeTypes)) > -1)) {
throw new HttpError(415, {'Content-Type': 'text/plain'}, `MIME type in Content-Type header is not supported. Use only ${mimeTypes.join(', ')}`);
}
},

// given a starting timestamp and the total timeout (in ms), return the remaining timeout (in ms)
calculateTimeout(startTimestamp, timeoutMs) {
const elapsedMs = new Date().getTime() - startTimestamp;
const remaining = timeoutMs - elapsedMs;
if (remaining <= 0) { return 1; } // the `request` library treats a timeout value of '0' as no timeout
return remaining;
},

stripMetadata,

getBaseUrl(env) {
if (env == null) { env = process.env.NODE_ENV; }
switch (env) {
case 'staging': return 'https://app.leadconduit-staging.com';
case 'development': return 'https://app.leadconduit-development.com';
case 'local': return 'http://leadconduit.localhost';
default: return 'https://app.leadconduit.com';
}
}
};
7 changes: 2 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
"url": "https://github.com/activeprospect/leadconduit-integration.git"
},
"scripts": {
"test": "cake test",
"prepublish": "cake build"
"test": "mocha"
},
"dependencies": {
"@activeprospect/indexer": "^1.3.1",
Expand All @@ -19,8 +18,6 @@
"mimeparse": "^0.1.4"
},
"devDependencies": {
"chai": "^1.9.0",
"coffee-script": ">= 1.7.0",
"leadconduit-cakefile": "^0.3.3"
"@activeprospect/integration-dev-dependencies": "^2.0.0"
}
}
28 changes: 28 additions & 0 deletions test/http-error-spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
const { assert } = require('chai');
const HttpError = require('../lib/http-error');

describe('HttpError', function() {

it('should capture stack trace', function() {
const fxn = function() {
throw new HttpError(500, {}, '');
};
try {
fxn();
return assert.fail();
} catch (e) {
const stack = e.stack.split('\n');
assert.equal(stack[0], 'HttpError: integration terminated early');
return assert.match(stack[1], /at fxn.*test\/http\-error\-spec\.coffee\:/);
}
});

it('should have name', () => assert.equal(new HttpError(500, {}, '').name, 'HttpError'));

it('should be an instance of HttpError', () => assert(new HttpError() instanceof HttpError));
});
123 changes: 123 additions & 0 deletions test/strip-metadata-spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
const { assert } = require('chai');
const types = require('leadconduit-types');
const strip = require('../lib/strip-metadata');

describe('Strip metadata', function() {

beforeEach(function() {
this.vars = {
submission: {
timestamp: '2019-02-27T08:02:53.669Z'
},
account: {
id: 'account123',
name: 'Lead Garden, Inc.',
sso_id: 'sso123'
},
flow: {
id: 'flow123',
name: 'Sales Leads'
},
random: 24,
source: {
id: 'source123',
name: 'Contact Form'
},
to: '[email protected]',
cc: '[email protected]',
lead: {
id: 'lead123',
email: types.email.parse('[email protected]'),
first_name: 'Dee',
last_name: 'Daniels',
phone_1: types.phone.parse('512-555-1212'),
trustedform_cert_url: types.url.parse('https://cert.trustedform.com/cert123')
},
suppression_list: {
query_item: {
key: '[email protected]',
found: types.boolean.parse(false),
reason: null,
outcome: 'success',
duration: types.number.parse(0.0123),
specified_lists: ['sales_leads']
},
add_item: {
reason: null,
outcome: 'success',
accepted: types.number.parse(1),
rejected: types.number.parse(0),
duration: types.number.parse(0.0456)
}
},
anura: {
outcome: 'success',
billable: types.number.parse(1),
is_suspect: types.boolean.parse(false)
},
leadconduit_classic: {
id: 'classic123'
}
};
});


it('should strip basic metadata', function() {
const expected = {
email: '[email protected]',
first_name: 'Dee',
last_name: 'Daniels',
phone_1: '5125551212',
trustedform_cert_url: 'https://cert.trustedform.com/cert123',
to: '[email protected]',
cc: '[email protected]',
suppression_list: {
query_item: {
key: '[email protected]',
found: false,
duration: 0.0123,
specified_lists: ['sales_leads']
},
add_item: {
accepted: 1,
rejected: 0,
duration: 0.0456
}
},
anura: {
is_suspect: false
},
leadconduit_classic: {
id: 'classic123'
}
};

assert.deepEqual(strip(this.vars), expected);
});


it('should strip basic metadata plus additional fields', function() {
const expected = {
email: '[email protected]',
first_name: 'Dee',
last_name: 'Daniels',
phone_1: '5125551212',
trustedform_cert_url: 'https://cert.trustedform.com/cert123',
to: '[email protected]',
suppression_list: {
add_item: {
accepted: 1,
rejected: 0
}
},
anura: {
is_suspect: false
},
leadconduit_classic: {
id: 'classic123'
}
};

assert.deepEqual(strip(this.vars, [new RegExp('query_item.*'), new RegExp('.*.duration'), 'cc']), expected);
});
});
28 changes: 28 additions & 0 deletions test/test-types-parse-spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const { assert } = require('chai');
const { parser } = require('../lib/test/types');

describe('Test types parse', function() {

it('should parse number to string', function() {
const parse = parser([ {name: 'foo', type: 'string'} ]);
assert.deepEqual(parse({foo: 5}), {foo: '5'});
});

it('should parse to typed value', function() {
const parse = parser([ {name: 'foo', type: 'number'} ]);
const parsed = parse({foo: '5'});
assert.equal(parsed.foo.valueOf(), 5);
assert.equal(parsed.foo.raw, '5');
assert.isTrue(parsed.foo.valid);
});

it('should do nothing if request variable is missing a type', function() {
const parse = parser([ {name: 'foo'} ]);
assert.deepEqual(parse({foo: 5}), {foo: 5});
});

it('should do nothing if request variable is missing', function() {
const parse = parser([]);
assert.deepEqual(parse({foo: 5}), {foo: 5});
});
});
Loading

0 comments on commit 04e1e8e

Please sign in to comment.