@@ -39,10 +39,11 @@ type ExporterImpl struct {
3939
4040 maxConcurrentRequests int
4141 limiter * rate.Limiter
42- client PardotClient
42+ client SalesforceClient
4343 emailCache * EmailCache
4444 emailsHandledCounter prometheus.Counter
4545 pardotErrorCounter prometheus.Counter
46+ caseErrorCounter prometheus.Counter
4647 log blog.Logger
4748}
4849
@@ -55,7 +56,7 @@ var _ emailpb.ExporterServer = (*ExporterImpl)(nil)
5556// is assigned 40% (20,000 requests), it should also receive 40% of the max
5657// concurrent requests (e.g., 2 out of 5). For more details, see:
5758// https://developer.salesforce.com/docs/marketing/pardot/guide/overview.html?q=rate%20limits
58- func NewExporterImpl (client PardotClient , cache * EmailCache , perDayLimit float64 , maxConcurrentRequests int , scope prometheus.Registerer , logger blog.Logger ) * ExporterImpl {
59+ func NewExporterImpl (client SalesforceClient , cache * EmailCache , perDayLimit float64 , maxConcurrentRequests int , scope prometheus.Registerer , logger blog.Logger ) * ExporterImpl {
5960 limiter := rate .NewLimiter (rate .Limit (perDayLimit / 86400.0 ), maxConcurrentRequests )
6061
6162 emailsHandledCounter := prometheus .NewCounter (prometheus.CounterOpts {
@@ -70,6 +71,12 @@ func NewExporterImpl(client PardotClient, cache *EmailCache, perDayLimit float64
7071 })
7172 scope .MustRegister (pardotErrorCounter )
7273
74+ caseErrorCounter := prometheus .NewCounter (prometheus.CounterOpts {
75+ Name : "email_exporter_case_errors" ,
76+ Help : "Total number of errors encountered when sending Cases to the Salesforce REST API" ,
77+ })
78+ scope .MustRegister (caseErrorCounter )
79+
7380 impl := & ExporterImpl {
7481 maxConcurrentRequests : maxConcurrentRequests ,
7582 limiter : limiter ,
@@ -78,6 +85,7 @@ func NewExporterImpl(client PardotClient, cache *EmailCache, perDayLimit float64
7885 emailCache : cache ,
7986 emailsHandledCounter : emailsHandledCounter ,
8087 pardotErrorCounter : pardotErrorCounter ,
88+ caseErrorCounter : caseErrorCounter ,
8189 log : logger ,
8290 }
8391 impl .wake = sync .NewCond (& impl .Mutex )
@@ -116,6 +124,33 @@ func (impl *ExporterImpl) SendContacts(ctx context.Context, req *emailpb.SendCon
116124 return & emptypb.Empty {}, nil
117125}
118126
127+ // SendCase immediately submits a new Case to the Salesforce REST API using the
128+ // provided details. Any retries are handled internally by the SalesforceClient.
129+ // The following fields are required: Origin, Subject, ContactEmail.
130+ func (impl * ExporterImpl ) SendCase (ctx context.Context , req * emailpb.SendCaseRequest ) (* emptypb.Empty , error ) {
131+ if core .IsAnyNilOrZero (req , req .Origin , req .Subject , req .ContactEmail ) {
132+ return nil , berrors .InternalServerError ("incomplete gRPC request message" )
133+ }
134+
135+ err := impl .client .SendCase (Case {
136+ Origin : req .Origin ,
137+ Subject : req .Subject ,
138+ Description : req .Description ,
139+ ContactEmail : req .ContactEmail ,
140+ Organization : req .Organization ,
141+ AccountId : req .AccountId ,
142+ RateLimitName : req .RateLimitName ,
143+ RateLimitTier : req .RateLimitTier ,
144+ UseCase : req .UseCase ,
145+ })
146+ if err != nil {
147+ impl .caseErrorCounter .Inc ()
148+ return nil , berrors .InternalServerError ("sending Case to the Salesforce REST API: %s" , err )
149+ }
150+
151+ return & emptypb.Empty {}, nil
152+ }
153+
119154// Start begins asynchronous processing of the email queue. When the parent
120155// daemonCtx is cancelled the queue will be drained and the workers will exit.
121156func (impl * ExporterImpl ) Start (daemonCtx context.Context ) {
0 commit comments