piper-utils 1.1.67 → 1.1.69
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/main.js +61 -50
- package/bin/main.js.map +1 -1
- package/package.json +1 -1
- package/src/audit/audit.js +14 -0
- package/src/audit/audit.test.js +33 -4
- package/src/database/dbUtils/partnerAccess/accessScope.js +99 -99
- package/src/database/dbUtils/partnerAccess/accessScope.test.js +287 -0
- package/src/database/dbUtils/partnerAccess/createAccessHelpers.js +38 -38
- package/src/requestResponse/requestResponse.js +6 -1
package/bin/main.js
CHANGED
|
@@ -95,51 +95,7 @@ async function assertCanWriteBookBusiness(access,businessId,{getPartnerById}={})
|
|
|
95
95
|
*/
|
|
96
96
|
async function assertCanWriteOwnBusiness(access,businessId,{getPartnerById}={}){if(!access)throw _errorCodes.errorList.unauthorized;if("global"===access.level)return;if("partner"===access.level){if(await(0,_accessContext.ensurePartnerScope)(access,{getPartnerById}),!access.partnerBusinessId)throw{..._errorCodes.errorList.partnerNotConfigured};if(businessId!==access.partnerBusinessId)throw _errorCodes.errorList.unauthorized;return}throw _errorCodes.errorList.unauthorized},exports.stampOwnBusinessId=async function stampOwnBusinessId(access,body,{getPartnerById}={}){if(!access||"partner"!==access.level)return;if(body.businessId)return;await(0,_accessContext.ensurePartnerScope)(access,{getPartnerById}),body.businessId=access.partnerBusinessId}
|
|
97
97
|
/***/;var _lodash=function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}(__webpack_require__(825)),_accessContext=__webpack_require__(62),_errorCodes=__webpack_require__(953)},
|
|
98
|
-
/***/179(__unused_webpack_module,exports,__webpack_require__){Object.defineProperty(exports,"__esModule",{value:!0}),exports.attachAudit=
|
|
99
|
-
/**
|
|
100
|
-
* Define a sibling `<ModelName>Audit` table for the given Sequelize model and
|
|
101
|
-
* wire the four lifecycle hooks (`afterCreate`, `afterUpsert`, `afterUpdate`,
|
|
102
|
-
* `afterDestroy`) that record changes into it.
|
|
103
|
-
*
|
|
104
|
-
* Call this **once per model at boot time** from inside the model's
|
|
105
|
-
* `dbSetup()` function. It is idempotent — calling it again for a model that
|
|
106
|
-
* already has an audit table attached is a no-op.
|
|
107
|
-
*
|
|
108
|
-
* The audit table has no foreign key into any user table; the username of the
|
|
109
|
-
* person who made the change is stamped onto each row from the JWT (set by
|
|
110
|
-
* {@link bindAuditRequest}). This keeps the audit util portable across domain
|
|
111
|
-
* services that own different user models.
|
|
112
|
-
*
|
|
113
|
-
* @example
|
|
114
|
-
* // src/customer/customer.js
|
|
115
|
-
* Customer.dbSetup = async function () {
|
|
116
|
-
* await attachAudit(Customer);
|
|
117
|
-
* };
|
|
118
|
-
*
|
|
119
|
-
* @param {import('sequelize').ModelStatic<any>} model the Sequelize model to audit
|
|
120
|
-
* @returns {Promise<void>} resolves when the audit table's `sync()` completes
|
|
121
|
-
* @see {@link bindAuditRequest} - per-request setup that turns audit on
|
|
122
|
-
* @see {@link getAuditModel} - retrieve the audit model from the parent
|
|
123
|
-
* @see {@link getAuditFilter} - build a filter for an audit history query
|
|
124
|
-
*/
|
|
125
|
-
async function attachAudit(model){if(auditModels[model.name])return;const auditModel=model.sequelize.define(model.name+"Audit",function getAuditSchema(model){const DataTypes=model.sequelize.Sequelize;return{field:{type:DataTypes.STRING,allowNull:!1},type:DataTypes.STRING,valueOld:DataTypes.STRING,valueNew:DataTypes.STRING,changedByUser:{type:DataTypes.STRING,allowNull:!1},businessId:{type:DataTypes.STRING,allowNull:!1}}}
|
|
126
|
-
/**
|
|
127
|
-
* Internal hook factory. Returns the async function that Sequelize invokes on
|
|
128
|
-
* each create/upsert/update/destroy. Reads its per-request state from
|
|
129
|
-
* {@link modelOptions}; emits zero rows when audit is disabled for the model.
|
|
130
|
-
*
|
|
131
|
-
* Sensitive fields inside JSON values (token, password, secret, key,
|
|
132
|
-
* credential, auth, securityCode, cvv, pin, ssn) are masked as
|
|
133
|
-
* `***************` before being persisted. String values longer than 255
|
|
134
|
-
* characters are truncated.
|
|
135
|
-
*
|
|
136
|
-
* Exported only so tests can drive the hook directly. Application code should
|
|
137
|
-
* never call this — use {@link attachAudit}.
|
|
138
|
-
*
|
|
139
|
-
* @param {string} modelName name of the parent Sequelize model
|
|
140
|
-
* @returns {(modelInfo: any, options: { type?: string, transaction?: import('sequelize').Transaction }) => Promise<void>}
|
|
141
|
-
* the hook function Sequelize will call on each lifecycle event
|
|
142
|
-
*/(model),{updatedAt:!1,freezeTableName:!0});return auditModel.belongsTo(model,{foreignKey:{allowNull:!0},onDelete:"SET NULL"}),auditModels[model.name]=auditModel,model.addHook("afterCreate",auditMe(model.name)),model.addHook("afterUpsert",auditMe(model.name)),model.addHook("afterUpdate",auditMe(model.name)),model.addHook("afterDestroy",auditMe(model.name)),auditModel.sync()}
|
|
98
|
+
/***/179(__unused_webpack_module,exports,__webpack_require__){Object.defineProperty(exports,"__esModule",{value:!0}),exports.attachAudit=attachAudit,exports.auditMe=auditMe,exports.auditModels=void 0,exports.bindAuditRequest=
|
|
143
99
|
/**
|
|
144
100
|
* Bind per-request audit context for one or more models. Call this **once at
|
|
145
101
|
* the top of each route handler** that mutates an audited model.
|
|
@@ -177,7 +133,14 @@ async function attachAudit(model){if(auditModels[model.name])return;const auditM
|
|
|
177
133
|
* @see {@link bindAuditRequestForUser} - the equivalent for non-JWT contexts
|
|
178
134
|
* (webhooks, integration crons, Cognito triggers)
|
|
179
135
|
* @see {@link attachAudit} - one-time setup that creates the audit table
|
|
180
|
-
|
|
136
|
+
*/
|
|
137
|
+
function bindAuditRequest(event,businessId,models){const user=(0,_requestResponse.getCurrentUser)(event),auditEnabled=!!((0,_accessRightsUtils.getCompanySettings)(event)||{}).auditEnabled;_lodash.default.forEach(models,model=>{
|
|
138
|
+
// Lazily ensure the audit table model + hooks are wired in this Lambda
|
|
139
|
+
// process. `attachAudit` is idempotent — first call registers, subsequent
|
|
140
|
+
// calls are no-ops. The returned sync() promise is intentionally not
|
|
141
|
+
// awaited; the table is expected to already exist from the migration
|
|
142
|
+
// Lambda, and the synchronous registration is what the request needs.
|
|
143
|
+
const attachPromise=attachAudit(model);attachPromise&&"function"==typeof attachPromise.catch&&attachPromise.catch(err=>console.error("attachAudit sync failed for",model.name,err)),modelOptions[model.name]={user,businessId,auditEnabled}})}
|
|
181
144
|
/**
|
|
182
145
|
* Bind per-request audit context for **system-driven flows** that do not carry
|
|
183
146
|
* an API Gateway JWT — for example, payment-gateway webhooks, integration
|
|
@@ -211,7 +174,9 @@ async function attachAudit(model){if(auditModels[model.name])return;const auditM
|
|
|
211
174
|
* binds context but suppresses writes; defaults to `true`
|
|
212
175
|
* @returns {void}
|
|
213
176
|
* @see {@link bindAuditRequest} - the JWT-driven equivalent for API handlers
|
|
214
|
-
*/,exports.bindAuditRequestForUser=function bindAuditRequestForUser(user,businessId,models,opts={}){const auditEnabled=!1!==opts.auditEnabled;_lodash.default.forEach(models,model=>{
|
|
177
|
+
*/,exports.bindAuditRequestForUser=function bindAuditRequestForUser(user,businessId,models,opts={}){const auditEnabled=!1!==opts.auditEnabled;_lodash.default.forEach(models,model=>{
|
|
178
|
+
// See bindAuditRequest for why this is here.
|
|
179
|
+
const attachPromise=attachAudit(model);attachPromise&&"function"==typeof attachPromise.catch&&attachPromise.catch(err=>console.error("attachAudit sync failed for",model.name,err)),modelOptions[model.name]={user,businessId,auditEnabled}})}
|
|
215
180
|
/**
|
|
216
181
|
* Get the `<ModelName>Audit` Sequelize model that {@link attachAudit} created
|
|
217
182
|
* for a parent model. Returns `undefined` if audit was never attached.
|
|
@@ -346,7 +311,51 @@ function getAuditFilter(modelToFilter,id,options){const filter={where:{businessI
|
|
|
346
311
|
* Exported for tests; do not mutate from app code.
|
|
347
312
|
*
|
|
348
313
|
* @type {Object<string, { user: { username: string, id?: number }, businessId: string|number, auditEnabled: boolean }>}
|
|
349
|
-
*/
|
|
314
|
+
*/
|
|
315
|
+
/**
|
|
316
|
+
* Internal hook factory. Returns the async function that Sequelize invokes on
|
|
317
|
+
* each create/upsert/update/destroy. Reads its per-request state from
|
|
318
|
+
* {@link modelOptions}; emits zero rows when audit is disabled for the model.
|
|
319
|
+
*
|
|
320
|
+
* Sensitive fields inside JSON values (token, password, secret, key,
|
|
321
|
+
* credential, auth, securityCode, cvv, pin, ssn) are masked as
|
|
322
|
+
* `***************` before being persisted. String values longer than 255
|
|
323
|
+
* characters are truncated.
|
|
324
|
+
*
|
|
325
|
+
* Exported only so tests can drive the hook directly. Application code should
|
|
326
|
+
* never call this — use {@link attachAudit}.
|
|
327
|
+
*
|
|
328
|
+
* @param {string} modelName name of the parent Sequelize model
|
|
329
|
+
* @returns {(modelInfo: any, options: { type?: string, transaction?: import('sequelize').Transaction }) => Promise<void>}
|
|
330
|
+
* the hook function Sequelize will call on each lifecycle event
|
|
331
|
+
*/
|
|
332
|
+
function auditMe(modelName){return async(modelInfo,options)=>{_lodash.default.isArray(modelInfo)&&(modelInfo=modelInfo[0]);const opts=modelOptions[modelName];if(!opts||!opts.auditEnabled)return;const auditModel=auditModels[modelName],writeAuditRow=async(field,type,valueOld,valueNew)=>{const data={field,type,valueOld:String(valueOld).substring(0,255),valueNew:String(valueNew).substring(0,255),businessId:opts.businessId,changedByUser:opts.user.username};data[modelName+"Id"]=modelInfo.dataValues.id;const newOptions={};return options.transaction&&(newOptions.transaction=options.transaction),auditModel.create(data,newOptions)},checkAudit=async(field,type="INITIAL",valueOld,valueNew)=>{if(!_lodash.default.isEqual(valueOld,valueNew)){if(_lodash.default.isString(valueOld)&&_lodash.default.isString(valueNew)&&(type="INSERT"),_lodash.default.isArray(valueOld)||_lodash.default.isArray(valueNew)){let oldString=JSON.stringify(valueOld||[]),newString=JSON.stringify(valueNew||[]);return oldString=oldString.replace(SENSITIVE_FIELDS_PATTERN,'"$1":"***************"').substring(0,255),newString=newString.replace(SENSITIVE_FIELDS_PATTERN,'"$1":"***************"').substring(0,255),writeAuditRow(field,"UPDATE",oldString,newString)}return _lodash.default.isObject(valueOld)||_lodash.default.isObject(valueNew)?(_lodash.default.forEach(valueOld,(valueSub,keySub)=>{checkAudit(field+" "+keySub,"DELETED",valueSub||"",(valueNew||{})[keySub]||"")}),void _lodash.default.forEach(valueNew,(valueSub,keySub)=>{checkAudit(field+" "+keySub,"INSERT",(valueOld||{})[keySub]||"",valueSub||"")})):writeAuditRow(field,type,valueOld,valueNew)}};return _bluebird.default.map([...modelInfo._changed],async field=>{const valueOld=modelInfo._previousDataValues[field]||"",valueNew=modelInfo.dataValues[field]||"";await checkAudit(field,options.type,valueOld,valueNew)})}}
|
|
333
|
+
/**
|
|
334
|
+
* Define a sibling `<ModelName>Audit` table for the given Sequelize model and
|
|
335
|
+
* wire the four lifecycle hooks (`afterCreate`, `afterUpsert`, `afterUpdate`,
|
|
336
|
+
* `afterDestroy`) that record changes into it.
|
|
337
|
+
*
|
|
338
|
+
* Call this **once per model at boot time** from inside the model's
|
|
339
|
+
* `dbSetup()` function. It is idempotent — calling it again for a model that
|
|
340
|
+
* already has an audit table attached is a no-op.
|
|
341
|
+
*
|
|
342
|
+
* The audit table has no foreign key into any user table; the username of the
|
|
343
|
+
* person who made the change is stamped onto each row from the JWT (set by
|
|
344
|
+
* {@link bindAuditRequest}). This keeps the audit util portable across domain
|
|
345
|
+
* services that own different user models.
|
|
346
|
+
*
|
|
347
|
+
* @example
|
|
348
|
+
* // src/customer/customer.js
|
|
349
|
+
* Customer.dbSetup = async function () {
|
|
350
|
+
* await attachAudit(Customer);
|
|
351
|
+
* };
|
|
352
|
+
*
|
|
353
|
+
* @param {import('sequelize').ModelStatic<any>} model the Sequelize model to audit
|
|
354
|
+
* @returns {Promise<void>} resolves when the audit table's `sync()` completes
|
|
355
|
+
* @see {@link bindAuditRequest} - per-request setup that turns audit on
|
|
356
|
+
* @see {@link getAuditModel} - retrieve the audit model from the parent
|
|
357
|
+
* @see {@link getAuditFilter} - build a filter for an audit history query
|
|
358
|
+
*/async function attachAudit(model){if(auditModels[model.name])return;const auditModel=model.sequelize.define(model.name+"Audit",function getAuditSchema(model){const DataTypes=model.sequelize.Sequelize;return{field:{type:DataTypes.STRING,allowNull:!1},type:DataTypes.STRING,valueOld:DataTypes.STRING,valueNew:DataTypes.STRING,changedByUser:{type:DataTypes.STRING,allowNull:!1},businessId:{type:DataTypes.STRING,allowNull:!1}}}(model),{updatedAt:!1,freezeTableName:!0});return auditModel.belongsTo(model,{foreignKey:{allowNull:!0},onDelete:"SET NULL"}),auditModels[model.name]=auditModel,model.addHook("afterCreate",auditMe(model.name)),model.addHook("afterUpsert",auditMe(model.name)),model.addHook("afterUpdate",auditMe(model.name)),model.addHook("afterDestroy",auditMe(model.name)),auditModel.sync()}},
|
|
350
359
|
/***/183(__unused_webpack_module,exports,__webpack_require__){Object.defineProperty(exports,"__esModule",{value:!0}),exports.watchBucket=
|
|
351
360
|
/**
|
|
352
361
|
* bucket watcher watches a s3 bucket and publishes events to a sns topic, this allows you to process files in s3 with a some transformer function
|
|
@@ -719,7 +728,9 @@ return errorBody.message=dynamooseError.trim(),errorBody}
|
|
|
719
728
|
* @param options {Object} an options object
|
|
720
729
|
* @returns {{statusCode:number, headers:object, body:string}} a response object
|
|
721
730
|
*/
|
|
722
|
-
function failure(body={},options){let cleanedErrorBody;_lodash.default.get(options,"dbClose",_lodash.default.noop)(),("LOG_ALL"===process.env.UTIL_LOG||"test"===process.env.BUILD_ENV)&&_lodash.default.isObject(body)&&console.error("-------\x3eALL UTIL ERROR:",JSON.stringify(body,null,2));cleanedErrorBody=body,_lodash.default.isUndefined(body.details)?(_lodash.default.isUndefined(body.errorCode)||_lodash.default.isUndefined(body.statusCode))&&(cleanedErrorBody=detectSequelizeError(body)):cleanedErrorBody=detectJoyError(body);if(_lodash.default.get(body,"response.data.message")){const err=_lodash.default.get(body,"response.data.message");_lodash.default.isObject(body)&&("LOG_ALL"!==process.env.UTIL_LOG&&"test"!==process.env.BUILD_ENV||console.error("-------\x3eMSG UTIL ERROR:",JSON.stringify(err,null,2)))}const newBody=_lodash.default.merge({statusCode:500,errorCode:"5XX",message:"INTERNAL UTIL ERROR"},cleanedErrorBody);
|
|
731
|
+
function failure(body={},options){let cleanedErrorBody;_lodash.default.get(options,"dbClose",_lodash.default.noop)(),("LOG_ALL"===process.env.UTIL_LOG||"test"===process.env.BUILD_ENV)&&_lodash.default.isObject(body)&&console.error("-------\x3eALL UTIL ERROR:",JSON.stringify(body,null,2));cleanedErrorBody=body,_lodash.default.isUndefined(body.details)?(_lodash.default.isUndefined(body.errorCode)||_lodash.default.isUndefined(body.statusCode))&&(cleanedErrorBody=detectSequelizeError(body)):cleanedErrorBody=detectJoyError(body);if(_lodash.default.get(body,"response.data.message")){const err=_lodash.default.get(body,"response.data.message");_lodash.default.isObject(body)&&("LOG_ALL"!==process.env.UTIL_LOG&&"test"!==process.env.BUILD_ENV||console.error("-------\x3eMSG UTIL ERROR:",JSON.stringify(err,null,2)))}const newBody=_lodash.default.merge({statusCode:500,errorCode:"5XX",message:"INTERNAL UTIL ERROR"},cleanedErrorBody);"LOG_ALL"!==process.env.UTIL_LOG&&"test"!==process.env.BUILD_ENV&&newBody.statusCode>=500&&
|
|
732
|
+
// Log the raw error object so thrown Error stacks survive (JSON.stringify drops them)
|
|
733
|
+
console.error("-------\x3eUTIL ERROR:",newBody.statusCode,_lodash.default.get(body,"message",""),body);return buildResponse(newBody.statusCode,newBody)}
|
|
723
734
|
/**
|
|
724
735
|
* Create a failure response object
|
|
725
736
|
*
|
|
@@ -773,7 +784,7 @@ function success(body,options){return _lodash.default.get(options,"dbClose",_lod
|
|
|
773
784
|
/**
|
|
774
785
|
* @param {string} html
|
|
775
786
|
* @param {{dbClose:function|undefined}} options
|
|
776
|
-
*/,exports.successHtml=function successHtml(html,options){return _lodash.default.get(options,"dbClose",_lodash.default.noop)(),{statusCode:200,headers:{"Content-Type":"text/html","Access-Control-Allow-Origin":"*","Access-Control-Allow-Credentials":!0,...htmlSecurityHeaders},body:html}};var _lodash=function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}(__webpack_require__(825)),_errorCodes=__webpack_require__(953);const securityHeaders={"Strict-Transport-Security":"max-age=31536000; includeSubDomains","X-Content-Type-Options":"nosniff","X-Frame-Options":"DENY","Referrer-Policy":"strict-origin-when-cross-origin","Permissions-Policy":"camera=(), microphone=(), geolocation=()","Cache-Control":"no-store","Content-Security-Policy":"default-src 'none'; frame-ancestors 'none'"},htmlSecurityHeaders={"Strict-Transport-Security":"max-age=31536000; includeSubDomains","X-Content-Type-Options":"nosniff","Referrer-Policy":"strict-origin-when-cross-origin","Permissions-Policy":"camera=(), microphone=(), geolocation=(), payment=*","Cache-Control":"no-store","Content-Security-Policy":["default-src 'self'","script-src 'self' 'unsafe-inline' https://browser.sentry-cdn.com https://test-htp.tokenex.com https://htp.tokenex.com https://sandbox.nmi.com https://secure.nmi.com https://applepay.cdn-apple.com","style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://sandbox.nmi.com https://secure.nmi.com","font-src 'self' https://fonts.gstatic.com","frame-src https:","connect-src 'self' https://*.tokenex.com https://*.sentry.io https://*.nmi.com https://*.apple.com","img-src 'self' data:","frame-ancestors *"].join("; ")};function buildResponse(statusCode,body){return{statusCode,headers:{"Access-Control-Allow-Origin":"*","Access-Control-Allow-Credentials":!0,...securityHeaders},body:JSON.stringify(body)}}function detectSequelizeError(body){const errorBody={},errorName=_lodash.default.get(body,"name","");if("SequelizeForeignKeyConstraintError"===errorName){const detail=_lodash.default.get(body,"parent.detail")||_lodash.default.get(body,"original.detail")||"",notPresent=detail.match(/Key \((\w+)\)=\((.+?)\) is not present in table "(\w+)"/),stillReferenced=detail.match(/Key \((\w+)\)=\((.+?)\) is still referenced from table "(\w+)"/);return errorBody.message=notPresent?"The referenced "+notPresent[3]+" does not exist ("+notPresent[1]+": "+notPresent[2]+")":stillReferenced?"This item cannot be removed because it is referenced by "+stillReferenced[3]:"A referenced item does not exist or is still in use",errorBody.statusCode=409,errorBody.errorCode="4090",errorBody}if("SequelizeUniqueConstraintError"===errorName){const fields=_lodash.default.get(body,"errors",[]).map(err=>_lodash.default.get(err,"path")).filter(Boolean);return errorBody.message=fields.length?"A record with this "+fields.join(", ")+" already exists":"A record with this value already exists",errorBody.statusCode=409,errorBody.errorCode="4091",errorBody}if("SequelizeValidationError"===errorName){const messages=_lodash.default.get(body,"errors",[]).map(err=>_lodash.default.get(err,"message")).filter(Boolean);return errorBody.message=messages.length?messages.join(", "):"Validation error",errorBody.statusCode=400,errorBody.errorCode="4001",errorBody}let sequelizeError=_lodash.default.get(body,"errors",[]).reduce((acc,err)=>acc=acc+" "+_lodash.default.get(err,"message"),"");const parentError=_lodash.default.get(body,"parent","");return sequelizeError=sequelizeError+_lodash.default.get(body,"original.detail","")+_lodash.default.get(body,"TypeError","")+parentError,errorBody.message=sequelizeError.trim(),errorBody}function detectJoyError(body){const errorBody={},joyError=_lodash.default.get(body,"details[0]",{}),v=(_lodash.default.get(body,"details[0].context.details")||[]).reduce((acc,contextItem)=>acc=acc+" "+contextItem?.message||"","")
|
|
787
|
+
*/,exports.successHtml=function successHtml(html,options){return _lodash.default.get(options,"dbClose",_lodash.default.noop)(),{statusCode:200,headers:{"Content-Type":"text/html","Access-Control-Allow-Origin":"*","Access-Control-Allow-Credentials":!0,...htmlSecurityHeaders},body:html}};var _lodash=function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}(__webpack_require__(825)),_errorCodes=__webpack_require__(953);const securityHeaders={"Strict-Transport-Security":"max-age=31536000; includeSubDomains","X-Content-Type-Options":"nosniff","X-Frame-Options":"DENY","Referrer-Policy":"strict-origin-when-cross-origin","Permissions-Policy":"camera=(), microphone=(), geolocation=()","Cache-Control":"no-store","Content-Security-Policy":"default-src 'none'; frame-ancestors 'none'"},htmlSecurityHeaders={"Strict-Transport-Security":"max-age=31536000; includeSubDomains","X-Content-Type-Options":"nosniff","Referrer-Policy":"strict-origin-when-cross-origin","Permissions-Policy":"camera=(), microphone=(), geolocation=(), payment=*","Cache-Control":"no-store","Content-Security-Policy":["default-src 'self'","script-src 'self' 'unsafe-inline' https://browser.sentry-cdn.com https://test-htp.tokenex.com https://htp.tokenex.com https://sandbox.nmi.com https://secure.nmi.com https://applepay.cdn-apple.com","style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://sandbox.nmi.com https://secure.nmi.com","font-src 'self' https://fonts.gstatic.com","frame-src https:","connect-src 'self' https://*.tokenex.com https://*.sentry.io https://*.nmi.com https://*.apple.com","img-src 'self' data:","frame-ancestors *"].join("; ")};function buildResponse(statusCode,body){return{statusCode,headers:{"Access-Control-Allow-Origin":"*","Access-Control-Allow-Credentials":!0,...securityHeaders},body:JSON.stringify(body)}}function detectSequelizeError(body){const errorBody={},errorName=_lodash.default.get(body,"name","");if("SequelizeForeignKeyConstraintError"===errorName){const detail=_lodash.default.get(body,"parent.detail")||_lodash.default.get(body,"original.detail")||"",notPresent=detail.match(/Key \((\w+)\)=\((.+?)\) is not present in table "(\w+)"/),stillReferenced=detail.match(/Key \((\w+)\)=\((.+?)\) is still referenced from table "(\w+)"/);return errorBody.message=notPresent?"The referenced "+notPresent[3]+" does not exist ("+notPresent[1]+": "+notPresent[2]+")":stillReferenced?"This item cannot be removed because it is referenced by "+stillReferenced[3]:"A referenced item does not exist or is still in use",errorBody.statusCode=409,errorBody.errorCode="4090",errorBody}if("SequelizeUniqueConstraintError"===errorName){const fields=_lodash.default.get(body,"errors",[]).map(err=>_lodash.default.get(err,"path")).filter(Boolean);return errorBody.message=fields.length?"A record with this "+fields.join(", ")+" already exists":"A record with this value already exists",errorBody.statusCode=409,errorBody.errorCode="4091",errorBody}if("SequelizeValidationError"===errorName){const messages=_lodash.default.get(body,"errors",[]).map(err=>_lodash.default.get(err,"message")).filter(Boolean);return errorBody.message=messages.length?messages.join(", "):"Validation error",errorBody.statusCode=400,errorBody.errorCode="4001",errorBody}let sequelizeError=_lodash.default.get(body,"errors",[]).reduce((acc,err)=>acc=acc+" "+_lodash.default.get(err,"message"),"");const parentError=_lodash.default.get(body,"parent","");return sequelizeError=sequelizeError+_lodash.default.get(body,"original.detail","")+_lodash.default.get(body,"TypeError","")+parentError,errorBody.message=sequelizeError.trim(),errorBody}function detectJoyError(body){const errorBody={},joyError=_lodash.default.get(body,"details[0]",{}),v=(_lodash.default.get(body,"details[0].context.details")||[]).reduce((acc,contextItem)=>acc=acc+" "+contextItem?.message||"",""),msg=(joyError?.message||"")+v;return msg&&(errorBody.message=_lodash.default.trim(msg)),errorBody.statusCode=400,errorBody.errorCode="4000",errorBody}},
|
|
777
788
|
/***/864(__unused_webpack_module,exports,__webpack_require__){Object.defineProperty(exports,"__esModule",{value:!0}),exports.loop=loop,exports.publishEvents=
|
|
778
789
|
/**
|
|
779
790
|
* Published SNS events at a steady pace for data in a particular bucket. Should be executed via a cloud watch cron job as a part of a micro service
|