-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
fe4d7df
commit 04e1e8e
Showing
13 changed files
with
499 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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']; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
require('@activeprospect/indexer')(__dirname, module); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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'; | ||
} | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}); | ||
}); | ||
}); |
Oops, something went wrong.