Skip to content
This repository was archived by the owner on Nov 4, 2021. It is now read-only.

Fire webhooks from internal action matching results - #479

Merged
Twixes merged 20 commits into
masterfrom
hooks-firing
Jun 18, 2021
Merged

Twixes merged 20 commits into
masterfrom
hooks-firing

Conversation

@Twixes

@Twixes Twixes commented Jun 16, 2021

Copy link
Copy Markdown
Contributor

Changes

Resolves #459. Webhooks fired directly here instead of in Python with Celery.

Checklist

  • Updated Settings section in README.md, if settings are affected
  • Jest tests

@Twixes Twixes added the bump patch Bump patch version when this PR gets merged label Jun 17, 2021
@Twixes
Twixes requested review from neilkakkar and yakkomajuri June 18, 2021 09:32
@Twixes
Twixes marked this pull request as ready for review June 18, 2021 09:33
Comment thread src/worker/ingestion/hooks.ts
Comment thread src/worker/ingestion/hooks.ts Outdated
let markdown = ''

if (tokenParts[0] === 'user') {
if (tokenParts[1] == 'name') {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (tokenParts[1] == 'name') {
if (tokenParts[1] === 'name') {

and a few below, too! Python->JS conversion can get so annoying 😂

Comment thread src/worker/ingestion/hooks.ts Outdated
} else {
const propertyName = `$${tokenParts[1]}`
const property = event.properties?.[propertyName]
text = markdown = stringify(property)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure multiple assignment is equivalent in JS & Python 👀 : https://stackoverflow.com/questions/1758576/multiple-left-hand-assignment-with-javascript

But since text and markdown are already defined, this should be the same?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Rearranged to avoid weird multiple assigment

Comment thread src/worker/ingestion/hooks.ts Outdated
return [messageText, messageMarkdown]
}

export class HookCannon {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is Cannon supposed to imply here? (a bit lost with the naming 😅 )

Is it because it finds and fires hooks?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Literally yes, it definitely may be too "fun" LOL. Feel free to propose something clearer

@neilkakkar neilkakkar Jun 18, 2021

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As someone who called a thread-killer the Terminator, yes, I'm very happy with a hook cannon 😂 .

Although hookCannon.findAndFireHooks feels like it's a HookCannonAndLogistician.

To make things clearer, perhaps a HookManager? It seems to fit in well with our other managers, and performs a similar role to the others (although most of them fetch, while this one fires)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to, drum roll, HookCommander

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ten-Hut!

Comment thread src/worker/ingestion/hooks.ts

if (database === 'clickhouse') {
expect(queryCounter).toBe(11 + 14 /* event & prop definitions */)
expect(queryCounter).toBe(10 + 14 /* event & prop definitions */)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👀

which query doesn't happen anymore?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't do shouldSendWebhooks anymore 🎉

expect(organization!.name).toEqual('TEST ORG')
expect(hub.db.postgresQuery).toHaveBeenCalledTimes(0)

jest.spyOn(global.Date, 'now').mockImplementation(() => new Date('2020-02-27 11:00:36').getTime())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this testing the cache TTL ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this whole organization-manager thing is almost totally copied from TeamManager code, because it's very much alike

@neilkakkar neilkakkar left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of questions, but otherwise looks good to me!

@yakkomajuri yakkomajuri left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

didn't run it yet but mostly looks good - let me try it out

Comment thread src/types.ts Outdated
import { UUID } from './utils/utils'
import { ActionManager } from './worker/ingestion/action-manager'
import { ActionMatcher } from './worker/ingestion/action-matcher'
import { HookCannon } from './worker/ingestion/hooks'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:D

Comment thread src/utils/utils.ts
switch (typeof value) {
case 'string':
return value
case 'undefined':

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think JSON.stringify(undefined) returns "undefined" already

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It in fact returns undefined instead of "undefined", which caught me off guard for a bit 😅

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ha, damn

if (url.includes('discord.com')) {
return WebhookType.Discord
}
return WebhookType.Teams

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a good approach? Probably legacy so should be fine, but while this might not be harmful, I think I'd like some explicit Teams checking with an additional unknown/unspecified type, just for clarity.

I regularly use https://webhook.site/ for example

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, Teams is just the default behavior this way. I don't know what webhook.site accepts and I don't want to support it, so Teams format it is.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense yeah - webhook.site is just a webhook dump.

When I added this comment I didn't see what the type was being used for, but yeah, if it's just formatting the message/payload, that's perfectly fine

Comment thread src/worker/ingestion/hooks.ts Outdated
const actionName = stringify(action.name)
let actionMarkdown: string
if (webhookType === WebhookType.Slack) {
actionMarkdown = `<${siteUrl}/action/${action.id}|${action.name}>`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe use actionName instead of action.name here too? (and below)

Comment thread src/worker/ingestion/hooks.ts Outdated
}

export function getTokens(messageFormat: string): [string[], string] {
const matchedTokens = messageFormat.match(/(?<=\[)(.*?)(?=\])/g) || []

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: as you know, I always appreciate a regex match example in a comment

Comment thread src/worker/ingestion/hooks.ts

const timeout = timeoutGuard(`Still running "fetchOrganization". Timeout warning after 30 sec!`)
try {
const organizationQueryResult = await this.db.postgresQuery(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

genuine q: we have SQL queries spread in multiple areas of the codebase, db, sql, and individual queries in some files here and there. do we have any sort of convention for where they should be?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think for some more bespoke stuff (like TeamManager.updateEventNamesAndProperties), it doesn't make sense to make it common via DB, BUT thanks for bringing this line up – I updated it to use DB.fetchOrganization (and similarly in TeamManager with fetchTeam)

@Twixes
Twixes requested a review from yakkomajuri June 18, 2021 11:59
@yakkomajuri

Copy link
Copy Markdown
Contributor

Ran this locally. Action calculated fine the first time and sent the relevant webhooks.

Second time though the UI told me the action was recalculated but the events didn't update and I didn't get the webhooks.

Got some errors for posthog:

ConnectionResetError: [Errno 54] Connection reset by peer

Don't have time to investigate, could be a fluke - just putting it out here for context.

@Twixes

Twixes commented Jun 18, 2021

Copy link
Copy Markdown
Contributor Author

What do you mean by "the events didn't update and I didn't get the webhooks"? You shouldn't get webhooks on action recalculation with this, the way Django does resend webhooks on action recalculation is unintended behavior.

@yakkomajuri

Copy link
Copy Markdown
Contributor

Right yeah, ofc - webhooks should fire as events come in. I guess the correlation I saw was what got me back into the old thinking model. Either way, the action didn't recalculate a second time and I didn't get webhooks for all events that were sent in 🤔

@yakkomajuri

yakkomajuri commented Jun 18, 2021

Copy link
Copy Markdown
Contributor

I'd say feel free to ignore though if this works for you - I did indeed get some webhooks so the system is working

@Twixes

Twixes commented Jun 18, 2021

Copy link
Copy Markdown
Contributor Author

Hm, all OK for me, recalculation should definitely not be affected because that's Django and not touched by PostHog/posthog#4794. I think we should be safe to go ahead

@Twixes
Twixes merged commit f11a9f1 into master Jun 18, 2021
@Twixes
Twixes deleted the hooks-firing branch June 18, 2021 14:23
fuziontech pushed a commit to PostHog/posthog that referenced this pull request Oct 12, 2021
…ostHog/plugin-server#479)

* Fire webhooks from internal action matching results

* Add Zapier `postEventToRestHook`

* Remove offloading of hooks to Python via Celery

* Fire Zapier in the plugin server

* Port webhook formatting tests over

* Update queryCounter expectations

* Update queryCounter expectations again

* Fully test and fix webhook formatting

* Reorganize hook firing into HookCannon

* Remove duplicated test

* Test OrganizationManager

* Add end-to-end test for action matching saving

* Add (almost) end-to-end tests for firing hooks

* Run prettier

* Rearrange `text = markdown = foo`

* Fix `==`s

* Rename `HookCannon`

* Add TOKENS_REGEX comment

* Use `fetchOrganization` and `fetchTeam` from DB
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

bump patch Bump patch version when this PR gets merged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fire core webhooks and Zapier triggers from plugin server

3 participants