Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat(coderd): add company logo when available for email notifications#14935

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
defelmnq merged 27 commits intomainfromfeat/notif-custom-logo
Oct 22, 2024

Conversation

defelmnq
Copy link
Contributor

@defelmnqdefelmnq commentedOct 2, 2024
edited
Loading

This PR aims toclose#14253

We keep the default behavior using the Coder logo if there's no logo set.
Otherwise we want to use the logo based on the URL set in appearance.

stirby and matifali reacted with heart emoji
@github-actionsGitHub Actions
Copy link

github-actionsbot commentedOct 2, 2024
edited
Loading

All contributors have signed the CLA ✍️ ✅
Posted by theCLA Assistant Lite bot.

@stirby
Copy link
Collaborator

@defelmnq Assuming this closes#14253.

I'm concerned about how the aspect ratio/inclusion of text impacts display. Right now, I believe the logo we use in the email includes "Coder" as text. The logo we use on the dashboard does not.

We may want to include whatever name they replace "Coder" with in the deployment appearance settings in the email as well.

@defelmnqdefelmnq changed the titlefeat(notifications): add company logo when available for email notificationsfeat(coderd): add company logo when available for email notificationsOct 3, 2024
@defelmnq
Copy link
ContributorAuthor

I have read the CLA Document and I hereby sign the CLA

cdrcommunity added a commit to coder/cla that referenced this pull requestOct 3, 2024
@defelmnq
Copy link
ContributorAuthor

@stirby Indeed the objective is to resolve#14253

You're totally right - I just have a question as we recommend using a 3:1 aspect ratio image (We recommend a transparent image with 3:1 aspect ratio.)

Should we put the name next to the logo anyway or maybe below ?

I will update the PR to add the value in the meantime.

@defelmnqdefelmnq marked this pull request as ready for reviewOctober 3, 2024 09:54
logoURL = NotificationsDefaultLogoURL
}

payload.Labels["_logo_url"] = logoURL
Copy link
Member

Choose a reason for hiding this comment

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

Thinking loud:

Logo URL does not seem to be aLabel orData. Maybe we need to introduce another property likeMeta? Or just put it on the same level as title and body.

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with@mtojek here.

I don't think this should go into the payload. It's going to be the same for every notification, and if it's in the payload then we'll be storing the same URL for every notification.

Have a look atcoderd/notifications/dispatch/smtp/html.gotmpl: you'll see{{ base_url }}. This is provided by a helper function, defined incli/server.go`:

// templateHelpers builds a set of functions which can be called in templates.// We build them here to avoid an import cycle by using coderd.Options in notifications.Manager.// We can later use this to inject whitelabel fields when app name / logo URL are overridden. <----functemplateHelpers(options*coderd.Options)map[string]any {returnmap[string]any{"base_url":func()string {returnoptions.AccessURL.String() },"current_year":func()string {returnstrconv.Itoa(time.Now().Year()) },}}

We should create a new helper which retrieves the logo instead, I think.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

Thanks, indeed it makes complete sense 🙏

I modified the approach to have bothlogo_url andapp_name defined in the templateHelpers managinf both the normal flow, default value, and error cases. Keen to have your opinion on it.

dannykopping reacted with heart emoji
Copy link
Contributor

Choose a reason for hiding this comment

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

Super 👍

@@ -223,6 +229,18 @@ func (n *notifier) prepare(ctx context.Context, msg database.AcquireNotification
return nil, xerrors.Errorf("failed to resolve handler %q", msg.Method)
}

logoURL, err := n.store.GetLogoURL(ctx)
if err != nil && !errors.Is(err, sql.ErrNoRows) {
n.log.Error(ctx, "failed fetching logo url", slog.Error(err))
Copy link
Member

Choose a reason for hiding this comment

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

This is an interesting use case. Some customers prefer to use their company logos instead of Coder default one. I think we should rather fail fast the notifier flow than default toCoder logo.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

cc@mtojek@mafredri as you both raised this one. In case of errors I can :

  • add default values (seems we do not want to avoid displaying Coder information to customers that prefer not.)
  • skip the logo or name
  • retry the notification

As this situation means that we had a failure fetching data from the db, I don't know what is the preferred solution.

Copy link
Member

Choose a reason for hiding this comment

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

This could be an option to make it less awkward:#14935 (comment)

... and just retry.

Copy link
Contributor

Choose a reason for hiding this comment

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

Errors in retrieving the data from the db will cause the notification to fail, and it'll be automatically retried later.
If we have a persistent issue which causes the notifications to exceed their retry limit (5 by default) then that's actually a good signal that something is wrong.

@@ -8,7 +8,7 @@
<body style="margin: 0; padding: 0; font-family: -apple-system, system-ui, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617; background: #f8fafc;">
<div style="max-width: 600px; margin: 20px auto; padding: 60px; border: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-align: left; font-size: 14px; line-height: 1.5;">
<div style="text-align: center;">
<img src="https://coder.com/coder-logo-horizontal.png" alt="Coder Logo" style="height: 40px;" />
<img src="{{ .Labels._logo_url }}" alt="Company Logo" style="height: 40px;" />
Copy link
Member

Choose a reason for hiding this comment

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

Should we present also the company name?

dannykopping reacted with thumbs up emoji
Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

🤓 Will add it and present some screenshots so we can iterate with the name.

"c": "d",
"a": "b",
"c": "d",
"_logo_url": notifications.NotificationsDefaultLogoURL,
Copy link
Member

Choose a reason for hiding this comment

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

If the reason for exposingNotificationsDefaultLogoURL is just this test, I think you can safely switch to a generichttp://foobar.tld/logo.png.

Copy link
ContributorAuthor

@defelmnqdefelmnqOct 3, 2024
edited
Loading

Choose a reason for hiding this comment

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

Was indeed not making sense at the time of your comment - good catch. Now that I changed to have these values defined incli/server.go I need these. Anyway, I feel like it should maybe not be only related to notifications. Any idea about where I can put this kind of default values ?

Copy link
Member

@mafredrimafredri left a comment

Choose a reason for hiding this comment

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

Nice work! Left some feedback, but I don't have much context on decisions around this, so feel free to ignore if my feedback isn't appropriate.

@dannykopping
Copy link
Contributor

You're totally right - I just have a question as we recommend using a 3:1 aspect ratio image (We recommend a transparent image with 3:1 aspect ratio.)

We setheight: 40px now, which means an image with a 3:1 aspect ratio would be 120px wide.
Thelogo we use is 364:81 (~4.5:1) ratio, so with the height of 40px it becomes ~180px wide.

Current:

image

Simulated with image with 3:1 aspect ratio:

image

I think a logo with 3:1 would look quite bad (or at least, it wouldn't look like a header image; it looks quite wimpy now).

Perhaps we should use a logo of ours which is actually 3:1 and set the height a bit higher (60px) so we maintain this 180px width?

Copy link
Contributor

@dannykoppingdannykopping left a comment

Choose a reason for hiding this comment

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

Nearly there, good work!

logoURL = NotificationsDefaultLogoURL
}

payload.Labels["_logo_url"] = logoURL
Copy link
Contributor

Choose a reason for hiding this comment

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

Super 👍

Copy link
Member

@mafredrimafredri left a comment

Choose a reason for hiding this comment

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

Except for the background contexts and potential for locking up through stalled DB queries, I think this looks great, nice work!

cli/server.go Outdated
@@ -1309,6 +1309,30 @@ func templateHelpers(options *coderd.Options) map[string]any {
return map[string]any{
"base_url": func() string { return options.AccessURL.String() },
"current_year": func() string { return strconv.Itoa(time.Now().Year()) },
"logo_url": func() string {
logoURL, err := options.Database.GetLogoURL(context.Background())
Copy link
Member

Choose a reason for hiding this comment

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

Perhaps passing a context totemplateHelpers (server lifetime) would be a good idea, and perhaps we should define a maximum execution time here (context.WithTimeout) so that we don't end up in a situation where the DB query simply blocks for an indefinite amount of time.

dannykopping reacted with thumbs up emoji
Copy link
Member

Choose a reason for hiding this comment

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

👍

I have similar feelings. We might be risking by placing DB call inside a template helper. I'm wondering if we shouldn't implement something similar to AppearanceFetcher for the site handler.

Copy link
Contributor

Choose a reason for hiding this comment

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

Good catch 👍

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

Indeed thanks, good catch ! 🙏

As these methods seem directly connected to templating engine from go , I've not been able to find a way to pass a context to it. Anyway, I created a context with a hardcoded 1 second timeout for it , to at least make it safer.

Not sure if there's a better way to do ?

Copy link
Member

@mtojekmtojekOct 4, 2024
edited
Loading

Choose a reason for hiding this comment

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

What would you say if we slightly modify this concept. My main concern is around swallowing the error in favor of returning an empty string"", so:

Wherever we render thehtmlBody:

htmlBody,err=render.GoTemplate(htmlTemplate,payload,s.helpers)iferr!=nil {returnnil,xerrors.Errorf("render full html template: %w",err)}

we create a dynamictemplate.FuncMap withapp_name andlogo_url. We could add a reference to the store toSMTPHandler, and retrieve values before rendering.

Thoughts?

@github-actionsgithub-actionsbot added the staleThis issue is like stale bread. labelOct 12, 2024
@matifalimatifali removed the staleThis issue is like stale bread. labelOct 12, 2024
@defelmnq
Copy link
ContributorAuthor

Update here - based on the last conversation with the team I moved thefetchApplicationName andfetchLogoURL functions to be isolated.

Also generated a specific golden file for a notification with custom application name and logo URL so we can validate they are correctly fetched.

After discussing it with@dannykopping I changed the Dispatcher signature to take dynamic helpers instead of ones defined at instantiation.

Finally, in term of logic we have three paths :

  • Either the appearance settings are not defined and we take Coder values.
  • Either they are defined and we take it.
  • Either we have a DB error while trying to fetch it and we go to the error path without sending the notification.

@defelmnqdefelmnq requested a review frommtojekOctober 18, 2024 13:08
Copy link
Member

@mtojekmtojek left a comment

Choose a reason for hiding this comment

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

Ship it 👍

Copy link
Contributor

@dannykoppingdannykopping left a comment

Choose a reason for hiding this comment

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

Nearly there!

}

func (s *SMTPHandler) Dispatcher(payload types.MessagePayload, titleTmpl, bodyTmpl string) (DeliveryFunc, error) {
func (s *SMTPHandler) Dispatcher(helpers template.FuncMap,payload types.MessagePayload, titleTmpl, bodyTmpl string) (DeliveryFunc, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: when passing arguments, I try think of them in order of significance.
The helpers are not the most significant argument here; they're likely the least. I'd movehelpers to the end.

Copy link
Contributor

Choose a reason for hiding this comment

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

Nice 👍

@@ -8,7 +8,7 @@
<body style="margin: 0; padding: 0; font-family: -apple-system, system-ui, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617; background: #f8fafc;">
<div style="max-width: 600px; margin: 20px auto; padding: 60px; border: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-align: left; font-size: 14px; line-height: 1.5;">
<div style="text-align: center;">
<img src="https://coder.com/coder-logo-horizontal.png" alt="Coder Logo" style="height: 40px;" />
<img src="{{ logo_url }}" alt="{{ app_name }} Logo" style="height: 40px;" />
Copy link
Member

Choose a reason for hiding this comment

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

note: my immediate thought was to use aData URL but apparently support for this is extremely limited.

Copy link
Contributor

@dannykoppingdannykopping left a comment

Choose a reason for hiding this comment

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

LGTM!

We just need to ensure failures to fetch app name / logo will be retried, then we can get this merged.

if err != nil {
return nil, xerrors.Errorf("fetchapp name: %w", err)
return nil, xerrors.Errorf("fetchhelpers: %w", err)
Copy link
Contributor

Choose a reason for hiding this comment

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

Have you validated that failures here cause a "temporary failure" so that subsequent requests are retried?

@stirby
Copy link
Collaborator

Exciting!

@defelmnqdefelmnq merged commit297089e intomainOct 22, 2024
26 checks passed
@defelmnqdefelmnq deleted the feat/notif-custom-logo branchOctober 22, 2024 12:06
@github-actionsgithub-actionsbot locked and limited conversation to collaboratorsOct 22, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account?Sign in.
Reviewers

@dannykoppingdannykoppingdannykopping approved these changes

@johnstcnjohnstcnjohnstcn approved these changes

@mafredrimafredrimafredri approved these changes

@mtojekmtojekmtojek approved these changes

Assignees

@defelmnqdefelmnq

Labels
None yet
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

Notifications: allow customers to rebrand email
7 participants
@defelmnq@stirby@dannykopping@mafredri@johnstcn@mtojek@matifali

[8]ページ先頭

©2009-2025 Movatter.jp