Skip to content

Commit

Permalink
Easi 4645/send emails (#2910)
Browse files Browse the repository at this point in the history
* remove unused

* implement half of tag handling

* use switch as both groups are not handled the same way

* CC itgov box when it is an admin who is tagged

* working on mechanism to dedupe when needed

* whoops - need to use poster role and info, not recipient

* fix CC in test

* remove DiscussionBoardType from input

* clean up skip conditional

* cautiously adding itgov team email...will double check things

* add `UserAccountsByIDsNP` method, implement group messages

* better naming

* fill in reply functionality, will see if there is a way to DRY this up

* use CreatedBy UUID instead of ID

* nil ptr deref fix attempt

* more nil ptr deref fix attempts

* add emailClient

* one more

* trying with mockSender

* add print

* one more

* still working on this e2e test

* nil check emailClient

* one more nil check

* whoops

* s/warn/info

* whoops - use proper ID

* postman

* swap to fmt for spacing consistency

* fix ids, update discussion links

* refactor resolver logic and touchup email templates without fixing tests

* update postman

* clean up tests

* remove unused enum

* cleanup error handling

* address panics first

* some cleanup

* remove bold tags, going one by one on cleaning up for tests

* hopefully getting closer to matching emails

* Update test email templates to have a valid group name

* passing tests

* urlFromPathAndQuery

---------

Co-authored-by: Clay Benson <clay.benson@oddball.io>
Co-authored-by: Lee Warrick <lee.warrick@oddball.io>
  • Loading branch information
3 people authored Dec 10, 2024
1 parent de03329 commit 9a50dd9
Show file tree
Hide file tree
Showing 22 changed files with 802 additions and 497 deletions.
52 changes: 45 additions & 7 deletions EASI.postman_collection.json
Original file line number Diff line number Diff line change
Expand Up @@ -1179,7 +1179,7 @@
"mode": "graphql",
"graphql": {
"query": "mutation createSystemIntakeGRBDiscussion(\n $input: createSystemIntakeGRBDiscussionPostInput!\n) {\n createSystemIntakeGRBDiscussionPost(input: $input) {\n id\n content\n grbRole\n votingRole\n systemIntakeID\n createdByUserAccount {\n id\n username\n }\n }\n}\n",
"variables": "{\r\n \"input\": {\r\n \"systemIntakeID\": \"8edb237e-ad48-49b2-91cf-8534362bc6cf\",\r\n \"content\": \"<p>banana apple carburetor Let me look into it, ok? <span data-type=\\\"mention\\\" tag-type=\\\"USER_ACCOUNT\\\" class=\\\"mention\\\" data-id-db=\\\"8dc55eda-be23-4822-aa69-a3f67de6078b\\\">@Audrey Abrams</span>!\\\" <a href=\\\"https://www.w3schools.com\\\">Visit W3Schools.com!</a></p>\"\r\n }\r\n}"
"variables": "{\r\n \"input\": {\r\n \"systemIntakeID\": \"{{systemIntakeID}}\",\r\n \"content\": \"<p>banana apple carburetor Let me look into it, ok? <span data-type=\\\"mention\\\" tag-type=\\\"USER_ACCOUNT\\\" class=\\\"mention\\\" data-id-db=\\\"{{UserAccountID}}\\\">@Mckayla Fritsch</span>!</p>\"\r\n }\r\n}"
}
},
"url": {
Expand Down Expand Up @@ -1212,7 +1212,7 @@
"mode": "graphql",
"graphql": {
"query": "mutation createSystemIntakeGRBDiscussion(\n $input: createSystemIntakeGRBDiscussionPostInput!\n) {\n createSystemIntakeGRBDiscussionPost(input: $input) {\n id\n content\n grbRole\n votingRole\n systemIntakeID\n createdByUserAccount {\n id\n username\n }\n }\n}\n",
"variables": "{\r\n \"input\": {\r\n \"systemIntakeID\": \"8edb237e-ad48-49b2-91cf-8534362bc6cf\",\r\n \"content\": \"<p>banana apple carburetor Let me look into it, ok? <span data-type=\\\"mention\\\" tag-type=\\\"GROUP_IT_GOV\\\" class=\\\"mention\\\">@Group</span> middle <span data-type=\\\"mention\\\" tag-type=\\\"GROUP_GRB_REVIEWERS\\\" class=\\\"mention\\\">@Group2</span>!\\\"</p>\"\r\n }\r\n}"
"variables": "{\r\n \"input\": {\r\n \"systemIntakeID\": \"{{systemIntakeID}}\",\r\n \"content\": \"<p>banana apple carburetor Let me look into it, ok? <span data-type=\\\"mention\\\" tag-type=\\\"GROUP_IT_GOV\\\" class=\\\"mention\\\">@Group</span> middle <span data-type=\\\"mention\\\" tag-type=\\\"GROUP_GRB_REVIEWERS\\\" class=\\\"mention\\\">@Group2</span>!\\\"</p>\"\r\n }\r\n}"
}
},
"url": {
Expand Down Expand Up @@ -1245,7 +1245,40 @@
"mode": "graphql",
"graphql": {
"query": "mutation createSystemIntakeGRBDiscussionReply($input: createSystemIntakeGRBDiscussionReplyInput!) {\n createSystemIntakeGRBDiscussionReply(input: $input) {\n id\n content\n grbRole\n votingRole\n systemIntakeID\n createdByUserAccount {\n id\n username\n }\n }\n}",
"variables": "{\r\n \"input\": {\r\n \"initialPostID\": \"{{SystemIntakeGRBDiscussionID}}\",\r\n \"content\": \"<p>monkey kiwi maduros senor</p>\"\r\n }\r\n}"
"variables": "{\r\n \"input\": {\r\n \"initialPostID\": \"{{SystemIntakeGRBDiscussionID}}\",\r\n \"content\": \"<p>monkey kiwi maduros senor <span data-type=\\\"mention\\\" tag-type=\\\"USER_ACCOUNT\\\" class=\\\"mention\\\" data-id-db=\\\"{{UserAccountID}}\\\">@Mckayla Fritsch</span>!</p>\"\r\n }\r\n}"
}
},
"url": {
"raw": "{{url}}",
"host": [
"{{url}}"
]
}
},
"response": []
},
{
"name": "Add GRB Discussion Reply Group Tags",
"event": [
{
"listen": "test",
"script": {
"exec": [
""
],
"type": "text/javascript",
"packages": {}
}
}
],
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "graphql",
"graphql": {
"query": "mutation createSystemIntakeGRBDiscussionReply($input: createSystemIntakeGRBDiscussionReplyInput!) {\n createSystemIntakeGRBDiscussionReply(input: $input) {\n id\n content\n grbRole\n votingRole\n systemIntakeID\n createdByUserAccount {\n id\n username\n }\n }\n}",
"variables": "{\r\n \"input\": {\r\n \"initialPostID\": \"{{SystemIntakeGRBDiscussionID}}\",\r\n \"content\": \"<p>Let me look into it, ok? <span data-type=\\\"mention\\\" tag-type=\\\"GROUP_IT_GOV\\\" class=\\\"mention\\\">@Group</span><span data-type=\\\"mention\\\" tag-type=\\\"GROUP_GRB_REVIEWERS\\\" class=\\\"mention\\\">@Group2</span>!</p>\"\r\n }\r\n}"
}
},
"url": {
Expand Down Expand Up @@ -3989,9 +4022,10 @@
"listen": "test",
"script": {
"exec": [
""
"pm.collectionVariables.set(\"UserAccountID\", pm.response.json().data.userAccount.id)"
],
"type": "text/javascript"
"type": "text/javascript",
"packages": {}
}
}
],
Expand All @@ -4001,8 +4035,8 @@
"body": {
"mode": "graphql",
"graphql": {
"query": "query {\n userAccount(username: \"EASI_SYSTEM\") {\n id\n username\n commonName\n familyName\n givenName\n email\n locale\n }\n}",
"variables": ""
"query": "query($username: String!) {\n userAccount(username: $username) {\n id\n username\n commonName\n familyName\n givenName\n email\n locale\n }\n}",
"variables": "{\r\n \"username\": \"USR1\"\r\n}"
}
},
"url": {
Expand Down Expand Up @@ -4247,6 +4281,10 @@
"key": "SystemIntakeGRBDiscussionID",
"value": "",
"type": "string"
},
{
"key": "UserAccountID",
"value": ""
}
]
}
2 changes: 2 additions & 0 deletions cmd/devdata/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/cms-enterprise/easi-app/cmd/devdata/mock"
"github.com/cms-enterprise/easi-app/pkg/appconfig"
"github.com/cms-enterprise/easi-app/pkg/appcontext"
"github.com/cms-enterprise/easi-app/pkg/dataloaders"
"github.com/cms-enterprise/easi-app/pkg/local"
"github.com/cms-enterprise/easi-app/pkg/models"
"github.com/cms-enterprise/easi-app/pkg/storage"
Expand Down Expand Up @@ -86,6 +87,7 @@ func main() {
nonUserCtx := context.Background()
nonUserCtx = mock.CtxWithNewDataloaders(nonUserCtx, store)
nonUserCtx = appcontext.WithLogger(nonUserCtx, logger)
nonUserCtx = appcontext.WithUserAccountService(nonUserCtx, dataloaders.GetUserAccountByID)

// userCtx is a local helper function (so we can not have to pass local variables all the time) that adds a principal
// to a context object and returns it.
Expand Down
98 changes: 38 additions & 60 deletions cmd/test_email_templates/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -632,33 +632,25 @@ func sendITGovEmails(ctx context.Context, client *email.Client) {
err = client.SystemIntake.SendGRBReviewDiscussionReplyEmail(
ctx,
email.SendGRBReviewDiscussionReplyEmailInput{
SystemIntakeID: intakeID,
UserName: "Discussion Tester #1",
RequestName: "GRB Review Discussion Test",
DiscussionBoardType: "Internal GRB Discussion Board",
GRBReviewLink: "google.com",
Role: "Voting Member",
DiscussionContent: `<p>banana apple carburetor Let me look into it, ok? <span data-type="mention" tag-type="USER_ACCOUNT" class="mention" data-id-db="8dc55eda-be23-4822-aa69-a3f67de6078b">@Audrey Abrams</span>!"</p>`,
DiscussionLink: "google.com",
ITGovernanceInboxAddress: "IT_Governance@cms.hhs.gov",
Recipient: requesterEmail,
SystemIntakeID: intakeID,
UserName: "Discussion Tester #1",
RequestName: "GRB Review Discussion Test",
Role: "Voting Member",
DiscussionContent: `<p>banana apple carburetor Let me look into it, ok? <span data-type="mention" tag-type="USER_ACCOUNT" class="mention" data-id-db="8dc55eda-be23-4822-aa69-a3f67de6078b">@Audrey Abrams</span>!"</p>`,
Recipient: requesterEmail,
},
)
noErr(err)

err = client.SystemIntake.SendGRBReviewDiscussionIndividualTaggedEmail(
ctx,
email.SendGRBReviewDiscussionIndividualTaggedEmailInput{
SystemIntakeID: intakeID,
UserName: "Discussion Tester #1",
RequestName: "GRB Review Discussion Test",
DiscussionBoardType: "Internal GRB Discussion Board",
GRBReviewLink: "google.com",
Role: "Voting Member",
DiscussionContent: `<p>banana apple carburetor Let me look into it, ok? <span data-type="mention" tag-type="USER_ACCOUNT" class="mention" data-id-db="8dc55eda-be23-4822-aa69-a3f67de6078b">@Audrey Abrams</span>!"</p>`,
DiscussionLink: "google.com",
ITGovernanceInboxAddress: "IT_Governance@cms.hhs.gov",
Recipient: requesterEmail,
SystemIntakeID: intakeID,
UserName: "Discussion Tester #1",
RequestName: "GRB Review Discussion Test",
Role: "Voting Member",
DiscussionContent: `<p>banana apple carburetor Let me look into it, ok? <span data-type="mention" tag-type="USER_ACCOUNT" class="mention" data-id-db="8dc55eda-be23-4822-aa69-a3f67de6078b">@Audrey Abrams</span>!"</p>`,
Recipients: []models.EmailAddress{requesterEmail},
},
)
noErr(err)
Expand All @@ -667,16 +659,12 @@ func sendITGovEmails(ctx context.Context, client *email.Client) {
err = client.SystemIntake.SendGRBReviewDiscussionIndividualTaggedEmail(
ctx,
email.SendGRBReviewDiscussionIndividualTaggedEmailInput{
SystemIntakeID: intakeID,
UserName: "Discussion Tester #1",
RequestName: "GRB Review Discussion Test",
DiscussionBoardType: "Internal GRB Discussion Board",
GRBReviewLink: "google.com",
Role: "", // empty to signify admin
DiscussionContent: `<p>banana apple carburetor Let me look into it, ok? <span data-type="mention" tag-type="USER_ACCOUNT" class="mention" data-id-db="8dc55eda-be23-4822-aa69-a3f67de6078b">@Audrey Abrams</span>!"</p>`,
DiscussionLink: "google.com",
ITGovernanceInboxAddress: "IT_Governance@cms.hhs.gov",
Recipient: requesterEmail,
SystemIntakeID: intakeID,
UserName: "Discussion Tester #1",
RequestName: "GRB Review Discussion Test",
Role: "Governance Admin Team",
DiscussionContent: `<p>banana apple carburetor Let me look into it, ok? <span data-type="mention" tag-type="USER_ACCOUNT" class="mention" data-id-db="8dc55eda-be23-4822-aa69-a3f67de6078b">@Audrey Abrams</span>!"</p>`,
Recipients: []models.EmailAddress{requesterEmail},
},
)
noErr(err)
Expand All @@ -685,33 +673,26 @@ func sendITGovEmails(ctx context.Context, client *email.Client) {
err = client.SystemIntake.SendGRBReviewDiscussionIndividualTaggedEmail(
ctx,
email.SendGRBReviewDiscussionIndividualTaggedEmailInput{
SystemIntakeID: intakeID,
UserName: "Discussion Tester #1",
RequestName: "GRB Review Discussion Test",
DiscussionBoardType: "Internal GRB Discussion Board",
GRBReviewLink: "google.com",
Role: "", // empty to signify admin
DiscussionContent: `<p>banana apple carburetor Let me look into it, ok? <span data-type="mention" tag-type="USER_ACCOUNT" class="mention" data-id-db="8dc55eda-be23-4822-aa69-a3f67de6078b">@Audrey Abrams</span>!"</p>`,
DiscussionLink: "google.com",
ITGovernanceInboxAddress: "IT_Governance@cms.hhs.gov",
Recipient: requesterEmail,
SystemIntakeID: intakeID,
UserName: "Discussion Tester #1",
RequestName: "GRB Review Discussion Test",
Role: "Governance Admin Team",
DiscussionContent: `<p>banana apple carburetor Let me look into it, ok? <span data-type="mention" tag-type="USER_ACCOUNT" class="mention" data-id-db="8dc55eda-be23-4822-aa69-a3f67de6078b">@Audrey Abrams</span>!"</p>`,
Recipients: []models.EmailAddress{requesterEmail},
},
)
noErr(err)

err = client.SystemIntake.SendGRBReviewDiscussionGroupTaggedEmail(
ctx,
email.SendGRBReviewDiscussionGroupTaggedEmailInput{
SystemIntakeID: intakeID,
UserName: "Discussion Tester #1",
RequestName: "GRB Review Discussion Test",
DiscussionBoardType: "Internal GRB Discussion Board",
GRBReviewLink: "google.com",
Role: "Voting Member",
DiscussionContent: `<p>banana apple carburetor Let me look into it, ok? <span data-type="mention" tag-type="USER_ACCOUNT" class="mention" data-id-db="8dc55eda-be23-4822-aa69-a3f67de6078b">@Audrey Abrams</span>!"</p>`,
DiscussionLink: "google.com",
ITGovernanceInboxAddress: "IT_Governance@cms.hhs.gov",
Recipients: emailNotificationRecipients.RegularRecipientEmails,
SystemIntakeID: intakeID,
UserName: "Discussion Tester #1",
RequestName: "GRB Review Discussion Test",
Role: "Voting Member, CIO",
GroupName: "Governance Admin Team",
DiscussionContent: `<p>banana apple carburetor Let me look into it, ok? <span data-type="mention" tag-type="USER_ACCOUNT" class="mention" data-id-db="8dc55eda-be23-4822-aa69-a3f67de6078b">@Audrey Abrams</span>!"</p>`,
Recipients: emailNotificationRecipients,
},
)
noErr(err)
Expand All @@ -720,16 +701,13 @@ func sendITGovEmails(ctx context.Context, client *email.Client) {
err = client.SystemIntake.SendGRBReviewDiscussionGroupTaggedEmail(
ctx,
email.SendGRBReviewDiscussionGroupTaggedEmailInput{
SystemIntakeID: intakeID,
UserName: "Discussion Tester #1",
RequestName: "GRB Review Discussion Test",
DiscussionBoardType: "Internal GRB Discussion Board",
GRBReviewLink: "google.com",
Role: "", // empty to signify admin
DiscussionContent: `<p>banana apple carburetor Let me look into it, ok? <span data-type="mention" tag-type="USER_ACCOUNT" class="mention" data-id-db="8dc55eda-be23-4822-aa69-a3f67de6078b">@Audrey Abrams</span>!"</p>`,
DiscussionLink: "google.com",
ITGovernanceInboxAddress: "IT_Governance@cms.hhs.gov",
Recipients: emailNotificationRecipients.RegularRecipientEmails,
SystemIntakeID: intakeID,
UserName: "Discussion Tester #1",
RequestName: "GRB Review Discussion Test",
Role: "Governance Admin Team",
GroupName: "GRB",
DiscussionContent: `<p>banana apple carburetor Let me look into it, ok? <span data-type="mention" tag-type="USER_ACCOUNT" class="mention" data-id-db="8dc55eda-be23-4822-aa69-a3f67de6078b">@Audrey Abrams</span>!"</p>`,
Recipients: emailNotificationRecipients,
},
)
noErr(err)
Expand Down
13 changes: 13 additions & 0 deletions pkg/email/email.go
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,19 @@ func (c Client) urlFromPath(path string) string {
Host: c.config.URLHost,
Path: path,
}

return u.String()
}

// urlFromPathAndQuery uses the client's URL configs to format one with a specific path and appended query
func (c Client) urlFromPathAndQuery(path string, query string) string {
u := url.URL{
Scheme: c.config.URLScheme,
Host: c.config.URLHost,
Path: path,
RawQuery: query,
}

return u.String()
}

Expand Down
55 changes: 25 additions & 30 deletions pkg/email/grb_review_discussion_group_tagged.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"context"
"errors"
"fmt"
"html/template"
"path"

Expand All @@ -15,17 +16,14 @@ import (
// SendGRBReviewDiscussionGroupTaggedEmailInput contains the data needed to send an email informing a group they
// have been tagged in a GRB discussion
type SendGRBReviewDiscussionGroupTaggedEmailInput struct {
SystemIntakeID uuid.UUID
UserName string
GroupName string // TODO NJD enum?
RequestName string
DiscussionBoardType string
GRBReviewLink string
Role string
DiscussionContent template.HTML
DiscussionLink string
ITGovernanceInboxAddress string
Recipients []models.EmailAddress
SystemIntakeID uuid.UUID
UserName string
GroupName string // TODO NJD enum?
RequestName string
Role string
DiscussionContent template.HTML
DiscussionID uuid.UUID
Recipients models.EmailNotificationRecipients
}

// GRBReviewDiscussionGroupTaggedBody contains the data needed for interpolation in
Expand All @@ -41,7 +39,7 @@ type GRBReviewDiscussionGroupTaggedBody struct {
DiscussionContent template.HTML
DiscussionLink string
ClientAddress string
ITGovernanceInboxAddress string
ITGovernanceInboxAddress models.EmailAddress
IsAdmin bool
}

Expand All @@ -51,23 +49,18 @@ func (sie systemIntakeEmails) grbReviewDiscussionGroupTaggedBody(input SendGRBRe
}

grbReviewPath := path.Join("it-governance", input.SystemIntakeID.String(), "grb-review")
grbDiscussionPath := path.Join(grbReviewPath, "discussionID=BLAH") // TODO: NJD add actual discussion ID field
role := input.Role
if len(role) < 1 {
role = "Governance Admin Team"
}

data := GRBReviewDiscussionGroupTaggedBody{
UserName: input.UserName,
GroupName: input.GroupName,
RequestName: input.RequestName,
DiscussionBoardType: input.DiscussionBoardType,
DiscussionBoardType: "Internal GRB Discussion Board",
GRBReviewLink: sie.client.urlFromPath(grbReviewPath),
Role: role,
Role: input.Role,
DiscussionContent: input.DiscussionContent,
DiscussionLink: sie.client.urlFromPath(grbDiscussionPath),
ITGovernanceInboxAddress: input.ITGovernanceInboxAddress,
IsAdmin: len(input.Role) < 1,
DiscussionLink: sie.client.urlFromPathAndQuery(grbReviewPath, fmt.Sprintf("discussionMode=reply&discussionId=%s", input.DiscussionID.String())),
ITGovernanceInboxAddress: sie.client.config.GRTEmail,
IsAdmin: input.Role == "Governance Admin Team",
}

var b bytes.Buffer
Expand All @@ -81,22 +74,24 @@ func (sie systemIntakeEmails) grbReviewDiscussionGroupTaggedBody(input SendGRBRe
// SendGRBReviewDiscussionGroupTaggedEmail sends an email to a group indicating that they have
// been tagged in a GRB discussion
func (sie systemIntakeEmails) SendGRBReviewDiscussionGroupTaggedEmail(ctx context.Context, input SendGRBReviewDiscussionGroupTaggedEmailInput) error {
subject := "The " + input.GroupName + "was tagged in a GRB Review discussion for " + input.RequestName
subject := fmt.Sprintf("The %[1]s was tagged in a GRB Review discussion for %[2]s", input.GroupName, input.RequestName)

body, err := sie.grbReviewDiscussionGroupTaggedBody(input)
if err != nil {
return err
}
email := NewEmail().
// use BCC as this is going to multiple recipients
WithBCCAddresses(input.Recipients.RegularRecipientEmails).
WithSubject(subject).
WithBody(body)

allRecipients := []models.EmailAddress{}
allRecipients = append(allRecipients, models.NewEmailAddress("fake@fake.com"))
if input.Recipients.ShouldNotifyITGovernance {
email = email.WithCCAddresses([]models.EmailAddress{sie.client.config.GRTEmail})
}

return sie.client.sender.Send(
ctx,
NewEmail().
// use BCC as this is going to multiple recipients
WithBCCAddresses(allRecipients).
WithSubject(subject).
WithBody(body),
email,
)
}
Loading

0 comments on commit 9a50dd9

Please sign in to comment.