Skip to content

Commit

Permalink
PP-11032 Remove Humps module (#3748)
Browse files Browse the repository at this point in the history
* PP-11032 Remove Humps module
  • Loading branch information
hjvoid authored Nov 2, 2023
1 parent ded51e2 commit 23231b3
Show file tree
Hide file tree
Showing 6 changed files with 217 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
const logger = require('../../../utils/logger')(__filename)
const { getLoggingFields } = require('../../../utils/logging-fields-helper')
const { output, redact } = require('../../../utils/structured-logging-value-helper')
const { keysToSnakeCase } = require('../../../utils/key-camelizer')
const userIpAddress = require('../../../utils/user-ip-address')
const humps = require('humps')
const lodash = require('lodash')

const logSelectedPayloadProperties = req => {
Expand Down Expand Up @@ -95,7 +95,8 @@ module.exports = (req, paymentProvider) => {
ip_address: userIpAddress(req)
}

const paymentData = humps.decamelizeKeys(JSON.parse(payload.paymentResponse.details.paymentMethodData.tokenizationData.token))
const paymentData = keysToSnakeCase(JSON.parse(payload.paymentResponse.details.paymentMethodData.tokenizationData.token))

delete payload.paymentResponse.details.paymentMethodData

if (paymentProvider === 'stripe') {
Expand Down
6 changes: 3 additions & 3 deletions app/services/normalise-charge.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
'use strict'

// NPM dependencies
const humps = require('humps')
const lodash = require('lodash')

// Local dependencies
const countries = require('../services/countries')
const normaliseCards = require('../services/normalise-cards')
const userIpAddress = require('../utils/user-ip-address')
const { keysToCamelCase } = require('../utils/key-camelizer')

module.exports = (function () {
const charge = function (charge, chargeId) {
Expand Down Expand Up @@ -92,7 +92,7 @@ module.exports = (function () {

const _normaliseConfirmationDetails = function (cardDetails) {
cardDetails.cardNumber = '●●●●●●●●●●●●' + cardDetails.last_digits_card_number
const normalisedDetails = humps.camelizeKeys(cardDetails)
const normalisedDetails = keysToCamelCase(cardDetails)
delete normalisedDetails.lastDigitsCardNumber
if (cardDetails.billing_address) {
normalisedDetails.billingAddress = _normaliseAddress(cardDetails.billing_address)
Expand Down Expand Up @@ -124,7 +124,7 @@ module.exports = (function () {
}

const _normaliseGatewayAccountDetails = function (accountDetails) {
const gatewayAccountDetails = humps.camelizeKeys(accountDetails)
const gatewayAccountDetails = keysToCamelCase(accountDetails)
gatewayAccountDetails.cardTypes = normaliseCards(gatewayAccountDetails.cardTypes)
return gatewayAccountDetails
}
Expand Down
53 changes: 53 additions & 0 deletions app/utils/key-camelizer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
'use strict'

function convertToCamelCase (objectKey) {
const camelizedString = objectKey.replace(/[-_\s]+(.)?/g, (match, chr) => chr ? chr.toUpperCase() : '')
return camelizedString.substr(0, 1).toLowerCase() + camelizedString.substr(1)
}

function convertToSnakeCase (objectKey, separator = '_', split = /(?=[A-Z])/) {
return objectKey.split(split).join(separator).toLowerCase()
}

function keysToCamelCase (obj) {
if (Array.isArray(obj)) {
return obj.map(item => keysToCamelCase(item))
} else if (typeof obj === 'object') {
return Object.keys(obj).reduce((result, key) => {
if (obj[key] === null) {
result[convertToCamelCase(key)] = null
} else if (typeof obj[key] === 'object') {
result[convertToCamelCase(key)] = keysToCamelCase(obj[key])
} else {
result[convertToCamelCase(key)] = obj[key]
}
return result
}, {})
} else {
return obj
}
}

function keysToSnakeCase (obj) {
if (Array.isArray(obj)) {
return obj.map(item => keysToSnakeCase(item))
} else if (typeof obj === 'object') {
return Object.keys(obj).reduce((result, key) => {
if (obj[key] === null) {
result[convertToSnakeCase(key)] = null
} else if (typeof obj[key] === 'object') {
result[convertToSnakeCase(key)] = keysToSnakeCase(obj[key])
} else {
result[convertToSnakeCase(key)] = obj[key]
}
return result
}, {})
} else {
return obj
}
};

module.exports = {
keysToCamelCase,
keysToSnakeCase
}
11 changes: 0 additions & 11 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@
"gaap-analytics": "^3.1.0",
"govuk-frontend": "^4.7.0",
"helmet": "3.23.3",
"humps": "^2.0.1",
"i18n": "0.15.x",
"lodash": "4.17.x",
"mailcheck": "^1.1.1",
Expand Down
158 changes: 158 additions & 0 deletions test/utils/key-camelizer.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
'use strict'

const expect = require('chai').expect
const { keysToCamelCase, keysToSnakeCase } = require('../../app/utils/key-camelizer')

const simpleSnakeObj = {
attr_one: 'foo',
attr_two: 'bar'
}

const simpleCamelObj = {
attrOne: 'foo',
attrTwo: 'bar'
}

const simplePascalObj = {
AttrOne: 'foo',
AttrTwo: 'bar'
}

const complexSnakeObj = {
attr_one: 'foo',
attr_two: {
nested_attr1: 'bar'
},
attr_three: {
nested_attr2: {
nested_attr3: [{
nested_in_array1: 'baz'
}, {
nested_in_array2: 'hello'
}, {
nested_in_array3: ['world', 'boo']
}]
}
}
}

const complexCamelObj = {
attrOne: 'foo',
attrTwo: {
nestedAttr1: 'bar'
},
attrThree: {
nestedAttr2: {
nestedAttr3: [{
nestedInArray1: 'baz'
}, {
nestedInArray2: 'hello'
}, {
nestedInArray3: ['world', 'boo']
}]
}
}
}

const complexPascalObj = {
AttrOne: 'foo',
AttrTwo: {
NestedAttr1: 'bar'
},
AttrThree: {
NestedAttr2: {
NestedAttr3: [{
NestedInArray1: 'baz'
}, {
NestedInArray2: 'hello'
}, {
NestedInArray3: ['world', 'boo']
}]
}
}
}

const complexCustomObj = {
'attr-one': 'foo',
'attr-two': {
'nested-attr1': 'bar'
},
'attr-three': {
'nested-attr2': {
'nested-attr3': [{
'nested-in-array1': 'baz'
}, {
'nested-in-array2': 'hello'
}, {
'nested-in-array3': ['world', 'boo']
}]
}
}
}

const nestedNullObj = {
attr_one: 'foo',
attr_two: 'bar',
attr_three: {
nested_obj: null
}
}

const camelizedNestedNullObj = {
attrOne: 'foo',
attrTwo: 'bar',
attrThree: {
nestedObj: null
}
}

const objectTests = [
{ nonCamelCase: simpleSnakeObj, camelizedCase: simpleCamelObj, objectName: 'simple snake object keys' },
{ nonCamelCase: simplePascalObj, camelizedCase: simpleCamelObj, objectName: 'simple pascal object keys' },
{ nonCamelCase: complexSnakeObj, camelizedCase: complexCamelObj, objectName: 'complex snake object keys' },
{ nonCamelCase: complexPascalObj, camelizedCase: complexCamelObj, objectName: 'complex pascal object keys' },
{ nonCamelCase: complexCustomObj, camelizedCase: complexCamelObj, objectName: 'complex custom object keys' },
{ nonCamelCase: nestedNullObj, camelizedCase: camelizedNestedNullObj, objectName: 'simple snake object with nested null property' }
]

const primitiveTypeTests = [
{ primitiveType: 34, objectName: 'number' },
{ primitiveType: 'Test string', objectName: 'string' },
{ primitiveType: true, objectName: 'boolean' }
]

const objectsForDecamelizingTests = objectTests.filter(obj => obj.objectName.includes('snake'))

describe('keysToCamelCase', () => {
objectTests.forEach(test => {
const { nonCamelCase, camelizedCase, objectName } = test
it(`converts a ${objectName} to camel case`, () => {
const result = keysToCamelCase(nonCamelCase)
expect(result).to.deep.equal(camelizedCase)
})
})
primitiveTypeTests.forEach(test => {
const { primitiveType, objectName } = test
it(`does not convert a ${objectName} to camel case and returns an unchanged argument`, () => {
const result = keysToCamelCase(primitiveType)
expect(result).to.deep.equal(primitiveType)
})
})
})

describe('keysToSnakeCase', () => {
objectsForDecamelizingTests.forEach(test => {
const { nonCamelCase, camelizedCase, objectName } = test
it(`converts a ${objectName} to snake case`, () => {
const result = keysToSnakeCase(camelizedCase)
expect(result).to.deep.equal(nonCamelCase)
})
})
primitiveTypeTests.forEach(test => {
const { primitiveType, objectName } = test
it(`does not convert a ${objectName} to camel case and returns and returns an unchanged argument`, () => {
const result = keysToSnakeCase(primitiveType)
expect(result).to.deep.equal(primitiveType)
})
})
})

0 comments on commit 23231b3

Please sign in to comment.