Skip to content

Commit 6c16784

Browse files
sfe: Export emails to mailing list at submission time (#8378)
Export the requester's email address at submission time if they've indicated the "Yes, email me more information" option in the the Fundraising field.
1 parent f86649b commit 6c16784

File tree

5 files changed

+39
-5
lines changed

5 files changed

+39
-5
lines changed

cmd/sfe/main.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
"github.com/letsencrypt/boulder/cmd"
1212
"github.com/letsencrypt/boulder/config"
13+
emailpb "github.com/letsencrypt/boulder/email/proto"
1314
"github.com/letsencrypt/boulder/features"
1415
bgrpc "github.com/letsencrypt/boulder/grpc"
1516
rapb "github.com/letsencrypt/boulder/ra/proto"
@@ -40,8 +41,9 @@ type Config struct {
4041

4142
TLS cmd.TLSConfig
4243

43-
RAService *cmd.GRPCClientConfig
44-
SAService *cmd.GRPCClientConfig
44+
RAService *cmd.GRPCClientConfig
45+
SAService *cmd.GRPCClientConfig
46+
EmailExporter *cmd.GRPCClientConfig
4547

4648
// UnpauseHMACKey validates incoming JWT signatures at the unpause
4749
// endpoint. This key must be the same as the one configured for all
@@ -131,6 +133,13 @@ func main() {
131133
cmd.FailOnError(err, "Failed to load credentials and create gRPC connection to SA")
132134
sac := sapb.NewStorageAuthorityReadOnlyClient(saConn)
133135

136+
var eec emailpb.ExporterClient
137+
if c.SFE.EmailExporter != nil {
138+
emailExporterConn, err := bgrpc.ClientSetup(c.SFE.EmailExporter, tlsConfig, stats, clk)
139+
cmd.FailOnError(err, "Failed to load credentials and create gRPC connection to email-exporter")
140+
eec = emailpb.NewExporterClient(emailExporterConn)
141+
}
142+
134143
var zendeskClient *zendesk.Client
135144
if c.SFE.Zendesk != nil {
136145
zendeskToken, err := c.SFE.Zendesk.Token.Pass()
@@ -176,6 +185,7 @@ func main() {
176185
c.SFE.Timeout.Duration,
177186
rac,
178187
sac,
188+
eec,
179189
unpauseHMACKey,
180190
zendeskClient,
181191
limiter,

sfe/overrides.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"strconv"
1212
"strings"
1313

14+
emailpb "github.com/letsencrypt/boulder/email/proto"
1415
berrors "github.com/letsencrypt/boulder/errors"
1516
"github.com/letsencrypt/boulder/iana"
1617
"github.com/letsencrypt/boulder/policy"
@@ -88,9 +89,11 @@ var (
8889
// the CertificatesPerDomainPerAccount rate limit override requests.
8990
certificatesPerDomainPerAccountTierOptions = []string{"300", "1000", "5000", "10000", "25000", "50000", "75000", "100000", "175000", "250000", "500000", "1000000", "1750000", "2500000"}
9091

92+
fundraisingYesOption = "Yes, email me more information."
93+
9194
// FundraisingOptions is the list of options for the fundraising field.
9295
FundraisingOptions = []string{
93-
"Yes, email me more information.",
96+
fundraisingYesOption,
9497
"No, not at this time.",
9598
}
9699

@@ -754,8 +757,12 @@ func (sfe *SelfServiceFrontEndImpl) submitOverrideRequestHandler(w http.Response
754757
return
755758
}
756759

757-
// TODO(#8363): If MailingListFieldName value is true, dispatch a request to
758-
// the Salesforce Pardot email exporter.
760+
if sfe.ee != nil && baseFields[fundraisingFieldName] == fundraisingYesOption {
761+
_, err := sfe.ee.SendContacts(r.Context(), &emailpb.SendContactsRequest{Emails: []string{baseFields[emailAddressFieldName]}})
762+
if err != nil {
763+
sfe.log.Errf("failed to send contact to email service: %s", err)
764+
}
765+
}
759766

760767
// TODO(#8362): If FundraisingFieldName value is true, use the Salesforce
761768
// API to create a new Lead record with the provided information.

sfe/overrides_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"strings"
1111
"testing"
1212

13+
"github.com/letsencrypt/boulder/mocks"
1314
rl "github.com/letsencrypt/boulder/ratelimits"
1415
"github.com/letsencrypt/boulder/sfe/zendesk"
1516
"github.com/letsencrypt/boulder/test/zendeskfake"
@@ -143,6 +144,8 @@ func TestSubmitOverrideRequestHandlerErrors(t *testing.T) {
143144
sfe.templatePages = minimalTemplates(t)
144145
client := createFakeZendeskClientServer(t)
145146
sfe.zendeskClient = client
147+
mockPardotClient, mockImpl := mocks.NewMockPardotClientImpl()
148+
sfe.ee = mocks.NewMockExporterImpl(mockPardotClient)
146149

147150
// Submit valid JSON with no rateLimit field.
148151
rec := httptest.NewRecorder()
@@ -177,6 +180,9 @@ func TestSubmitOverrideRequestHandlerErrors(t *testing.T) {
177180
if rec.Code != http.StatusBadRequest {
178181
t.Errorf("Both domain and IP: status=%d; expect 400", rec.Code)
179182
}
183+
if len(mockImpl.GetCreatedContacts()) != 0 {
184+
t.Errorf("PardotClient.SendContact called unexpectedly")
185+
}
180186
}
181187

182188
func TestSubmitOverrideRequestHandlerSuccess(t *testing.T) {
@@ -269,6 +275,9 @@ func TestSubmitOverrideRequestHandlerSuccess(t *testing.T) {
269275

270276
for _, tt := range tests {
271277
t.Run(tt.name, func(t *testing.T) {
278+
mockPardotClient, mockImpl := mocks.NewMockPardotClientImpl()
279+
sfe.ee = mocks.NewMockExporterImpl(mockPardotClient)
280+
272281
iterationBase := map[string]string{}
273282
maps.Copy(iterationBase, testBase)
274283
maps.Copy(iterationBase, tt.fields)
@@ -309,6 +318,9 @@ func TestSubmitOverrideRequestHandlerSuccess(t *testing.T) {
309318
}
310319
break
311320
}
321+
if len(mockImpl.GetCreatedContacts()) != 1 {
322+
t.Errorf("PardotClient.SendContact not called exactly once")
323+
}
312324
})
313325
}
314326
}

sfe/sfe.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
1919

2020
"github.com/letsencrypt/boulder/core"
21+
emailpb "github.com/letsencrypt/boulder/email/proto"
2122
blog "github.com/letsencrypt/boulder/log"
2223
"github.com/letsencrypt/boulder/metrics/measured_http"
2324
rapb "github.com/letsencrypt/boulder/ra/proto"
@@ -55,6 +56,7 @@ var (
5556
type SelfServiceFrontEndImpl struct {
5657
ra rapb.RegistrationAuthorityClient
5758
sa sapb.StorageAuthorityReadOnlyClient
59+
ee emailpb.ExporterClient
5860

5961
log blog.Logger
6062
clk clock.Clock
@@ -80,6 +82,7 @@ func NewSelfServiceFrontEndImpl(
8082
requestTimeout time.Duration,
8183
rac rapb.RegistrationAuthorityClient,
8284
sac sapb.StorageAuthorityReadOnlyClient,
85+
eec emailpb.ExporterClient,
8386
unpauseHMACKey []byte,
8487
zendeskClient *zendesk.Client,
8588
limiter *rl.Limiter,
@@ -100,6 +103,7 @@ func NewSelfServiceFrontEndImpl(
100103
requestTimeout: requestTimeout,
101104
ra: rac,
102105
sa: sac,
106+
ee: eec,
103107
unpauseHMACKey: unpauseHMACKey,
104108
zendeskClient: zendeskClient,
105109
templatePages: tmplPages,

sfe/sfe_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ func setupSFE(t *testing.T) (SelfServiceFrontEndImpl, clock.FakeClock) {
6464
10*time.Second,
6565
&MockRegistrationAuthority{},
6666
mockSA,
67+
nil,
6768
key,
6869
nil,
6970
limiter,

0 commit comments

Comments
 (0)