Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Add a generic webhook for sending event notifications #879

Open
wants to merge 19 commits into
base: main
Choose a base branch
from

Conversation

brian-lou
Copy link
Contributor

This PR adds a generic webhook: /event-notifier/v1 which can be used to send events and messages to Slack and DD (with support for Jira coming soon).

Copy link

codecov bot commented Oct 16, 2024

Codecov Report

Attention: Patch coverage is 91.80328% with 5 lines in your changes missing coverage. Please review.

Project coverage is 89.75%. Comparing base (9aaa714) to head (bc4ed32).
Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
src/webhooks/generic-notifier/generic-notifier.ts 90.19% 5 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #879      +/-   ##
==========================================
+ Coverage   89.43%   89.75%   +0.32%     
==========================================
  Files         121      124       +3     
  Lines        3701     3769      +68     
  Branches      741      753      +12     
==========================================
+ Hits         3310     3383      +73     
+ Misses        379      374       -5     
  Partials       12       12              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@brian-lou brian-lou changed the title Add a generic webhook for sending event notifications Feat: Add a generic webhook for sending event notifications Oct 17, 2024
Copy link

@fpacifici fpacifici left a comment

Choose a reason for hiding this comment

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

Some comments in line

export type GenericEvent = {
source: string;
timestamp: number;
service_name?: string; // Official service registry name if applicable

Choose a reason for hiding this comment

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

What are you going to use this for ? If I remember correctly the service registry was supposed to be used to route messages to the right channel, but the channel is also passed as a parameter.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, the idea was to use the service-registry if the service_name is supplied, but I'm not sure if everything that will use this in the future will be associated with a service. If we can make that guarantee, I can remove the channel parameter

Copy link

@fpacifici fpacifici Nov 7, 2024

Choose a reason for hiding this comment

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

If we can make that guarantee, I can remove the channel parameter
we cannot make this guarantee.

So the problem is the same as below. Please let's not have an api method with a lot of optional fields at the same level and rely on the user to know which combination are valid and which ones are not.

Instead, I would suggest one of these two so that making mistakes is harder:

  • Have more granular methods: one for slack with smart routing that goes to service and one where we specify the channel. Though we need to think through which methods we are going to build upfront.
  • Have one method but provide different structured types for the different types of notification: slack with direct routing, slack with service routing (service is relevant only for slack), jira, datadog, etc.

Comment on lines 39 to 48
channels: {
slack?: string[]; // list of Slack Channels
datadog?: string[]; // list of DD Monitors
jira?: string[]; // list of Jira Projects
bigquery?: string;
};
tags?: string[]; // Not used for Slack
misc: {
alertType?: EventAlertType; // Datadog alert type
blocks?: (KnownBlock | Block)[]; // Optional Slack blocks

Choose a reason for hiding this comment

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

IT has been quite some time since when we discussed this design.
I cannot remember anymore whether the generic event was supposed to know where to route the message or whether it was supposed to be the simple api the user would use to route a message to a specific channel.

If it is the first (the user sends the message and there is logic in eng-pipes that figures out where to route the message) then it should not be a generic event and it should not contain channel specific parameters (like tags and the list of channels)

If it is the simple one (the user picks the channel), then I don't think putting all the channel fields in the same message and api is a good design, you should have dedicated apis per channel. It would be quite hard to understand which fields are supposed to be populated in which scenario.

If there are good reaons to have one api (still in the simple option where eng-pipes does not provide any routing loigic) and if you wanted to support multiple channels in one api call then a better api design would be to require a list of objects, each object containing all the details needed for a specific channel:

channel[]
  
where channel is the union of 
Slack: {
   channel: string
   blocks
}

Datadog {
   monitor: string
   tags: string[]
}

...

If there are common fields they can be specified only once.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think the idea was to have a combination of both: a generic, simple api which a user can use to route a message to a specific channel, and a more complex webhook which has logic to route more complicated events.

I agree that the typing for the generic event payload can be improved, will work on that

Choose a reason for hiding this comment

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

Defining the public api of a service is a task that should be thought through carefully as changing it afterwards when clients are using is hard.
So feel free to take the time to think the details of all the apis method you expect to create and send a PR with only those types and the empty business logic so we can review if the whole api is coherent and meaningful.

Comment on lines 39 to 40
await messageSlack(body);
await sendEventToDatadog(body, moment().unix());

Choose a reason for hiding this comment

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

Where are the other channels ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The only other one was Jira, but never really got around to getting that added since Alex was (i think?) working a little on the env setup, so I was originally waiting for him to get that done first.

message: GenericEvent,
timestamp: number
) {
if (message.data.channels.datadog) {

Choose a reason for hiding this comment

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

I'd like a pattern where you decide which methods to call in the genericEventNotifier function. That is the orchestrating function, which parses the message and trigger the relevant channels. From their name sendEventToDatadog and messageSlack should always send the message, not decide when not to send.

Dockerfile Outdated
RUN mkdir -p ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts

# Install production dependencies. & update service-registry to main
RUN --mount=type=ssh yarn install --immutable && yarn up "service-registry@git+ssh://[email protected]:getsentry/service-registry#main"

Choose a reason for hiding this comment

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

I don't think you can do this.
service-registry is private, this is still a public repo.
I'd suggest you skip this part for now.

I think you will have to provide the service registry as a config file when you deploy the system, not as a package in the image.

package.json Outdated Show resolved Hide resolved
Comment on lines 32 to 57
export interface SlackMessage {
type: 'slack';
channels: string[];
text: string;
blocks?: KnownBlock[] | Block[];
}

export interface DatadogEvent {
type: 'datadog';
title: string;
text: string;
tags: string[];
alertType: EventAlertType;
}

export interface JiraEvent {
type: 'jira';
projectId: string;
title: string;
}

export type GenericEvent = {
source: string;
timestamp: number;
data: (DatadogEvent | JiraEvent | SlackMessage | ServiceSlackMessage)[];
};

Choose a reason for hiding this comment

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

Ok, this is better.

Comment on lines 59 to 66
// Currently only used for Slack notifications since
// service registry only contains Slack channels (and not DD or Jira or others)
export interface ServiceSlackMessage {
type: 'service_notification';
service_name: string; // Official service registry service id
text: string;
blocks?: KnownBlock[] | Block[];
}

Choose a reason for hiding this comment

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

This seems the same as the slack message, except that you can provide the channel via a service name.
Doing it this way would mean we would duplicate all the methods that can take a destination from the service registry.

Why not abstracting the concept of SlackChannel, and pass it here https://github.com/getsentry/eng-pipes/pull/879/files#diff-a9ba9cbedd19c9f66d564d2e89912890209b98f0a7ef19187877d2587300e476R34 .

You can create two implementations: SlackChannel and ServiceSlackChannel. One contains the channel name and the other contains the service name.

src/webhooks/README.md Outdated Show resolved Hide resolved
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants