Skip to content

Commit

Permalink
[EASI-4457] TRB Advice letter dataloader (#2702)
Browse files Browse the repository at this point in the history
* add dataloaders to TRB Feedback

* convert slice to postgres array

* use dataloaders in status calls

* remove unnecessary no rows error check

* add dataloaders to advice letter and finish refactoring statuses

* sort mock users

* nil check statuses
  • Loading branch information
mynar7 authored Jul 16, 2024
1 parent e66b076 commit 08b3419
Show file tree
Hide file tree
Showing 21 changed files with 295 additions and 222 deletions.
126 changes: 63 additions & 63 deletions cmd/devdata/main.go

Large diffs are not rendered by default.

49 changes: 21 additions & 28 deletions cmd/devdata/mock/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,43 +8,23 @@ import (

"github.com/cms-enterprise/easi-app/pkg/appcontext"
"github.com/cms-enterprise/easi-app/pkg/authentication"
"github.com/cms-enterprise/easi-app/pkg/dataloaders"
"github.com/cms-enterprise/easi-app/pkg/local"
"github.com/cms-enterprise/easi-app/pkg/local/cedarcoremock"
"github.com/cms-enterprise/easi-app/pkg/models"
"github.com/cms-enterprise/easi-app/pkg/storage"
"github.com/cms-enterprise/easi-app/pkg/userhelpers"
)

// These represent some users who have mocked okta data
// corresponds to list in /pkg/local/okta_api.go
const (
// PrincipalUser is the "current user" when seeding the data (Adeline Aarons)
PrincipalUser = "ABCD"

// TestUser is the "TEST user" when seeding the data (Terry Thompson)
TestUser = "TEST"

// EndToEnd1User is the username of the user for some end to end testing
EndToEnd1User = "E2E1"

// EndToEnd2User is the username of the user for some end to end testing
EndToEnd2User = "E2E2"

AllyUser = "A11Y"
GaryUser = "GRTB"
AubryUser = "ADMI"
User1User = "USR1"
User2User = "USR2"
User3User = "USR3"
User4User = "USR4"
User5User = "USR5"
TheoUser = "CJRW"
PrincipalUser string = "USR1"
EndToEndUserOne string = "E2E1"
TestUser string = "TEST"
AccessibilityUser string = "A11Y"
Batman string = "BTMN"
)

var UserNamesForCedarSystemRoles = []string{
PrincipalUser, TestUser, EndToEnd1User, EndToEnd2User, AllyUser, GaryUser, AubryUser, User1User, User2User, User3User, User4User, User5User, TheoUser,
// Duplicate so we don't run out of users for roles
PrincipalUser, TestUser, EndToEnd1User, EndToEnd2User, AllyUser, GaryUser, AubryUser, User1User, User2User, User3User, User4User, User5User, TheoUser,
PrincipalUser, TestUser, EndToEnd1User, EndToEnd2User, AllyUser, GaryUser, AubryUser, User1User, User2User, User3User, User4User, User5User, TheoUser}

// FetchUserInfoMock mocks the fetch user info logic
func FetchUserInfoMock(ctx context.Context, username string) (*models.UserInfo, error) {
localOktaClient := local.NewOktaAPIClient()
Expand Down Expand Up @@ -80,3 +60,16 @@ func CtxWithLoggerAndPrincipal(logger *zap.Logger, store *storage.Store, usernam
ctx = appcontext.WithPrincipal(ctx, princ)
return ctx
}

func CtxWithNewDataloaders(ctx context.Context, store *storage.Store) context.Context {
getCedarSystems := func(ctx context.Context) ([]*models.CedarSystem, error) {
return cedarcoremock.GetActiveSystems(), nil
}

buildDataloaders := func() *dataloaders.Dataloaders {
return dataloaders.NewDataloaders(store, local.NewOktaAPIClient().FetchUserInfos, getCedarSystems)
}

// Set up mocked dataloaders for the test context
return dataloaders.CTXWithLoaders(ctx, buildDataloaders)
}
8 changes: 4 additions & 4 deletions cmd/devdata/trb_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,7 @@ func (s *seederConfig) seedTRBWithAttendees(ctx context.Context, trbRequestID uu
_, err := s.addAttendee(
ctx,
trbRequestID,
"USR1",
mock.Batman,
models.PersonRoleInformationSystemSecurityAdvisor,
"Security Component",
)
Expand All @@ -691,7 +691,7 @@ func (s *seederConfig) seedTRBWithAttendees(ctx context.Context, trbRequestID uu
_, err = s.addAttendee(
ctx,
trbRequestID,
"TEST",
mock.TestUser,
models.PersonRoleBusinessOwner,
"Business Component",
)
Expand All @@ -701,7 +701,7 @@ func (s *seederConfig) seedTRBWithAttendees(ctx context.Context, trbRequestID uu
_, err = s.addAttendee(
ctx,
trbRequestID,
"A11Y",
mock.AccessibilityUser,
models.PersonRoleCRA,
"Cyber Component",
)
Expand Down Expand Up @@ -845,7 +845,7 @@ func (s *seederConfig) addAdviceLetter(ctx context.Context, trb *models.TRBReque
}
}

letter, outsideErr = resolvers.GetTRBAdviceLetterByTRBRequestID(ctx, s.store, trb.ID)
letter, outsideErr = resolvers.GetTRBAdviceLetterByTRBRequestID(ctx, trb.ID)
if outsideErr != nil {
return nil, outsideErr
}
Expand Down
4 changes: 3 additions & 1 deletion pkg/dataloaders/dataloaders.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ type Dataloaders struct {
SystemIntakeRelatedSystemIntakes *dataloadgen.Loader[uuid.UUID, []*models.SystemIntake]
SystemIntakeRelatedTRBRequests *dataloadgen.Loader[uuid.UUID, []*models.TRBRequest]
SystemIntakeSystems *dataloadgen.Loader[uuid.UUID, []*models.SystemIntakeSystem]
TRBRequestAdviceLetter *dataloadgen.Loader[uuid.UUID, *models.TRBAdviceLetter]
TRBRequestContractNumbers *dataloadgen.Loader[uuid.UUID, []*models.TRBRequestContractNumber]
TRBRequestForm *dataloadgen.Loader[uuid.UUID, *models.TRBRequestForm]
TRBRequestFeedback *dataloadgen.Loader[uuid.UUID, []*models.TRBRequestFeedback]
Expand Down Expand Up @@ -109,10 +110,11 @@ func NewDataloaders(store *storage.Store, fetchUserInfos fetchUserInfosFunc, get
SystemIntakeRelatedSystemIntakes: dataloadgen.NewLoader(dr.batchRelatedSystemIntakesBySystemIntakeIDs),
SystemIntakeRelatedTRBRequests: dataloadgen.NewLoader(dr.batchRelatedTRBRequestsBySystemIntakeIDs),
SystemIntakeSystems: dataloadgen.NewLoader(dr.batchSystemIntakeSystemsBySystemIntakeIDs),
TRBRequestAdviceLetter: dataloadgen.NewLoader(dr.batchTRBRequestAdviceLettersByTRBRequestIDs),
TRBRequestContractNumbers: dataloadgen.NewLoader(dr.batchTRBRequestContractNumbersByTRBRequestIDs),
TRBRequestForm: dataloadgen.NewLoader(dr.batchTRBRequestFormsByTRBRequestIDs),
TRBRequestFeedback: dataloadgen.NewLoader(dr.batchTRBRequestFeedbackByTRBRequestIDs),
TRBRequestFeedbackNewest: dataloadgen.NewLoader(dr.batchTRBRequestNewestFeedbackByTRBRequestIDs),
TRBRequestContractNumbers: dataloadgen.NewLoader(dr.batchTRBRequestContractNumbersByTRBRequestIDs),
TRBRequestRelatedSystemIntakes: dataloadgen.NewLoader(dr.batchRelatedSystemIntakesByTRBRequestIDs),
TRBRequestRelatedTRBRequests: dataloadgen.NewLoader(dr.batchRelatedTRBRequestsByTRBRequestIDs),
TRBRequestSystems: dataloadgen.NewLoader(dr.batchTRBRequestSystemsByTRBRequestIDs),
Expand Down
29 changes: 29 additions & 0 deletions pkg/dataloaders/trb_request_advice_letter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package dataloaders

import (
"context"
"errors"

"github.com/google/uuid"

"github.com/cms-enterprise/easi-app/pkg/helpers"
"github.com/cms-enterprise/easi-app/pkg/models"
)

func (d *dataReader) batchTRBRequestAdviceLettersByTRBRequestIDs(ctx context.Context, trbRequestIDs []uuid.UUID) ([]*models.TRBAdviceLetter, []error) {
data, err := d.db.GetTRBAdviceLettersByTRBRequestIDs(ctx, trbRequestIDs)
if err != nil {
return nil, []error{err}
}

return helpers.OneToOne(trbRequestIDs, data), nil
}

func GetTRBAdviceLetterByTRBRequestID(ctx context.Context, trbRequestID uuid.UUID) (*models.TRBAdviceLetter, error) {
loaders, ok := loadersFromCTX(ctx)
if !ok {
return nil, errors.New("unexpected nil loaders in GetTRBRequestContractNumbersByTRBRequestID")
}

return loaders.TRBRequestAdviceLetter.Load(ctx, trbRequestID)
}
5 changes: 3 additions & 2 deletions pkg/graph/resolvers/trb_advice_letter.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import (
"golang.org/x/sync/errgroup"

"github.com/cms-enterprise/easi-app/pkg/appcontext"
"github.com/cms-enterprise/easi-app/pkg/dataloaders"
"github.com/cms-enterprise/easi-app/pkg/email"
"github.com/cms-enterprise/easi-app/pkg/models"
"github.com/cms-enterprise/easi-app/pkg/storage"
)

// GetTRBAdviceLetterByTRBRequestID fetches a TRB advice letter record by its associated request's ID.
func GetTRBAdviceLetterByTRBRequestID(ctx context.Context, store *storage.Store, id uuid.UUID) (*models.TRBAdviceLetter, error) {
return store.GetTRBAdviceLetterByTRBRequestID(ctx, id)
func GetTRBAdviceLetterByTRBRequestID(ctx context.Context, id uuid.UUID) (*models.TRBAdviceLetter, error) {
return dataloaders.GetTRBAdviceLetterByTRBRequestID(ctx, id)
}

// CreateTRBAdviceLetter creates an advice letter for a TRB request, in the "In Progress" status, when the advice letter is ready to be worked on.
Expand Down
2 changes: 1 addition & 1 deletion pkg/graph/resolvers/trb_request_feedback.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ func CreateTRBRequestFeedback(
}

// GetTRBRequestFeedbackByTRBRequestID retrieves TRB request feedback records for a given TRB request ID
func GetTRBRequestFeedbackByTRBRequestID(ctx context.Context, store *storage.Store, id uuid.UUID) ([]*models.TRBRequestFeedback, error) {
func GetTRBRequestFeedbackByTRBRequestID(ctx context.Context, id uuid.UUID) ([]*models.TRBRequestFeedback, error) {
results, err := dataloaders.GetTRBRequestFeedbackByTRBRequestID(ctx, id)
if err != nil {
return nil, err
Expand Down
4 changes: 2 additions & 2 deletions pkg/graph/resolvers/trb_request_feedback_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func (s *ResolverSuite) TestCreateTRBRequestFeedback() {
s.NoError(err)
s.Nil(latestFeedback)

form, err := GetTRBRequestFormByTRBRequestID(s.ctxWithNewDataloaders(), store, trbRequest.ID)
form, err := GetTRBRequestFormByTRBRequestID(s.ctxWithNewDataloaders(), trbRequest.ID)
s.NoError(err)

s.Run("create/update/fetch TRB request feedback", func() {
Expand Down Expand Up @@ -127,7 +127,7 @@ func (s *ResolverSuite) TestCreateTRBRequestFeedback() {
updatedFeedbackStatus, err := getTRBFeedbackStatus(s.ctxWithNewDataloaders(), trbRequest.ID)
s.NoError(err)
s.EqualValues(models.TRBFeedbackStatusEditsRequested, *updatedFeedbackStatus)
form, err := GetTRBRequestFormByTRBRequestID(s.ctxWithNewDataloaders(), store, trbRequest.ID)
form, err := GetTRBRequestFormByTRBRequestID(s.ctxWithNewDataloaders(), trbRequest.ID)
s.NoError(err)
s.EqualValues(form.Status, models.TRBFormStatusInProgress)
s.EqualValues(models.TRBFormStatusInProgress, form.Status)
Expand Down
2 changes: 1 addition & 1 deletion pkg/graph/resolvers/trb_request_form.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ func UpdateTRBRequestForm(
}

// GetTRBRequestFormByTRBRequestID retrieves a TRB request form record for a given TRB request ID
func GetTRBRequestFormByTRBRequestID(ctx context.Context, store *storage.Store, id uuid.UUID) (*models.TRBRequestForm, error) {
func GetTRBRequestFormByTRBRequestID(ctx context.Context, id uuid.UUID) (*models.TRBRequestForm, error) {
form, err := dataloaders.GetTRBRequestFormByTRBRequestID(ctx, id)
if err != nil {
return nil, err
Expand Down
2 changes: 1 addition & 1 deletion pkg/graph/resolvers/trb_request_form_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func (s *ResolverSuite) TestCreateTRBRequestForm() {

s.Run("create/update/fetch TRB request forms", func() {
// fetch the form
fetched, err := GetTRBRequestFormByTRBRequestID(s.ctxWithNewDataloaders(), s.testConfigs.Store, trbRequest.ID)
fetched, err := GetTRBRequestFormByTRBRequestID(s.ctxWithNewDataloaders(), trbRequest.ID)
s.NoError(err)
s.NotNil(fetched)

Expand Down
107 changes: 56 additions & 51 deletions pkg/graph/resolvers/trb_request_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ package resolvers

import (
"context"
"fmt"
"time"

"github.com/google/uuid"
"golang.org/x/sync/errgroup"

"github.com/cms-enterprise/easi-app/pkg/dataloaders"
"github.com/cms-enterprise/easi-app/pkg/models"
"github.com/cms-enterprise/easi-app/pkg/storage"
)

func getTRBFormStatus(ctx context.Context, trbRequestID uuid.UUID) (*models.TRBFormStatus, error) {
Expand Down Expand Up @@ -62,6 +61,9 @@ func getTRBConsultPrepStatus(ctx context.Context, trbRequest models.TRBRequest)
if err != nil {
return nil, err
}
if feedbackStatus == nil {
return nil, fmt.Errorf("feedback status is nil for trb request %v", trbRequest.ID)
}

if *feedbackStatus == models.TRBFeedbackStatusCompleted {
if trbRequest.ConsultMeetingTime != nil && time.Now().After(*trbRequest.ConsultMeetingTime) {
Expand All @@ -79,6 +81,9 @@ func getTRBAttendConsultStatus(ctx context.Context, trbRequest models.TRBRequest
if err != nil {
return nil, err
}
if feedbackStatus == nil {
return nil, fmt.Errorf("feedback status is nil for trb request %v", trbRequest.ID)
}

if *feedbackStatus == models.TRBFeedbackStatusCompleted {
if trbRequest.ConsultMeetingTime != nil {
Expand All @@ -94,10 +99,10 @@ func getTRBAttendConsultStatus(ctx context.Context, trbRequest models.TRBRequest
return &status, nil
}

func getTRBAdviceLetterStatus(ctx context.Context, store *storage.Store, trbRequest models.TRBRequest) (*models.TRBAdviceLetterStatus, error) {
func getTRBAdviceLetterStatus(ctx context.Context, trbRequest models.TRBRequest) (*models.TRBAdviceLetterStatus, error) {
var status models.TRBAdviceLetterStatus

letter, err := store.GetTRBAdviceLetterByTRBRequestID(ctx, trbRequest.ID)
letter, err := dataloaders.GetTRBAdviceLetterByTRBRequestID(ctx, trbRequest.ID)
if err != nil {
return nil, err
}
Expand All @@ -117,53 +122,53 @@ func getTRBAdviceLetterStatus(ctx context.Context, store *storage.Store, trbRequ
}

// GetTRBTaskStatuses retrieves all of the statuses for the steps of a given TRB request's task list
func GetTRBTaskStatuses(ctx context.Context, store *storage.Store, trbRequest models.TRBRequest) (*models.TRBTaskStatuses, error) {
errGroup := new(errgroup.Group)

var formStatus *models.TRBFormStatus
var errForm error
errGroup.Go(func() error {
formStatus, errForm = getTRBFormStatus(ctx, trbRequest.ID)
return errForm
})

var feedbackStatus *models.TRBFeedbackStatus
var errFeedback error
errGroup.Go(func() error {
feedbackStatus, errFeedback = getTRBFeedbackStatus(ctx, trbRequest.ID)
return errFeedback
})

var consultPrepStatus *models.TRBConsultPrepStatus
var errConsultPrep error
errGroup.Go(func() error {
consultPrepStatus, errConsultPrep = getTRBConsultPrepStatus(ctx, trbRequest)
return errConsultPrep
})

var attendConsultStatus *models.TRBAttendConsultStatus
var errAttendConsult error
errGroup.Go(func() error {
attendConsultStatus, errAttendConsult = getTRBAttendConsultStatus(ctx, trbRequest)
return errAttendConsult
})

var adviceLetterStatus *models.TRBAdviceLetterStatus
adviceLetterStatusTaskList := models.TRBAdviceLetterStatusTaskListInReview
var errAdviceLetter error
errGroup.Go(func() error {
adviceLetterStatus, errAdviceLetter = getTRBAdviceLetterStatus(ctx, store, trbRequest)
if *adviceLetterStatus == models.TRBAdviceLetterStatusCannotStartYet {
adviceLetterStatusTaskList = models.TRBAdviceLetterStatusTaskListCannotStartYet
} else if *adviceLetterStatus == models.TRBAdviceLetterStatusCompleted {
adviceLetterStatusTaskList = models.TRBAdviceLetterStatusTaskListCompleted
}
return errAdviceLetter
})
func GetTRBTaskStatuses(ctx context.Context, trbRequest models.TRBRequest) (*models.TRBTaskStatuses, error) {
formStatus, err := getTRBFormStatus(ctx, trbRequest.ID)
if err != nil {
return nil, err
}
if formStatus == nil {
return nil, fmt.Errorf("form status is nil for trb request %v", trbRequest.ID)
}

if err := errGroup.Wait(); err != nil {
feedbackStatus, err := getTRBFeedbackStatus(ctx, trbRequest.ID)
if err != nil {
return nil, err
}
if feedbackStatus == nil {
return nil, fmt.Errorf("feedback status is nil for trb request %v", trbRequest.ID)
}

consultPrepStatus, err := getTRBConsultPrepStatus(ctx, trbRequest)
if err != nil {
return nil, err
}
if consultPrepStatus == nil {
return nil, fmt.Errorf("consult prep status is nil for trb request %v", trbRequest.ID)
}

attendConsultStatus, err := getTRBAttendConsultStatus(ctx, trbRequest)
if err != nil {
return nil, err
}
if attendConsultStatus == nil {
return nil, fmt.Errorf("attend consult status is nil for trb request %v", trbRequest.ID)
}

adviceLetterStatus, err := getTRBAdviceLetterStatus(ctx, trbRequest)
if err != nil {
return nil, err
}
if adviceLetterStatus == nil {
return nil, fmt.Errorf("advice letter status is nil for trb request %v", trbRequest.ID)
}

adviceLetterStatusTaskList := models.TRBAdviceLetterStatusTaskListInReview
if *adviceLetterStatus == models.TRBAdviceLetterStatusCannotStartYet {
adviceLetterStatusTaskList = models.TRBAdviceLetterStatusTaskListCannotStartYet
} else if *adviceLetterStatus == models.TRBAdviceLetterStatusCompleted {
adviceLetterStatusTaskList = models.TRBAdviceLetterStatusTaskListCompleted
}

statuses := models.TRBTaskStatuses{
FormStatus: *formStatus,
Expand All @@ -178,11 +183,11 @@ func GetTRBTaskStatuses(ctx context.Context, store *storage.Store, trbRequest mo
}

// GetTRBRequestStatus calculates the overall status of the TRB request
func GetTRBRequestStatus(ctx context.Context, store *storage.Store, trbRequest models.TRBRequest) (models.TRBRequestStatus, error) {
func GetTRBRequestStatus(ctx context.Context, trbRequest models.TRBRequest) (models.TRBRequestStatus, error) {
var status models.TRBRequestStatus
status = models.TRBRequestStatusNew

taskStatuses, err := GetTRBTaskStatuses(ctx, store, trbRequest)
taskStatuses, err := GetTRBTaskStatuses(ctx, trbRequest)
if err != nil {
return status, err
}
Expand Down Expand Up @@ -236,7 +241,7 @@ func GetTRBRequestStatus(ctx context.Context, store *storage.Store, trbRequest m
// Advice letter sent
if adviceLetterStatus == models.TRBAdviceLetterStatusCompleted {
// Get the advice letter and check if follow-up was recommended
adviceLetter, err := GetTRBAdviceLetterByTRBRequestID(ctx, store, trbRequest.ID)
adviceLetter, err := GetTRBAdviceLetterByTRBRequestID(ctx, trbRequest.ID)
if err != nil {
return status, err
}
Expand Down
Loading

0 comments on commit 08b3419

Please sign in to comment.