-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
MLPAB-1740 Add page to enter an organisation or company name (#972)
* Add page to enter an organisation or company name * Remove the word 'account' from the start page
- Loading branch information
Showing
19 changed files
with
649 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
describe('Enter group name', () => { | ||
beforeEach(() => { | ||
cy.visit('/fixtures/supporter?redirect=/enter-the-name-of-your-organisation-or-company'); | ||
}); | ||
|
||
it('can be started', () => { | ||
cy.get('#f-name').type('My name' + Math.random()); | ||
cy.contains('button', 'Continue').click(); | ||
|
||
cy.url().should('contain', '/organisation-or-company-created'); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package actor | ||
|
||
import "time" | ||
|
||
// An Organisation contains users associated with a set of permissions that work on the | ||
// same set of LPAs. | ||
type Organisation struct { | ||
PK, SK string | ||
// CreatedAt is when the Organisation was created | ||
CreatedAt time.Time | ||
// UpdatedAt is when the Organisation was last updated | ||
UpdatedAt time.Time | ||
// ID is a unique identifier for the Organisation | ||
ID string | ||
// Name of the Organisation, this is unique across all Organisations | ||
Name string | ||
} | ||
|
||
// A Member is the association of a OneLogin user with an Organisation. | ||
type Member struct { | ||
PK, SK string | ||
// CreatedAt is when the Member was created | ||
CreatedAt time.Time | ||
// UpdatedAt is when the Member was last updated | ||
UpdatedAt time.Time | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package app | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"time" | ||
|
||
"github.com/ministryofjustice/opg-modernising-lpa/internal/actor" | ||
"github.com/ministryofjustice/opg-modernising-lpa/internal/page" | ||
) | ||
|
||
type organisationStore struct { | ||
dynamoClient DynamoClient | ||
uuidString func() string | ||
now func() time.Time | ||
} | ||
|
||
func (s *organisationStore) Create(ctx context.Context, name string) error { | ||
data, err := page.SessionDataFromContext(ctx) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if data.SessionID == "" { | ||
return errors.New("organisationStore.Create requires SessionID") | ||
} | ||
|
||
organisationID := s.uuidString() | ||
|
||
organisation := &actor.Organisation{ | ||
PK: organisationKey(organisationID), | ||
SK: organisationKey(organisationID), | ||
ID: organisationID, | ||
Name: name, | ||
CreatedAt: s.now(), | ||
} | ||
|
||
if err := s.dynamoClient.Create(ctx, organisation); err != nil { | ||
return fmt.Errorf("error creating organisation: %w", err) | ||
} | ||
|
||
member := &actor.Member{ | ||
PK: organisationKey(organisationID), | ||
SK: subKey(data.SessionID), | ||
CreatedAt: s.now(), | ||
} | ||
|
||
if err := s.dynamoClient.Create(ctx, member); err != nil { | ||
return fmt.Errorf("error creating organisation member: %w", err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func organisationKey(s string) string { | ||
return "ORGANISATION#" + s | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package app | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/ministryofjustice/opg-modernising-lpa/internal/actor" | ||
"github.com/ministryofjustice/opg-modernising-lpa/internal/page" | ||
"github.com/stretchr/testify/assert" | ||
mock "github.com/stretchr/testify/mock" | ||
) | ||
|
||
func TestOrganisationStoreCreate(t *testing.T) { | ||
ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{SessionID: "an-id"}) | ||
|
||
dynamoClient := newMockDynamoClient(t) | ||
dynamoClient.EXPECT(). | ||
Create(ctx, &actor.Organisation{ | ||
PK: "ORGANISATION#a-uuid", | ||
SK: "ORGANISATION#a-uuid", | ||
ID: "a-uuid", | ||
CreatedAt: testNow, | ||
Name: "A name", | ||
}). | ||
Return(nil) | ||
dynamoClient.EXPECT(). | ||
Create(ctx, &actor.Member{ | ||
PK: "ORGANISATION#a-uuid", | ||
SK: "#SUB#an-id", | ||
CreatedAt: testNow, | ||
}). | ||
Return(nil) | ||
|
||
organisationStore := &organisationStore{dynamoClient: dynamoClient, now: testNowFn, uuidString: func() string { return "a-uuid" }} | ||
|
||
err := organisationStore.Create(ctx, "A name") | ||
assert.Nil(t, err) | ||
} | ||
|
||
func TestOrganisationStoreCreateWithSessionMissing(t *testing.T) { | ||
ctx := context.Background() | ||
organisationStore := &organisationStore{dynamoClient: nil, now: testNowFn} | ||
|
||
err := organisationStore.Create(ctx, "A name") | ||
assert.Equal(t, page.SessionMissingError{}, err) | ||
} | ||
|
||
func TestOrganisationStoreCreateWithMissingSessionID(t *testing.T) { | ||
ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{}) | ||
organisationStore := &organisationStore{dynamoClient: nil, now: testNowFn} | ||
|
||
err := organisationStore.Create(ctx, "A name") | ||
assert.Error(t, err) | ||
} | ||
|
||
func TestOrganisationStoreCreateWhenErrors(t *testing.T) { | ||
ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{SessionID: "an-id"}) | ||
|
||
testcases := map[string]func(*testing.T) *mockDynamoClient{ | ||
"organisation": func(t *testing.T) *mockDynamoClient { | ||
dynamoClient := newMockDynamoClient(t) | ||
dynamoClient.EXPECT(). | ||
Create(ctx, mock.Anything). | ||
Return(expectedError) | ||
|
||
return dynamoClient | ||
}, | ||
"member": func(t *testing.T) *mockDynamoClient { | ||
dynamoClient := newMockDynamoClient(t) | ||
dynamoClient.EXPECT(). | ||
Create(ctx, mock.Anything). | ||
Return(nil). | ||
Once() | ||
dynamoClient.EXPECT(). | ||
Create(ctx, mock.Anything). | ||
Return(expectedError) | ||
|
||
return dynamoClient | ||
}, | ||
} | ||
|
||
for name, makeMockDynamoClient := range testcases { | ||
t.Run(name, func(t *testing.T) { | ||
dynamoClient := makeMockDynamoClient(t) | ||
organisationStore := &organisationStore{dynamoClient: dynamoClient, now: testNowFn, uuidString: func() string { return "a-uuid" }} | ||
|
||
err := organisationStore.Create(ctx, "A name") | ||
assert.ErrorIs(t, err, expectedError) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package fixtures | ||
|
||
import ( | ||
"net/http" | ||
|
||
"github.com/ministryofjustice/opg-modernising-lpa/internal/page" | ||
"github.com/ministryofjustice/opg-modernising-lpa/internal/random" | ||
"github.com/ministryofjustice/opg-modernising-lpa/internal/sesh" | ||
) | ||
|
||
func Supporter(sessionStore sesh.Store) page.Handler { | ||
return func(appData page.AppData, w http.ResponseWriter, r *http.Request) error { | ||
var ( | ||
redirect = r.FormValue("redirect") | ||
|
||
supporterSub = random.String(16) | ||
) | ||
|
||
if err := sesh.SetLoginSession(sessionStore, r, w, &sesh.LoginSession{Sub: supporterSub, Email: testEmail}); err != nil { | ||
return err | ||
} | ||
|
||
http.Redirect(w, r, "/supporter/"+redirect, http.StatusFound) | ||
return nil | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package supporter | ||
|
||
import ( | ||
"net/http" | ||
|
||
"github.com/ministryofjustice/opg-go-common/template" | ||
"github.com/ministryofjustice/opg-modernising-lpa/internal/page" | ||
"github.com/ministryofjustice/opg-modernising-lpa/internal/validation" | ||
) | ||
|
||
type enterOrganisationNameData struct { | ||
App page.AppData | ||
Errors validation.List | ||
Form *enterOrganisationNameForm | ||
} | ||
|
||
func EnterOrganisationName(tmpl template.Template, organisationStore OrganisationStore) Handler { | ||
return func(appData page.AppData, w http.ResponseWriter, r *http.Request) error { | ||
data := &enterOrganisationNameData{ | ||
App: appData, | ||
Form: &enterOrganisationNameForm{}, | ||
} | ||
|
||
if r.Method == http.MethodPost { | ||
data.Form = readEnterOrganisationNameForm(r) | ||
data.Errors = data.Form.Validate() | ||
|
||
if !data.Errors.Any() { | ||
if err := organisationStore.Create(r.Context(), data.Form.Name); err != nil { | ||
return err | ||
} | ||
|
||
return page.Paths.Supporter.OrganisationCreated.Redirect(w, r, appData) | ||
} | ||
} | ||
|
||
return tmpl(w, data) | ||
} | ||
} | ||
|
||
type enterOrganisationNameForm struct { | ||
Name string | ||
} | ||
|
||
func readEnterOrganisationNameForm(r *http.Request) *enterOrganisationNameForm { | ||
return &enterOrganisationNameForm{ | ||
Name: page.PostFormString(r, "name"), | ||
} | ||
} | ||
|
||
func (f *enterOrganisationNameForm) Validate() validation.List { | ||
var errors validation.List | ||
|
||
errors.String("name", "fullOrganisationOrCompanyName", f.Name, | ||
validation.Empty(), | ||
validation.StringTooLong(100)) | ||
|
||
return errors | ||
} |
Oops, something went wrong.