Skip to content

Commit

Permalink
Codemod for webhook verifier option renaming (#4675)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tobbe authored Mar 7, 2022
1 parent 4261bbe commit 11fbb75
Show file tree
Hide file tree
Showing 6 changed files with 227 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Rename `timestamp` webhook verifier option

This codemod renames the `timestamp` webhook verifier option to `currentTimestampOverride`

The first thing it does is find all files that needs to be updated. We need to
look through all functions in the api/functions directory. In those functions
we're looking for calls to the `verifyEvent`, `verifySignature` or
`signPayload` functions.

A call can look like this:

```js
verifyEvent('timestampSchemeVerifier', {
event,
secret: process.env.STRIPE_WEBHOOK_SECRET,
options,
})
```

Finally we need to find that `options` object and rename `timestamp` (if it
exists) to `currentTimestampOverride`
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { verifyEvent, signPayload } from '@redwoodjs/api/webhooks'

export const handler = async (event, _context) => {
signPayload('timestampSchemeVerifier', {
event,
options: {
timestamp: Date.now(),
},
})

verifyEvent('timestampSchemeVerifier', {
event,
options: {
timestamp: Date.now() - 60 * 1000, // one minute ago
},
})

const options = {
timestamp: Date.now(),
}

verifyEvent('timestampSchemeVerifier', {
event,
options,
})

const verifierOptions = {
timestamp: Date.now(),
}

verifyEvent('timestampSchemeVerifier', {
event,
options: verifierOptions,
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {
verifyEvent,
signPayload,
} from '@redwoodjs/api/webhooks'

export const handler = async (event, _context) => {
signPayload('timestampSchemeVerifier', {
event,
options: {
currentTimestampOverride: Date.now()
}
})

verifyEvent('timestampSchemeVerifier', {
event,
options: {
currentTimestampOverride: Date.now() - 60*1000 // one minute ago
}
})

const options = {
currentTimestampOverride: Date.now(),
}

verifyEvent('timestampSchemeVerifier', {
event,
options,
})

const verifierOptions = {
currentTimestampOverride: Date.now(),
}

verifyEvent('timestampSchemeVerifier', {
event,
options: verifierOptions,
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
describe('Rename verifier timestamp option', () => {
it('Modifies simple Function', async () => {
await matchTransformSnapshot('renameVerifierTimestamp', 'simple')
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import type { FileInfo, API, Identifier, ASTNode, ASTPath } from 'jscodeshift'
import core from 'jscodeshift'

function renameTimestamp(
j: core.JSCodeshift,
optionsObject: ASTNode | ASTNode[] | ASTPath | ASTPath[]
) {
j(optionsObject)
.find(j.ObjectProperty, { key: { name: 'timestamp' } })
.replaceWith((objectProperty) => {
const currentTimestampOverride = j.objectProperty.from({
key: j.identifier('currentTimestampOverride'),
value: objectProperty.value.value,
// @ts-expect-error - trailingComments
comments: objectProperty.value.trailingComments || null,
})
return currentTimestampOverride
})
}

export default function transform(file: FileInfo, api: API) {
const j = api.jscodeshift
const ast = j(file.source)

ast
.find(j.CallExpression, (callExpression) => {
const calleeName = (callExpression.callee as Identifier).name

// Find all calls to
// `signPayload('timestampSchemeVerifier', ...)`
// `verifyEvent('timestampSchemeVerifier', ...)`
// `verifySignature('timestampSchemeVerifier', ...)`
return (
(calleeName === 'signPayload' ||
calleeName === 'verifyEvent' ||
calleeName === 'verifySignature') &&
callExpression.arguments[0]?.type === 'StringLiteral' &&
callExpression.arguments[0]?.value === 'timestampSchemeVerifier'
)
})
.forEach(({ node: callExpression }) => {
j(callExpression)
// Find all object properties called `options`
.find(j.ObjectProperty, { key: { name: 'options' } })
.forEach(({ value: options }) => {
// This codemod supports inline options object, like:
//
// verifyEvent('timestampSchemeVerifier', {
// event,
// options: {
// timestamp: Date.now() - 60 * 1000, // one minute ago
// },
// })
//
// or when the options object is declared elsewhere, like:
//
// const verifierOptions = {
// timestamp: Date.now(),
// }
//
// verifyEvent('timestampSchemeVerifier', {
// event,
// options: verifierOptions,
// })

if (j.ObjectExpression.check(options.value)) {
// An inline options object is an ObjectExpression
renameTimestamp(j, options.value)
} else if (j.Identifier.check(options.value)) {
// An options object referenced by name is an Identifier.
// Identifiers have a `name`
ast.findVariableDeclarators(options.value.name).forEach((n) => {
renameTimestamp(j, n.node)
})
}
})
})

return ast.toSource({ trailingComma: true, quote: 'single' })
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import path from 'path'

import fg from 'fast-glob'
import task from 'tasuku'

import getRWPaths from '../../../lib/getRWPaths'
import runTransform from '../../../lib/runTransform'

export const command = 'rename-verifier-timestamp'
export const description =
'(v0.47.x->v0.48.x) Renames the timestamp webhook verifier option'

/**
* The functions dir can look like like...
*
* functions
* ├── graphql.js
* ├── healthz.js
* ├── jsonproduct.js
* ├── payment.js
* ├── paysonCallback.js
* ├── prisma.js
* ├── shipping
* │ ├── shipping.scenarios.ts
* │ ├── shipping.test.ts
* │ └── shipping.ts
* ├── snipcartWebhooks.js
* ├── swishCallback.js
* └── swishCheckout.js
*/
export const handler = () => {
task(
'Rename timestamp to currentTimestampOverride',
async ({ setError }: task.TaskInnerApi) => {
try {
await runTransform({
transformPath: path.join(__dirname, 'renameVerifierTimestamp.js'),
targetPaths: fg.sync('/**/*.{js,ts}', {
cwd: getRWPaths().api.functions,
absolute: true,
}),
})
} catch (e: any) {
setError('Failed to codemod your project \n' + e?.message)
}
}
)
}

0 comments on commit 11fbb75

Please sign in to comment.