diff --git a/cypress/e2e/donor/add-correspondent.cy.js b/cypress/e2e/donor/add-correspondent.cy.js index 93e0c8076a..3e8c56945a 100644 --- a/cypress/e2e/donor/add-correspondent.cy.js +++ b/cypress/e2e/donor/add-correspondent.cy.js @@ -1,59 +1,75 @@ import { AddressFormAssertions } from "../../support/e2e"; describe('Add correspondent', () => { - beforeEach(() => { - cy.visit('/fixtures?progress=provideYourDetails&redirect='); - }); - - it('allows none', () => { - cy.contains('M-FAKE-').click(); - cy.contains('Go to task list').click(); - cy.contains('li', 'Add a correspondent').should('contain', 'Not started').click(); - - cy.checkA11yApp(); - cy.contains('label', 'No').click(); - cy.contains('button', 'Save and continue').click(); - cy.contains('li', 'Add a correspondent').should('contain', 'Completed'); - }); - - it('allows without address', () => { - cy.contains('M-FAKE-').click(); - cy.contains('Go to task list').click(); - cy.contains('li', 'Add a correspondent').should('contain', 'Not started').click(); - - cy.checkA11yApp(); - cy.contains('label', 'Yes').click(); - cy.contains('button', 'Save and continue').click(); - - cy.checkA11yApp(); - cy.get('#f-first-names').type('John'); - cy.get('#f-last-name').type('Smith'); - cy.get('#f-email').type('email@example.com'); - cy.contains('label', 'No').click(); - cy.contains('button', 'Save and continue').click(); - cy.contains('li', 'Add a correspondent').should('contain', 'Completed'); - }); - - it('allows with address', () => { - cy.contains('M-FAKE-').click(); - cy.contains('Go to task list').click(); - cy.contains('li', 'Add a correspondent').should('contain', 'Not started').click(); - - cy.checkA11yApp(); - cy.contains('label', 'Yes').click(); - cy.contains('button', 'Save and continue').click(); - - cy.checkA11yApp(); - cy.get('#f-first-names').type('John'); - cy.get('#f-last-name').type('Smith'); - cy.get('#f-email').type('email@example.com'); - cy.contains('label', 'Yes').click(); - cy.contains('button', 'Save and continue').click(); - - cy.contains('label', 'Enter a new address').click(); - cy.contains('button', 'Continue').click(); - AddressFormAssertions.assertCanAddAddressFromSelect() - - cy.contains('li', 'Add a correspondent').should('contain', 'Completed'); - }); + beforeEach(() => { + cy.visit('/fixtures?progress=provideYourDetails&redirect='); + }); + + it('allows none', () => { + cy.contains('M-FAKE-').click(); + cy.contains('Go to task list').click(); + cy.contains('li', 'Add a correspondent').should('contain', 'Not started').click(); + + cy.checkA11yApp(); + cy.contains('label', 'No').click(); + cy.contains('button', 'Save and continue').click(); + cy.contains('li', 'Add a correspondent').should('contain', 'Completed'); + }); + + it('allows without address', () => { + cy.contains('M-FAKE-').click(); + cy.contains('Go to task list').click(); + cy.contains('li', 'Add a correspondent').should('contain', 'Not started').click(); + + cy.checkA11yApp(); + cy.contains('label', 'Yes').click(); + cy.contains('button', 'Save and continue').click(); + + cy.checkA11yApp(); + cy.get('#f-first-names').type('John'); + cy.get('#f-last-name').type('Smith'); + cy.get('#f-email').type('email@example.com'); + cy.contains('label', 'No').click(); + cy.contains('button', 'Save and continue').click(); + cy.contains('li', 'Add a correspondent').should('contain', 'Completed'); + + cy.contains('.govuk-summary-list__row', 'Reference number').find('.govuk-summary-list__value') + .invoke('text') + .then((uid) => { + cy.visit(`http://localhost:9001/?detail-type=correspondent-updated&detail=${uid}`); + + cy.contains(`{"uid":"${uid}","firstNames":"John","lastName":"Smith","email":"email@example.com"}`); + }); + }); + + it('allows with address', () => { + cy.contains('M-FAKE-').click(); + cy.contains('Go to task list').click(); + cy.contains('li', 'Add a correspondent').should('contain', 'Not started').click(); + + cy.checkA11yApp(); + cy.contains('label', 'Yes').click(); + cy.contains('button', 'Save and continue').click(); + + cy.checkA11yApp(); + cy.get('#f-first-names').type('John'); + cy.get('#f-last-name').type('Smith'); + cy.get('#f-email').type('email@example.com'); + cy.contains('label', 'Yes').click(); + cy.contains('button', 'Save and continue').click(); + + cy.contains('label', 'Enter a new address').click(); + cy.contains('button', 'Continue').click(); + AddressFormAssertions.assertCanAddAddressFromSelect() + + cy.contains('li', 'Add a correspondent').should('contain', 'Completed'); + + cy.contains('.govuk-summary-list__row', 'Reference number').find('.govuk-summary-list__value') + .invoke('text') + .then((uid) => { + cy.visit(`http://localhost:9001/?detail-type=correspondent-updated&detail=${uid}`); + + cy.contains(`{"uid":"${uid}","firstNames":"John","lastName":"Smith","email":"email@example.com","address":{"line1":"2 RICHMOND PLACE","line2":"","line3":"","town":"BIRMINGHAM","postcode":"B14 7ED","country":"GB"}}`); + }); + }); }); diff --git a/internal/donor/donorpage/add_correspondent.go b/internal/donor/donorpage/add_correspondent.go index 4548fa315e..532953855c 100644 --- a/internal/donor/donorpage/add_correspondent.go +++ b/internal/donor/donorpage/add_correspondent.go @@ -7,6 +7,7 @@ import ( "github.com/ministryofjustice/opg-modernising-lpa/internal/appcontext" "github.com/ministryofjustice/opg-modernising-lpa/internal/donor" "github.com/ministryofjustice/opg-modernising-lpa/internal/donor/donordata" + "github.com/ministryofjustice/opg-modernising-lpa/internal/event" "github.com/ministryofjustice/opg-modernising-lpa/internal/form" "github.com/ministryofjustice/opg-modernising-lpa/internal/task" "github.com/ministryofjustice/opg-modernising-lpa/internal/validation" @@ -19,7 +20,7 @@ type addCorrespondentData struct { Donor *donordata.Provided } -func AddCorrespondent(tmpl template.Template, donorStore DonorStore) Handler { +func AddCorrespondent(tmpl template.Template, donorStore DonorStore, eventClient EventClient) Handler { return func(appData appcontext.Data, w http.ResponseWriter, r *http.Request, provided *donordata.Provided) error { data := &addCorrespondentData{ App: appData, @@ -36,8 +37,17 @@ func AddCorrespondent(tmpl template.Template, donorStore DonorStore) Handler { var redirectUrl donor.Path if provided.AddCorrespondent.IsNo() { + if provided.Correspondent.FirstNames != "" { + if err := eventClient.SendCorrespondentUpdated(r.Context(), event.CorrespondentUpdated{ + UID: provided.LpaUID, + }); err != nil { + return err + } + } + provided.Correspondent = donordata.Correspondent{} provided.Tasks.AddCorrespondent = task.StateCompleted + redirectUrl = donor.PathTaskList } else { if provided.Correspondent.FirstNames == "" { diff --git a/internal/donor/donorpage/add_correspondent_test.go b/internal/donor/donorpage/add_correspondent_test.go index 82fe3590fe..46f2cf78a9 100644 --- a/internal/donor/donorpage/add_correspondent_test.go +++ b/internal/donor/donorpage/add_correspondent_test.go @@ -9,6 +9,7 @@ import ( "github.com/ministryofjustice/opg-modernising-lpa/internal/donor" "github.com/ministryofjustice/opg-modernising-lpa/internal/donor/donordata" + "github.com/ministryofjustice/opg-modernising-lpa/internal/event" "github.com/ministryofjustice/opg-modernising-lpa/internal/form" "github.com/ministryofjustice/opg-modernising-lpa/internal/page" "github.com/ministryofjustice/opg-modernising-lpa/internal/task" @@ -30,7 +31,7 @@ func TestGetAddCorrespondent(t *testing.T) { }). Return(nil) - err := AddCorrespondent(template.Execute, nil)(testAppData, w, r, &donordata.Provided{}) + err := AddCorrespondent(template.Execute, nil, nil)(testAppData, w, r, &donordata.Provided{}) resp := w.Result() assert.Nil(t, err) @@ -50,7 +51,7 @@ func TestGetAddCorrespondentFromStore(t *testing.T) { }). Return(nil) - err := AddCorrespondent(template.Execute, nil)(testAppData, w, r, &donordata.Provided{AddCorrespondent: form.Yes}) + err := AddCorrespondent(template.Execute, nil, nil)(testAppData, w, r, &donordata.Provided{AddCorrespondent: form.Yes}) resp := w.Result() assert.Nil(t, err) @@ -66,7 +67,7 @@ func TestGetAddCorrespondentWhenTemplateErrors(t *testing.T) { Execute(w, mock.Anything). Return(expectedError) - err := AddCorrespondent(template.Execute, nil)(testAppData, w, r, &donordata.Provided{}) + err := AddCorrespondent(template.Execute, nil, nil)(testAppData, w, r, &donordata.Provided{}) resp := w.Result() assert.Equal(t, expectedError, err) @@ -81,6 +82,7 @@ func TestPostAddCorrespondent(t *testing.T) { expectedCorrespondent donordata.Correspondent expectedTaskState task.State redirect donor.Path + setupEventClient func(*mockEventClient) }{ "yes was yes": { yesNo: form.Yes, @@ -107,6 +109,13 @@ func TestPostAddCorrespondent(t *testing.T) { existingTaskState: task.StateCompleted, expectedTaskState: task.StateCompleted, redirect: donor.PathTaskList, + setupEventClient: func(eventClient *mockEventClient) { + eventClient.EXPECT(). + SendCorrespondentUpdated(mock.Anything, event.CorrespondentUpdated{ + UID: "lpa-uid", + }). + Return(nil) + }, }, "no": { yesNo: form.No, @@ -129,14 +138,21 @@ func TestPostAddCorrespondent(t *testing.T) { donorStore.EXPECT(). Put(r.Context(), &donordata.Provided{ LpaID: "lpa-id", + LpaUID: "lpa-uid", AddCorrespondent: tc.yesNo, Correspondent: tc.expectedCorrespondent, Tasks: donordata.Tasks{AddCorrespondent: tc.expectedTaskState}, }). Return(nil) - err := AddCorrespondent(nil, donorStore)(testAppData, w, r, &donordata.Provided{ + eventClient := newMockEventClient(t) + if tc.setupEventClient != nil { + tc.setupEventClient(eventClient) + } + + err := AddCorrespondent(nil, donorStore, eventClient)(testAppData, w, r, &donordata.Provided{ LpaID: "lpa-id", + LpaUID: "lpa-uid", Correspondent: tc.existingCorrespondent, Tasks: donordata.Tasks{AddCorrespondent: tc.existingTaskState}, }) @@ -149,6 +165,29 @@ func TestPostAddCorrespondent(t *testing.T) { } } +func TestPostAddCorrespondentWhenEventClientErrors(t *testing.T) { + f := url.Values{ + form.FieldNames.YesNo: {form.No.String()}, + } + + w := httptest.NewRecorder() + r, _ := http.NewRequest(http.MethodPost, "/", strings.NewReader(f.Encode())) + r.Header.Add("Content-Type", page.FormUrlEncoded) + + eventClient := newMockEventClient(t) + eventClient.EXPECT(). + SendCorrespondentUpdated(mock.Anything, mock.Anything). + Return(expectedError) + + err := AddCorrespondent(nil, nil, eventClient)(testAppData, w, r, &donordata.Provided{ + LpaID: "lpa-id", + LpaUID: "lpa-uid", + Correspondent: donordata.Correspondent{FirstNames: "John"}, + Tasks: donordata.Tasks{AddCorrespondent: task.StateCompleted}, + }) + assert.Equal(t, expectedError, err) +} + func TestPostAddCorrespondentWhenStoreErrors(t *testing.T) { f := url.Values{ form.FieldNames.YesNo: {form.Yes.String()}, @@ -163,7 +202,7 @@ func TestPostAddCorrespondentWhenStoreErrors(t *testing.T) { Put(r.Context(), mock.Anything). Return(expectedError) - err := AddCorrespondent(nil, donorStore)(testAppData, w, r, &donordata.Provided{}) + err := AddCorrespondent(nil, donorStore, nil)(testAppData, w, r, &donordata.Provided{}) assert.Equal(t, expectedError, err) } @@ -180,7 +219,7 @@ func TestPostAddCorrespondentWhenValidationErrors(t *testing.T) { })). Return(nil) - err := AddCorrespondent(template.Execute, nil)(testAppData, w, r, &donordata.Provided{}) + err := AddCorrespondent(template.Execute, nil, nil)(testAppData, w, r, &donordata.Provided{}) resp := w.Result() assert.Nil(t, err) diff --git a/internal/donor/donorpage/enter_correspondent_address.go b/internal/donor/donorpage/enter_correspondent_address.go index e585d30915..91d927ec3d 100644 --- a/internal/donor/donorpage/enter_correspondent_address.go +++ b/internal/donor/donorpage/enter_correspondent_address.go @@ -8,12 +8,13 @@ import ( "github.com/ministryofjustice/opg-modernising-lpa/internal/appcontext" "github.com/ministryofjustice/opg-modernising-lpa/internal/donor" "github.com/ministryofjustice/opg-modernising-lpa/internal/donor/donordata" + "github.com/ministryofjustice/opg-modernising-lpa/internal/event" "github.com/ministryofjustice/opg-modernising-lpa/internal/form" "github.com/ministryofjustice/opg-modernising-lpa/internal/place" "github.com/ministryofjustice/opg-modernising-lpa/internal/task" ) -func EnterCorrespondentAddress(logger Logger, tmpl template.Template, addressClient AddressClient, donorStore DonorStore) Handler { +func EnterCorrespondentAddress(logger Logger, tmpl template.Template, addressClient AddressClient, donorStore DonorStore, eventClient EventClient) Handler { return func(appData appcontext.Data, w http.ResponseWriter, r *http.Request, provided *donordata.Provided) error { data := newChooseAddressData( appData, @@ -35,6 +36,17 @@ func EnterCorrespondentAddress(logger Logger, tmpl template.Template, addressCli provided.Tasks.AddCorrespondent = task.StateCompleted provided.Correspondent.Address = address + if err := eventClient.SendCorrespondentUpdated(r.Context(), event.CorrespondentUpdated{ + UID: provided.LpaUID, + FirstNames: provided.Correspondent.FirstNames, + LastName: provided.Correspondent.LastName, + Email: provided.Correspondent.Email, + Phone: provided.Correspondent.Phone, + Address: &provided.Correspondent.Address, + }); err != nil { + return err + } + if err := donorStore.Put(r.Context(), provided); err != nil { return err } diff --git a/internal/donor/donorpage/enter_correspondent_address_test.go b/internal/donor/donorpage/enter_correspondent_address_test.go index 1e692a8dd8..bec1444795 100644 --- a/internal/donor/donorpage/enter_correspondent_address_test.go +++ b/internal/donor/donorpage/enter_correspondent_address_test.go @@ -10,6 +10,7 @@ import ( "github.com/ministryofjustice/opg-modernising-lpa/internal/donor" "github.com/ministryofjustice/opg-modernising-lpa/internal/donor/donordata" + "github.com/ministryofjustice/opg-modernising-lpa/internal/event" "github.com/ministryofjustice/opg-modernising-lpa/internal/form" "github.com/ministryofjustice/opg-modernising-lpa/internal/page" "github.com/ministryofjustice/opg-modernising-lpa/internal/place" @@ -34,7 +35,7 @@ func TestGetEnterCorrespondentAddress(t *testing.T) { }). Return(nil) - err := EnterCorrespondentAddress(nil, template.Execute, nil, nil)(testAppData, w, r, &donordata.Provided{ + err := EnterCorrespondentAddress(nil, template.Execute, nil, nil, nil)(testAppData, w, r, &donordata.Provided{ Correspondent: donordata.Correspondent{ FirstNames: "John", LastName: "Smith", @@ -67,7 +68,7 @@ func TestGetEnterCorrespondentAddressFromStore(t *testing.T) { }). Return(nil) - err := EnterCorrespondentAddress(nil, template.Execute, nil, nil)(testAppData, w, r, &donordata.Provided{ + err := EnterCorrespondentAddress(nil, template.Execute, nil, nil, nil)(testAppData, w, r, &donordata.Provided{ Correspondent: donordata.Correspondent{ Address: address, }, @@ -97,7 +98,7 @@ func TestGetEnterCorrespondentAddressManual(t *testing.T) { }). Return(nil) - err := EnterCorrespondentAddress(nil, template.Execute, nil, nil)(testAppData, w, r, &donordata.Provided{}) + err := EnterCorrespondentAddress(nil, template.Execute, nil, nil, nil)(testAppData, w, r, &donordata.Provided{}) resp := w.Result() assert.Nil(t, err) @@ -119,7 +120,7 @@ func TestGetEnterCorrespondentAddressWhenTemplateErrors(t *testing.T) { }). Return(expectedError) - err := EnterCorrespondentAddress(nil, template.Execute, nil, nil)(testAppData, w, r, &donordata.Provided{}) + err := EnterCorrespondentAddress(nil, template.Execute, nil, nil, nil)(testAppData, w, r, &donordata.Provided{}) resp := w.Result() assert.Equal(t, expectedError, err) @@ -143,7 +144,8 @@ func TestPostEnterCorrespondentAddressManual(t *testing.T) { donorStore := newMockDonorStore(t) donorStore.EXPECT(). Put(r.Context(), &donordata.Provided{ - LpaID: "lpa-id", + LpaID: "lpa-id", + LpaUID: "lpa-uid", Correspondent: donordata.Correspondent{ Address: testAddress, }, @@ -153,8 +155,17 @@ func TestPostEnterCorrespondentAddressManual(t *testing.T) { }). Return(nil) - err := EnterCorrespondentAddress(nil, nil, nil, donorStore)(testAppData, w, r, &donordata.Provided{ - LpaID: "lpa-id", + eventClient := newMockEventClient(t) + eventClient.EXPECT(). + SendCorrespondentUpdated(r.Context(), event.CorrespondentUpdated{ + UID: "lpa-uid", + Address: &testAddress, + }). + Return(nil) + + err := EnterCorrespondentAddress(nil, nil, nil, donorStore, eventClient)(testAppData, w, r, &donordata.Provided{ + LpaID: "lpa-id", + LpaUID: "lpa-uid", }) resp := w.Result() @@ -163,6 +174,32 @@ func TestPostEnterCorrespondentAddressManual(t *testing.T) { assert.Equal(t, donor.PathTaskList.Format("lpa-id"), resp.Header.Get("Location")) } +func TestPostEnterCorrespondentAddressManualWhenEventClientErrors(t *testing.T) { + f := url.Values{ + form.FieldNames.Address.Action: {"manual"}, + form.FieldNames.Address.Line1: {"a"}, + form.FieldNames.Address.Line2: {"b"}, + form.FieldNames.Address.Line3: {"c"}, + form.FieldNames.Address.TownOrCity: {"d"}, + form.FieldNames.Address.Postcode: {"e"}, + } + + w := httptest.NewRecorder() + r, _ := http.NewRequest(http.MethodPost, "/", strings.NewReader(f.Encode())) + r.Header.Add("Content-Type", page.FormUrlEncoded) + + eventClient := newMockEventClient(t) + eventClient.EXPECT(). + SendCorrespondentUpdated(mock.Anything, mock.Anything). + Return(expectedError) + + err := EnterCorrespondentAddress(nil, nil, nil, nil, eventClient)(testAppData, w, r, &donordata.Provided{ + LpaID: "lpa-id", + LpaUID: "lpa-uid", + }) + assert.Equal(t, expectedError, err) +} + func TestPostEnterCorrespondentAddressManualWhenStoreErrors(t *testing.T) { f := url.Values{ form.FieldNames.Address.Action: {"manual"}, @@ -182,7 +219,12 @@ func TestPostEnterCorrespondentAddressManualWhenStoreErrors(t *testing.T) { Put(r.Context(), mock.Anything). Return(expectedError) - err := EnterCorrespondentAddress(nil, nil, nil, donorStore)(testAppData, w, r, &donordata.Provided{}) + eventClient := newMockEventClient(t) + eventClient.EXPECT(). + SendCorrespondentUpdated(mock.Anything, mock.Anything). + Return(nil) + + err := EnterCorrespondentAddress(nil, nil, nil, donorStore, eventClient)(testAppData, w, r, &donordata.Provided{}) assert.Equal(t, expectedError, err) } @@ -215,7 +257,12 @@ func TestPostEnterCorrespondentAddressManualFromStore(t *testing.T) { }). Return(nil) - err := EnterCorrespondentAddress(nil, nil, nil, donorStore)(testAppData, w, r, &donordata.Provided{ + eventClient := newMockEventClient(t) + eventClient.EXPECT(). + SendCorrespondentUpdated(mock.Anything, mock.Anything). + Return(nil) + + err := EnterCorrespondentAddress(nil, nil, nil, donorStore, eventClient)(testAppData, w, r, &donordata.Provided{ LpaID: "lpa-id", Correspondent: donordata.Correspondent{ FirstNames: "John", @@ -262,7 +309,7 @@ func TestPostEnterCorrespondentAddressManualWhenValidationError(t *testing.T) { }). Return(nil) - err := EnterCorrespondentAddress(nil, template.Execute, nil, nil)(testAppData, w, r, &donordata.Provided{}) + err := EnterCorrespondentAddress(nil, template.Execute, nil, nil, nil)(testAppData, w, r, &donordata.Provided{}) resp := w.Result() assert.Nil(t, err) @@ -303,7 +350,7 @@ func TestPostEnterCorrespondentAddressSelect(t *testing.T) { }). Return(nil) - err := EnterCorrespondentAddress(nil, template.Execute, nil, nil)(testAppData, w, r, &donordata.Provided{}) + err := EnterCorrespondentAddress(nil, template.Execute, nil, nil, nil)(testAppData, w, r, &donordata.Provided{}) resp := w.Result() assert.Nil(t, err) @@ -346,7 +393,7 @@ func TestPostEnterCorrespondentAddressSelectWhenValidationError(t *testing.T) { }). Return(nil) - err := EnterCorrespondentAddress(nil, template.Execute, addressClient, nil)(testAppData, w, r, &donordata.Provided{}) + err := EnterCorrespondentAddress(nil, template.Execute, addressClient, nil, nil)(testAppData, w, r, &donordata.Provided{}) resp := w.Result() assert.Nil(t, err) @@ -388,7 +435,7 @@ func TestPostEnterCorrespondentAddressLookup(t *testing.T) { }). Return(nil) - err := EnterCorrespondentAddress(nil, template.Execute, addressClient, nil)(testAppData, w, r, &donordata.Provided{}) + err := EnterCorrespondentAddress(nil, template.Execute, addressClient, nil, nil)(testAppData, w, r, &donordata.Provided{}) resp := w.Result() assert.Nil(t, err) @@ -431,7 +478,7 @@ func TestPostEnterCorrespondentAddressLookupError(t *testing.T) { }). Return(nil) - err := EnterCorrespondentAddress(logger, template.Execute, addressClient, nil)(testAppData, w, r, &donordata.Provided{}) + err := EnterCorrespondentAddress(logger, template.Execute, addressClient, nil, nil)(testAppData, w, r, &donordata.Provided{}) resp := w.Result() assert.Nil(t, err) @@ -479,7 +526,7 @@ func TestPostEnterCorrespondentAddressInvalidPostcodeError(t *testing.T) { }). Return(nil) - err := EnterCorrespondentAddress(logger, template.Execute, addressClient, nil)(testAppData, w, r, &donordata.Provided{}) + err := EnterCorrespondentAddress(logger, template.Execute, addressClient, nil, nil)(testAppData, w, r, &donordata.Provided{}) resp := w.Result() assert.Nil(t, err) @@ -521,7 +568,7 @@ func TestPostEnterCorrespondentAddressValidPostcodeNoAddresses(t *testing.T) { }). Return(nil) - err := EnterCorrespondentAddress(logger, template.Execute, addressClient, nil)(testAppData, w, r, &donordata.Provided{}) + err := EnterCorrespondentAddress(logger, template.Execute, addressClient, nil, nil)(testAppData, w, r, &donordata.Provided{}) resp := w.Result() assert.Nil(t, err) @@ -552,7 +599,7 @@ func TestPostEnterCorrespondentAddressLookupWhenValidationError(t *testing.T) { }). Return(nil) - err := EnterCorrespondentAddress(nil, template.Execute, nil, nil)(testAppData, w, r, &donordata.Provided{}) + err := EnterCorrespondentAddress(nil, template.Execute, nil, nil, nil)(testAppData, w, r, &donordata.Provided{}) resp := w.Result() assert.Nil(t, err) @@ -583,7 +630,7 @@ func TestPostEnterCorrespondentAddressReuse(t *testing.T) { }). Return(nil) - err := EnterCorrespondentAddress(nil, template.Execute, nil, nil)(testAppData, w, r, &donordata.Provided{ + err := EnterCorrespondentAddress(nil, template.Execute, nil, nil, nil)(testAppData, w, r, &donordata.Provided{ Donor: donordata.Donor{Address: place.Address{Line1: "donor lane"}}, }) resp := w.Result() @@ -622,7 +669,12 @@ func TestPostEnterCorrespondentAddressReuseSelect(t *testing.T) { }). Return(nil) - err := EnterCorrespondentAddress(nil, nil, nil, donorStore)(testAppData, w, r, &donordata.Provided{ + eventClient := newMockEventClient(t) + eventClient.EXPECT(). + SendCorrespondentUpdated(mock.Anything, mock.Anything). + Return(nil) + + err := EnterCorrespondentAddress(nil, nil, nil, donorStore, eventClient)(testAppData, w, r, &donordata.Provided{ LpaID: "lpa-id", }) resp := w.Result() @@ -647,7 +699,12 @@ func TestPostEnterCorrespondentAddressReuseSelectWhenError(t *testing.T) { Put(r.Context(), mock.Anything). Return(expectedError) - err := EnterCorrespondentAddress(nil, nil, nil, donorStore)(testAppData, w, r, &donordata.Provided{ + eventClient := newMockEventClient(t) + eventClient.EXPECT(). + SendCorrespondentUpdated(mock.Anything, mock.Anything). + Return(nil) + + err := EnterCorrespondentAddress(nil, nil, nil, donorStore, eventClient)(testAppData, w, r, &donordata.Provided{ LpaID: "lpa-id", }) @@ -679,7 +736,7 @@ func TestPostEnterCorrespondentAddressReuseSelectWhenValidationError(t *testing. }). Return(nil) - err := EnterCorrespondentAddress(nil, template.Execute, nil, nil)(testAppData, w, r, &donordata.Provided{ + err := EnterCorrespondentAddress(nil, template.Execute, nil, nil, nil)(testAppData, w, r, &donordata.Provided{ Donor: donordata.Donor{Address: place.Address{Line1: "donor lane"}}, }) resp := w.Result() diff --git a/internal/donor/donorpage/enter_correspondent_details.go b/internal/donor/donorpage/enter_correspondent_details.go index d060e55627..7f9e4902eb 100644 --- a/internal/donor/donorpage/enter_correspondent_details.go +++ b/internal/donor/donorpage/enter_correspondent_details.go @@ -8,6 +8,7 @@ import ( "github.com/ministryofjustice/opg-modernising-lpa/internal/appcontext" "github.com/ministryofjustice/opg-modernising-lpa/internal/donor" "github.com/ministryofjustice/opg-modernising-lpa/internal/donor/donordata" + "github.com/ministryofjustice/opg-modernising-lpa/internal/event" "github.com/ministryofjustice/opg-modernising-lpa/internal/form" "github.com/ministryofjustice/opg-modernising-lpa/internal/page" "github.com/ministryofjustice/opg-modernising-lpa/internal/place" @@ -22,7 +23,7 @@ type enterCorrespondentDetailsData struct { NameWarning *actor.SameNameWarning } -func EnterCorrespondentDetails(tmpl template.Template, donorStore DonorStore) Handler { +func EnterCorrespondentDetails(tmpl template.Template, donorStore DonorStore, eventClient EventClient) Handler { return func(appData appcontext.Data, w http.ResponseWriter, r *http.Request, provided *donordata.Provided) error { data := &enterCorrespondentDetailsData{ App: appData, @@ -52,6 +53,17 @@ func EnterCorrespondentDetails(tmpl template.Template, donorStore DonorStore) Ha if provided.Correspondent.WantAddress.IsNo() { provided.Correspondent.Address = place.Address{} provided.Tasks.AddCorrespondent = task.StateCompleted + + if err := eventClient.SendCorrespondentUpdated(r.Context(), event.CorrespondentUpdated{ + UID: provided.LpaUID, + FirstNames: provided.Correspondent.FirstNames, + LastName: provided.Correspondent.LastName, + Email: provided.Correspondent.Email, + Phone: provided.Correspondent.Phone, + }); err != nil { + return err + } + redirect = donor.PathTaskList } else { if !provided.Tasks.AddCorrespondent.IsCompleted() && provided.Correspondent.Address.Line1 == "" { diff --git a/internal/donor/donorpage/enter_correspondent_details_test.go b/internal/donor/donorpage/enter_correspondent_details_test.go index b1a408e8cb..8a10b72233 100644 --- a/internal/donor/donorpage/enter_correspondent_details_test.go +++ b/internal/donor/donorpage/enter_correspondent_details_test.go @@ -9,6 +9,7 @@ import ( "github.com/ministryofjustice/opg-modernising-lpa/internal/donor" "github.com/ministryofjustice/opg-modernising-lpa/internal/donor/donordata" + "github.com/ministryofjustice/opg-modernising-lpa/internal/event" "github.com/ministryofjustice/opg-modernising-lpa/internal/form" "github.com/ministryofjustice/opg-modernising-lpa/internal/page" "github.com/ministryofjustice/opg-modernising-lpa/internal/place" @@ -30,7 +31,7 @@ func TestGetEnterCorrespondentDetails(t *testing.T) { }). Return(nil) - err := EnterCorrespondentDetails(template.Execute, nil)(testAppData, w, r, &donordata.Provided{}) + err := EnterCorrespondentDetails(template.Execute, nil, nil)(testAppData, w, r, &donordata.Provided{}) resp := w.Result() assert.Nil(t, err) @@ -52,7 +53,7 @@ func TestGetEnterCorrespondentDetailsFromStore(t *testing.T) { }). Return(nil) - err := EnterCorrespondentDetails(template.Execute, nil)(testAppData, w, r, &donordata.Provided{ + err := EnterCorrespondentDetails(template.Execute, nil, nil)(testAppData, w, r, &donordata.Provided{ Correspondent: donordata.Correspondent{ FirstNames: "John", }, @@ -72,7 +73,7 @@ func TestGetEnterCorrespondentDetailsWhenTemplateErrors(t *testing.T) { Execute(w, mock.Anything). Return(expectedError) - err := EnterCorrespondentDetails(template.Execute, nil)(testAppData, w, r, &donordata.Provided{}) + err := EnterCorrespondentDetails(template.Execute, nil, nil)(testAppData, w, r, &donordata.Provided{}) resp := w.Result() assert.Equal(t, expectedError, err) @@ -94,8 +95,9 @@ func TestPostEnterCorrespondentDetails(t *testing.T) { donorStore := newMockDonorStore(t) donorStore.EXPECT(). Put(r.Context(), &donordata.Provided{ - LpaID: "lpa-id", - Donor: donordata.Donor{FirstNames: "John", LastName: "Smith"}, + LpaID: "lpa-id", + LpaUID: "lpa-uid", + Donor: donordata.Donor{FirstNames: "John", LastName: "Smith"}, Correspondent: donordata.Correspondent{ FirstNames: "John", LastName: "Doe", @@ -106,9 +108,20 @@ func TestPostEnterCorrespondentDetails(t *testing.T) { }). Return(nil) - err := EnterCorrespondentDetails(nil, donorStore)(testAppData, w, r, &donordata.Provided{ - LpaID: "lpa-id", - Donor: donordata.Donor{FirstNames: "John", LastName: "Smith"}, + eventClient := newMockEventClient(t) + eventClient.EXPECT(). + SendCorrespondentUpdated(r.Context(), event.CorrespondentUpdated{ + UID: "lpa-uid", + FirstNames: "John", + LastName: "Doe", + Email: "email@example.com", + }). + Return(nil) + + err := EnterCorrespondentDetails(nil, donorStore, eventClient)(testAppData, w, r, &donordata.Provided{ + LpaID: "lpa-id", + LpaUID: "lpa-uid", + Donor: donordata.Donor{FirstNames: "John", LastName: "Smith"}, }) resp := w.Result() @@ -144,7 +157,7 @@ func TestPostEnterCorrespondentDetailsWhenWantsAddress(t *testing.T) { }). Return(nil) - err := EnterCorrespondentDetails(nil, donorStore)(testAppData, w, r, &donordata.Provided{ + err := EnterCorrespondentDetails(nil, donorStore, nil)(testAppData, w, r, &donordata.Provided{ LpaID: "lpa-id", Donor: donordata.Donor{FirstNames: "John", LastName: "Smith"}, }) @@ -155,6 +168,31 @@ func TestPostEnterCorrespondentDetailsWhenWantsAddress(t *testing.T) { assert.Equal(t, donor.PathEnterCorrespondentAddress.Format("lpa-id"), resp.Header.Get("Location")) } +func TestPostEnterCorrespondentDetailsWhenEventClientErrors(t *testing.T) { + f := url.Values{ + "first-names": {"John"}, + "last-name": {"Doe"}, + "email": {"email@example.com"}, + form.FieldNames.YesNo: {form.No.String()}, + } + + w := httptest.NewRecorder() + r, _ := http.NewRequest(http.MethodPost, "/", strings.NewReader(f.Encode())) + r.Header.Add("Content-Type", page.FormUrlEncoded) + + eventClient := newMockEventClient(t) + eventClient.EXPECT(). + SendCorrespondentUpdated(mock.Anything, mock.Anything). + Return(expectedError) + + err := EnterCorrespondentDetails(nil, nil, eventClient)(testAppData, w, r, &donordata.Provided{ + LpaID: "lpa-id", + LpaUID: "lpa-uid", + Donor: donordata.Donor{FirstNames: "John", LastName: "Smith"}, + }) + assert.Equal(t, expectedError, err) +} + func TestPostEnterCorrespondentDetailsWhenValidationError(t *testing.T) { form := url.Values{ "last-name": {"Doe"}, @@ -173,7 +211,7 @@ func TestPostEnterCorrespondentDetailsWhenValidationError(t *testing.T) { })). Return(nil) - err := EnterCorrespondentDetails(template.Execute, nil)(testAppData, w, r, &donordata.Provided{ + err := EnterCorrespondentDetails(template.Execute, nil, nil)(testAppData, w, r, &donordata.Provided{ Donor: donordata.Donor{ FirstNames: "John", LastName: "Doe", @@ -202,7 +240,7 @@ func TestPostEnterCorrespondentDetailsWhenStoreErrors(t *testing.T) { Put(r.Context(), mock.Anything). Return(expectedError) - err := EnterCorrespondentDetails(nil, donorStore)(testAppData, w, r, &donordata.Provided{ + err := EnterCorrespondentDetails(nil, donorStore, nil)(testAppData, w, r, &donordata.Provided{ Donor: donordata.Donor{ FirstNames: "John", Address: place.Address{Line1: "abc"}, diff --git a/internal/donor/donorpage/mock_EventClient_test.go b/internal/donor/donorpage/mock_EventClient_test.go index b1c24e83fd..9f724b0b80 100644 --- a/internal/donor/donorpage/mock_EventClient_test.go +++ b/internal/donor/donorpage/mock_EventClient_test.go @@ -69,6 +69,53 @@ func (_c *mockEventClient_SendCertificateProviderStarted_Call) RunAndReturn(run return _c } +// SendCorrespondentUpdated provides a mock function with given fields: ctx, e +func (_m *mockEventClient) SendCorrespondentUpdated(ctx context.Context, e event.CorrespondentUpdated) error { + ret := _m.Called(ctx, e) + + if len(ret) == 0 { + panic("no return value specified for SendCorrespondentUpdated") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, event.CorrespondentUpdated) error); ok { + r0 = rf(ctx, e) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// mockEventClient_SendCorrespondentUpdated_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendCorrespondentUpdated' +type mockEventClient_SendCorrespondentUpdated_Call struct { + *mock.Call +} + +// SendCorrespondentUpdated is a helper method to define mock.On call +// - ctx context.Context +// - e event.CorrespondentUpdated +func (_e *mockEventClient_Expecter) SendCorrespondentUpdated(ctx interface{}, e interface{}) *mockEventClient_SendCorrespondentUpdated_Call { + return &mockEventClient_SendCorrespondentUpdated_Call{Call: _e.mock.On("SendCorrespondentUpdated", ctx, e)} +} + +func (_c *mockEventClient_SendCorrespondentUpdated_Call) Run(run func(ctx context.Context, e event.CorrespondentUpdated)) *mockEventClient_SendCorrespondentUpdated_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(event.CorrespondentUpdated)) + }) + return _c +} + +func (_c *mockEventClient_SendCorrespondentUpdated_Call) Return(_a0 error) *mockEventClient_SendCorrespondentUpdated_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockEventClient_SendCorrespondentUpdated_Call) RunAndReturn(run func(context.Context, event.CorrespondentUpdated) error) *mockEventClient_SendCorrespondentUpdated_Call { + _c.Call.Return(run) + return _c +} + // SendIdentityCheckMismatched provides a mock function with given fields: ctx, e func (_m *mockEventClient) SendIdentityCheckMismatched(ctx context.Context, e event.IdentityCheckMismatched) error { ret := _m.Called(ctx, e) diff --git a/internal/donor/donorpage/register.go b/internal/donor/donorpage/register.go index a036c83d81..c5fe70bdc0 100644 --- a/internal/donor/donorpage/register.go +++ b/internal/donor/donorpage/register.go @@ -144,6 +144,7 @@ type EventClient interface { SendUidRequested(ctx context.Context, e event.UidRequested) error SendCertificateProviderStarted(ctx context.Context, e event.CertificateProviderStarted) error SendIdentityCheckMismatched(ctx context.Context, e event.IdentityCheckMismatched) error + SendCorrespondentUpdated(ctx context.Context, e event.CorrespondentUpdated) error } type DashboardStore interface { @@ -332,11 +333,11 @@ func Register( RemovePersonToNotify(tmpls.Get("remove_person_to_notify.gohtml"), donorStore)) handleWithDonor(donor.PathAddCorrespondent, page.None, - AddCorrespondent(tmpls.Get("add_correspondent.gohtml"), donorStore)) + AddCorrespondent(tmpls.Get("add_correspondent.gohtml"), donorStore, eventClient)) handleWithDonor(donor.PathEnterCorrespondentDetails, page.CanGoBack, - EnterCorrespondentDetails(tmpls.Get("enter_correspondent_details.gohtml"), donorStore)) + EnterCorrespondentDetails(tmpls.Get("enter_correspondent_details.gohtml"), donorStore, eventClient)) handleWithDonor(donor.PathEnterCorrespondentAddress, page.CanGoBack, - EnterCorrespondentAddress(logger, tmpls.Get("choose_address.gohtml"), addressClient, donorStore)) + EnterCorrespondentAddress(logger, tmpls.Get("choose_address.gohtml"), addressClient, donorStore, eventClient)) handleWithDonor(donor.PathGettingHelpSigning, page.CanGoBack, Guidance(tmpls.Get("getting_help_signing.gohtml"))) diff --git a/internal/event/client.go b/internal/event/client.go index 5bf5844b2d..e3c84bf0b4 100644 --- a/internal/event/client.go +++ b/internal/event/client.go @@ -24,6 +24,7 @@ var events = map[any]string{ (*CertificateProviderStarted)(nil): "certificate-provider-started", (*AttorneyStarted)(nil): "attorney-started", (*IdentityCheckMismatched)(nil): "identity-check-mismatched", + (*CorrespondentUpdated)(nil): "correspondent-updated", } type eventbridgeClient interface { @@ -82,6 +83,10 @@ func (c *Client) SendIdentityCheckMismatched(ctx context.Context, event Identity return send[IdentityCheckMismatched](ctx, c, event) } +func (c *Client) SendCorrespondentUpdated(ctx context.Context, event CorrespondentUpdated) error { + return send[CorrespondentUpdated](ctx, c, event) +} + func send[T any](ctx context.Context, c *Client, detail any) error { detailType, ok := events[(*T)(nil)] if !ok { diff --git a/internal/event/client_test.go b/internal/event/client_test.go index 463a24a77d..4fba00b058 100644 --- a/internal/event/client_test.go +++ b/internal/event/client_test.go @@ -73,6 +73,11 @@ func TestClientSendEvents(t *testing.T) { return func(client *Client) error { return client.SendIdentityCheckMismatched(ctx, event) }, event }, + "correspondent-updated": func() (func(*Client) error, any) { + event := CorrespondentUpdated{UID: "a"} + + return func(client *Client) error { return client.SendCorrespondentUpdated(ctx, event) }, event + }, } for eventName, setup := range testcases { diff --git a/internal/event/events.go b/internal/event/events.go index 4c49c2ceaa..a6b874950b 100644 --- a/internal/event/events.go +++ b/internal/event/events.go @@ -86,3 +86,12 @@ type IdentityCheckMismatchedDetails struct { LastName string `json:"lastName"` DateOfBirth date.Date `json:"dateOfBirth"` } + +type CorrespondentUpdated struct { + UID string `json:"uid"` + FirstNames string `json:"firstNames,omitempty"` + LastName string `json:"lastName,omitempty"` + Email string `json:"email,omitempty"` + Phone string `json:"phone,omitempty"` + Address *place.Address `json:"address,omitempty"` +} diff --git a/internal/event/events_test.go b/internal/event/events_test.go index 8a80f9ca02..88b5654298 100644 --- a/internal/event/events_test.go +++ b/internal/event/events_test.go @@ -138,6 +138,29 @@ var eventTests = map[string]map[string]any{ }, }, }, + "correspondent-updated": { + "remove": CorrespondentUpdated{UID: "M-1111-1111-1111"}, + "without address": CorrespondentUpdated{ + UID: "M-1111-1111-1111", + FirstNames: "John", + LastName: "Smith", + Email: "john@example.com", + Phone: "07777", + }, + "with address": CorrespondentUpdated{ + UID: "M-1111-1111-1111", + FirstNames: "John", + LastName: "Smith", + Email: "john@example.com", + Phone: "07777", + Address: &place.Address{ + Line1: "line-1", + TownOrCity: "town", + Postcode: "F1 1FF", + Country: "GB", + }, + }, + }, } func TestEventSchema(t *testing.T) { diff --git a/internal/event/testdata/correspondent-updated.json b/internal/event/testdata/correspondent-updated.json new file mode 100644 index 0000000000..53119c7874 --- /dev/null +++ b/internal/event/testdata/correspondent-updated.json @@ -0,0 +1,58 @@ +{ + "$id": "https://opg.service.justice.gov.uk/opg.poas.makeregister/correspondent-updated.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "opg.poas.makeregister/correspondent-updated", + "type": "object", + "properties": { + "uid": { + "type": "string", + "description": "The UID of the LPA", + "pattern": "^M(-[A-Z0-9]{4}){3}$" + }, + "firstNames": { + "type": "string", + "description": "The correspondent's first name(s) including any middle names" + }, + "lastName": { + "type": "string", + "description": "The correspondent's last name" + }, + "email": { + "type": "string", + "description": "The correspondent's email address" + }, + "phone": { + "type": "string", + "description": "The correspondent's phone number" + }, + "address": { + "type": "object", + "description": "The correspondent's address", + "properties": { + "line1": { + "type": "string" + }, + "line2": { + "type": "string" + }, + "line3": { + "type": "string" + }, + "town": { + "type": "string" + }, + "postcode": { + "type": "string", + "pattern": "^[A-Z0-9 ]{1,9}$" + }, + "country": { + "type": "string", + "description": "2-digit ISO 3166-1 country code per FCDO definitions: https://www.gov.uk/government/publications/geographical-names-and-information", + "pattern": "^[A-Z]{2}$" + } + }, + "required": ["line1", "town", "country"] + } + }, + "required": ["uid"] +} diff --git a/scripts/get_event_schemas.sh b/scripts/get_event_schemas.sh index 05d2a96bb4..23cb390a20 100644 --- a/scripts/get_event_schemas.sh +++ b/scripts/get_event_schemas.sh @@ -12,7 +12,8 @@ for v in uid-requested \ payment-received \ certificate-provider-started \ attorney-started \ - identity-check-mismatched + identity-check-mismatched \ + correspondent-updated do echo $v curl -o internal/event/testdata/$v.json "https://raw.githubusercontent.com/ministryofjustice/opg-event-store/main/domains/POAS/events/$v/schema.json"