Skip to content

Commit

Permalink
MLPAB-1639 Send attorneys to the lpa store on signing (#949)
Browse files Browse the repository at this point in the history
  • Loading branch information
hawx authored Jan 8, 2024
1 parent 13e5093 commit 9a0289c
Show file tree
Hide file tree
Showing 21 changed files with 563 additions and 207 deletions.
10 changes: 7 additions & 3 deletions internal/actor/attorney.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func (as Attorneys) Addresses() []place.Address {
}

func (as Attorneys) Get(id string) (Attorney, bool) {
idx := slices.IndexFunc(as.Attorneys, func(a Attorney) bool { return a.ID == id })
idx := as.Index(id)
if idx == -1 {
return Attorney{}, false
}
Expand All @@ -93,7 +93,7 @@ func (as Attorneys) Get(id string) (Attorney, bool) {
}

func (as *Attorneys) Put(attorney Attorney) {
idx := slices.IndexFunc(as.Attorneys, func(a Attorney) bool { return a.ID == attorney.ID })
idx := as.Index(attorney.ID)
if idx == -1 {
as.Attorneys = append(as.Attorneys, attorney)
} else {
Expand All @@ -102,7 +102,7 @@ func (as *Attorneys) Put(attorney Attorney) {
}

func (as *Attorneys) Delete(attorney Attorney) bool {
idx := slices.IndexFunc(as.Attorneys, func(a Attorney) bool { return a.ID == attorney.ID })
idx := as.Index(attorney.ID)
if idx == -1 {
return false
}
Expand All @@ -111,6 +111,10 @@ func (as *Attorneys) Delete(attorney Attorney) bool {
return true
}

func (as *Attorneys) Index(id string) int {
return slices.IndexFunc(as.Attorneys, func(a Attorney) bool { return a.ID == id })
}

func (as Attorneys) FullNames() []string {
var names []string

Expand Down
1 change: 1 addition & 0 deletions internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ func App(
errorHandler,
notFoundHandler,
dashboardStore,
lpaStoreClient,
)

donor.Register(
Expand Down
49 changes: 48 additions & 1 deletion internal/lpastore/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,12 +262,59 @@ func (c *Client) SendCertificateProvider(ctx context.Context, lpaUID string, cer
body.Changes = append(body.Changes, updateRequestChange{Key: "/certificateProvider/address/country", New: certificateProvider.HomeAddress.Country})
}

return c.sendUpdate(ctx, lpaUID, body)
}

func (c *Client) SendAttorney(ctx context.Context, donor *actor.DonorProvidedDetails, attorney *actor.AttorneyProvidedDetails) error {
var attorneyKey string
if attorney.IsTrustCorporation && attorney.IsReplacement {
attorneyKey = "/trustCorporations/1"
} else if attorney.IsTrustCorporation {
attorneyKey = "/trustCorporations/0"
} else if attorney.IsReplacement {
attorneyKey = fmt.Sprintf("/attorneys/%d", len(donor.Attorneys.Attorneys)+donor.ReplacementAttorneys.Index(attorney.ID))
} else {
attorneyKey = fmt.Sprintf("/attorneys/%d", donor.Attorneys.Index(attorney.ID))
}

body := updateRequest{
Type: "ATTORNEY_SIGN",
Changes: []updateRequestChange{
{Key: attorneyKey + "/mobile", New: attorney.Mobile},
{Key: attorneyKey + "/contactLanguagePreference", New: attorney.ContactLanguagePreference.String()},
},
}

if attorney.IsTrustCorporation {
body.Changes = append(body.Changes,
updateRequestChange{Key: attorneyKey + "/signatories/0/firstNames", New: attorney.AuthorisedSignatories[0].FirstNames},
updateRequestChange{Key: attorneyKey + "/signatories/0/lastName", New: attorney.AuthorisedSignatories[0].LastName},
updateRequestChange{Key: attorneyKey + "/signatories/0/professionalTitle", New: attorney.AuthorisedSignatories[0].ProfessionalTitle},
updateRequestChange{Key: attorneyKey + "/signatories/0/signedAt", New: attorney.AuthorisedSignatories[0].Confirmed},
)

if !attorney.AuthorisedSignatories[1].Confirmed.IsZero() {
body.Changes = append(body.Changes,
updateRequestChange{Key: attorneyKey + "/signatories/1/firstNames", New: attorney.AuthorisedSignatories[1].FirstNames},
updateRequestChange{Key: attorneyKey + "/signatories/1/lastName", New: attorney.AuthorisedSignatories[1].LastName},
updateRequestChange{Key: attorneyKey + "/signatories/1/professionalTitle", New: attorney.AuthorisedSignatories[1].ProfessionalTitle},
updateRequestChange{Key: attorneyKey + "/signatories/1/signedAt", New: attorney.AuthorisedSignatories[1].Confirmed},
)
}
} else {
body.Changes = append(body.Changes, updateRequestChange{Key: attorneyKey + "/signedAt", New: attorney.Confirmed})
}

return c.sendUpdate(ctx, donor.LpaUID, body)
}

func (c *Client) sendUpdate(ctx context.Context, uid string, body updateRequest) error {
var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(body); err != nil {
return err
}

req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.baseURL+"/lpas/"+lpaUID+"/updates", &buf)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.baseURL+"/lpas/"+uid+"/updates", &buf)
if err != nil {
return err
}
Expand Down
145 changes: 136 additions & 9 deletions internal/lpastore/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,131 @@ func TestClientSendCertificateProvider(t *testing.T) {
assert.Nil(t, err)
}

func TestClientSendAttorney(t *testing.T) {
testcases := map[string]struct {
attorney *actor.AttorneyProvidedDetails
donor *actor.DonorProvidedDetails
json string
}{
"attorney": {
attorney: &actor.AttorneyProvidedDetails{
ID: "xyz",
Mobile: "07777",
Confirmed: time.Date(2000, time.January, 2, 3, 4, 5, 6, time.UTC),
ContactLanguagePreference: localize.Cy,
},
donor: &actor.DonorProvidedDetails{
LpaUID: "lpa-uid",
Attorneys: actor.Attorneys{
Attorneys: []actor.Attorney{
{ID: "abc"}, {ID: "xyz"},
},
},
},
json: `{"type":"ATTORNEY_SIGN","changes":[{"key":"/attorneys/1/mobile","old":null,"new":"07777"},{"key":"/attorneys/1/contactLanguagePreference","old":null,"new":"cy"},{"key":"/attorneys/1/signedAt","old":null,"new":"2000-01-02T03:04:05.000000006Z"}]}`,
},
"replacement attorney": {
attorney: &actor.AttorneyProvidedDetails{
ID: "xyz",
IsReplacement: true,
Mobile: "07777",
Confirmed: time.Date(2000, time.January, 2, 3, 4, 5, 6, time.UTC),
ContactLanguagePreference: localize.Cy,
},
donor: &actor.DonorProvidedDetails{
LpaUID: "lpa-uid",
Attorneys: actor.Attorneys{
Attorneys: []actor.Attorney{
{ID: "abc"}, {ID: "xyz"},
},
},
ReplacementAttorneys: actor.Attorneys{
Attorneys: []actor.Attorney{
{ID: "abc"}, {ID: "xyz"},
},
},
},
json: `{"type":"ATTORNEY_SIGN","changes":[{"key":"/attorneys/3/mobile","old":null,"new":"07777"},{"key":"/attorneys/3/contactLanguagePreference","old":null,"new":"cy"},{"key":"/attorneys/3/signedAt","old":null,"new":"2000-01-02T03:04:05.000000006Z"}]}`,
},
"trust corporation": {
attorney: &actor.AttorneyProvidedDetails{
ID: "xyz",
IsTrustCorporation: true,
Mobile: "07777",
AuthorisedSignatories: [2]actor.TrustCorporationSignatory{{
FirstNames: "John",
LastName: "Signer",
ProfessionalTitle: "Director",
Confirmed: time.Date(2000, time.January, 2, 3, 4, 5, 6, time.UTC),
}, {
FirstNames: "Dave",
LastName: "Signer",
ProfessionalTitle: "Assistant to the Director",
Confirmed: time.Date(2000, time.January, 2, 3, 4, 5, 7, time.UTC),
}},
ContactLanguagePreference: localize.En,
},
donor: &actor.DonorProvidedDetails{
LpaUID: "lpa-uid",
},
json: `{"type":"ATTORNEY_SIGN","changes":[{"key":"/trustCorporations/0/mobile","old":null,"new":"07777"},{"key":"/trustCorporations/0/contactLanguagePreference","old":null,"new":"en"},{"key":"/trustCorporations/0/signatories/0/firstNames","old":null,"new":"John"},{"key":"/trustCorporations/0/signatories/0/lastName","old":null,"new":"Signer"},{"key":"/trustCorporations/0/signatories/0/professionalTitle","old":null,"new":"Director"},{"key":"/trustCorporations/0/signatories/0/signedAt","old":null,"new":"2000-01-02T03:04:05.000000006Z"},{"key":"/trustCorporations/0/signatories/1/firstNames","old":null,"new":"Dave"},{"key":"/trustCorporations/0/signatories/1/lastName","old":null,"new":"Signer"},{"key":"/trustCorporations/0/signatories/1/professionalTitle","old":null,"new":"Assistant to the Director"},{"key":"/trustCorporations/0/signatories/1/signedAt","old":null,"new":"2000-01-02T03:04:05.000000007Z"}]}`,
},
"replacement trust corporation": {
attorney: &actor.AttorneyProvidedDetails{
ID: "xyz",
IsTrustCorporation: true,
IsReplacement: true,
Mobile: "07777",
AuthorisedSignatories: [2]actor.TrustCorporationSignatory{{
FirstNames: "John",
LastName: "Signer",
ProfessionalTitle: "Director",
Confirmed: time.Date(2000, time.January, 2, 3, 4, 5, 6, time.UTC),
}},
ContactLanguagePreference: localize.En,
},
donor: &actor.DonorProvidedDetails{
LpaUID: "lpa-uid",
},
json: `{"type":"ATTORNEY_SIGN","changes":[{"key":"/trustCorporations/1/mobile","old":null,"new":"07777"},{"key":"/trustCorporations/1/contactLanguagePreference","old":null,"new":"en"},{"key":"/trustCorporations/1/signatories/0/firstNames","old":null,"new":"John"},{"key":"/trustCorporations/1/signatories/0/lastName","old":null,"new":"Signer"},{"key":"/trustCorporations/1/signatories/0/professionalTitle","old":null,"new":"Director"},{"key":"/trustCorporations/1/signatories/0/signedAt","old":null,"new":"2000-01-02T03:04:05.000000006Z"}]}`,
},
}

for name, tc := range testcases {
t.Run(name, func(t *testing.T) {
ctx := context.Background()

secretsClient := newMockSecretsClient(t)
secretsClient.
On("Secret", ctx, secrets.LpaStoreJwtSecretKey).
Return("secret", nil)

var body []byte
doer := newMockDoer(t)
doer.
On("Do", mock.MatchedBy(func(req *http.Request) bool {
if body == nil {
body, _ = io.ReadAll(req.Body)
}

return assert.Equal(t, ctx, req.Context()) &&
assert.Equal(t, http.MethodPost, req.Method) &&
assert.Equal(t, "http://base/lpas/lpa-uid/updates", req.URL.String()) &&
assert.Equal(t, "application/json", req.Header.Get("Content-Type")) &&
assert.Equal(t, "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJvcGcucG9hcy5tYWtlcmVnaXN0ZXIiLCJzdWIiOiJ0b2RvIiwiaWF0Ijo5NDY3ODIyNDV9.6XFN3faS16wZf0garTwR4NSBxjFrAGKK3I04nK0ItMk", req.Header.Get("X-Jwt-Authorization")) &&
assert.JSONEq(t, tc.json, string(body))
})).
Return(&http.Response{StatusCode: http.StatusCreated, Body: io.NopCloser(strings.NewReader(""))}, nil)

client := New("http://base", secretsClient, doer)
client.now = func() time.Time { return time.Date(2000, time.January, 2, 3, 4, 5, 6, time.UTC) }
err := client.SendAttorney(ctx, tc.donor, tc.attorney)

assert.Nil(t, err)
})
}
}

func TestClientServiceContract(t *testing.T) {
now := func() time.Time { return time.Date(2000, 1, 2, 0, 0, 0, 0, time.UTC) }

Expand Down Expand Up @@ -575,11 +700,11 @@ func TestClientServiceContract(t *testing.T) {
})
})

t.Run("SendCertificateProvider", func(t *testing.T) {
t.Run("sendUpdate", func(t *testing.T) {
pact.
AddInteraction().
Given("The lpa store is available").
UponReceiving("A request to create a certificate provider for a case").
UponReceiving("A request to update the lpa").
WithRequest(dsl.Request{
Method: http.MethodPost,
Path: dsl.String("/lpas/M-0000-1111-2222/updates"),
Expand All @@ -590,10 +715,11 @@ func TestClientServiceContract(t *testing.T) {
"X-Jwt-Authorization": dsl.Regex("Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJvcGcucG9hcy5tYWtlcmVnaXN0ZXIiLCJzdWIiOiJ0b2RvIiwiaWF0Ijo5NDY3NzEyMDB9.teh381oIhucqUD3EhBTaaBTLFI1O2FOWGe-44Ftk0LY", "Bearer .+"),
},
Body: dsl.Like(map[string]any{
"type": dsl.Like("CERTIFICATE_PROVIDER_SIGN"),
"type": dsl.Like("A_TYPE"),
"changes": dsl.EachLike(map[string]any{
"key": dsl.Like("/path"),
"new": dsl.Like("value"),
"key": dsl.Like("/a/key"),
"old": dsl.Like("old"),
"new": dsl.Like("new"),
}, 1),
}),
}).
Expand All @@ -617,10 +743,11 @@ func TestClientServiceContract(t *testing.T) {
now: now,
}

err := client.SendCertificateProvider(context.Background(), "M-0000-1111-2222", &actor.CertificateProviderProvidedDetails{
HomeAddress: address,
Certificate: actor.Certificate{Agreed: now()},
ContactLanguagePreference: localize.Cy,
err := client.sendUpdate(context.Background(), "M-0000-1111-2222", updateRequest{
Type: "A_TYPE",
Changes: []updateRequestChange{
{Key: "/a/key", Old: "old", New: "new"},
},
})
assert.Nil(t, err)
return nil
Expand Down
15 changes: 6 additions & 9 deletions internal/page/attorney/mock_AddressClient_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 6 additions & 21 deletions internal/page/attorney/mock_AttorneyStore_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 9a0289c

Please sign in to comment.