feat: support time_zone for user registration context
This commit is contained in:
@@ -149,25 +149,42 @@ func (c *RESTClient) ExistsByUserID(ctx context.Context, userID common.UserID) (
|
||||
return response.Exists, nil
|
||||
}
|
||||
|
||||
// EnsureUserByEmail returns an existing user for email, creates a new user
|
||||
// when registration is allowed, or reports a blocked outcome.
|
||||
func (c *RESTClient) EnsureUserByEmail(ctx context.Context, email common.Email) (ports.EnsureUserResult, error) {
|
||||
// EnsureUserByEmail returns an existing user for input.Email, creates a new
|
||||
// user when registration is allowed, or reports a blocked outcome.
|
||||
func (c *RESTClient) EnsureUserByEmail(ctx context.Context, input ports.EnsureUserInput) (ports.EnsureUserResult, error) {
|
||||
if err := validateContext(ctx, "ensure user by email"); err != nil {
|
||||
return ports.EnsureUserResult{}, err
|
||||
}
|
||||
if err := email.Validate(); err != nil {
|
||||
if err := input.Validate(); err != nil {
|
||||
return ports.EnsureUserResult{}, fmt.Errorf("ensure user by email: %w", err)
|
||||
}
|
||||
|
||||
payload := struct {
|
||||
Email string `json:"email"`
|
||||
RegistrationContext *struct {
|
||||
PreferredLanguage string `json:"preferred_language"`
|
||||
TimeZone string `json:"time_zone"`
|
||||
} `json:"registration_context,omitempty"`
|
||||
}{
|
||||
Email: input.Email.String(),
|
||||
}
|
||||
if input.RegistrationContext != nil {
|
||||
payload.RegistrationContext = &struct {
|
||||
PreferredLanguage string `json:"preferred_language"`
|
||||
TimeZone string `json:"time_zone"`
|
||||
}{
|
||||
PreferredLanguage: input.RegistrationContext.PreferredLanguage,
|
||||
TimeZone: input.RegistrationContext.TimeZone,
|
||||
}
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Outcome ports.EnsureUserOutcome `json:"outcome"`
|
||||
UserID string `json:"user_id,omitempty"`
|
||||
BlockReasonCode userresolution.BlockReasonCode `json:"block_reason_code,omitempty"`
|
||||
}
|
||||
|
||||
if err := c.doJSON(ctx, "ensure user by email", http.MethodPost, ensureByEmailPath, map[string]string{
|
||||
"email": email.String(),
|
||||
}, &response, false); err != nil {
|
||||
if err := c.doJSON(ctx, "ensure user by email", http.MethodPost, ensureByEmailPath, payload, &response, false); err != nil {
|
||||
return ports.EnsureUserResult{}, err
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const restClientEnsureTimeZone = "Europe/Kaliningrad"
|
||||
|
||||
func TestNewRESTClient(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -107,7 +109,13 @@ func TestRESTClientEndpointSuccessCases(t *testing.T) {
|
||||
{
|
||||
name: "ensure user by email",
|
||||
run: func(t *testing.T, client *RESTClient) {
|
||||
result, err := client.EnsureUserByEmail(context.Background(), common.Email("created@example.com"))
|
||||
result, err := client.EnsureUserByEmail(context.Background(), ports.EnsureUserInput{
|
||||
Email: common.Email("created@example.com"),
|
||||
RegistrationContext: &ports.RegistrationContext{
|
||||
PreferredLanguage: "en",
|
||||
TimeZone: restClientEnsureTimeZone,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, ports.EnsureUserResult{
|
||||
Outcome: ports.EnsureUserOutcomeCreated,
|
||||
@@ -212,7 +220,7 @@ func TestRESTClientEndpointSuccessCases(t *testing.T) {
|
||||
Method: http.MethodPost,
|
||||
Path: ensureByEmailPath,
|
||||
ContentType: "application/json",
|
||||
Body: `{"email":"created@example.com"}`,
|
||||
Body: `{"email":"created@example.com","registration_context":{"preferred_language":"en","time_zone":"Europe/Kaliningrad"}}`,
|
||||
}, requests[0])
|
||||
case "block by user id":
|
||||
assert.Equal(t, capturedRequest{
|
||||
@@ -331,7 +339,13 @@ func TestRESTClientMutationMethodsDoNotRetry(t *testing.T) {
|
||||
{
|
||||
name: "ensure user by email",
|
||||
run: func(client *RESTClient) error {
|
||||
_, err := client.EnsureUserByEmail(context.Background(), common.Email("pilot@example.com"))
|
||||
_, err := client.EnsureUserByEmail(context.Background(), ports.EnsureUserInput{
|
||||
Email: common.Email("pilot@example.com"),
|
||||
RegistrationContext: &ports.RegistrationContext{
|
||||
PreferredLanguage: "en",
|
||||
TimeZone: restClientEnsureTimeZone,
|
||||
},
|
||||
})
|
||||
return err
|
||||
},
|
||||
},
|
||||
@@ -405,7 +419,13 @@ func TestRESTClientStrictDecodingAndUnexpectedStatuses(t *testing.T) {
|
||||
body: `{"outcome":"mystery"}`,
|
||||
wantErrText: "unsupported",
|
||||
run: func(client *RESTClient) error {
|
||||
_, err := client.EnsureUserByEmail(context.Background(), common.Email("pilot@example.com"))
|
||||
_, err := client.EnsureUserByEmail(context.Background(), ports.EnsureUserInput{
|
||||
Email: common.Email("pilot@example.com"),
|
||||
RegistrationContext: &ports.RegistrationContext{
|
||||
PreferredLanguage: "en",
|
||||
TimeZone: restClientEnsureTimeZone,
|
||||
},
|
||||
})
|
||||
return err
|
||||
},
|
||||
},
|
||||
@@ -415,7 +435,13 @@ func TestRESTClientStrictDecodingAndUnexpectedStatuses(t *testing.T) {
|
||||
body: `{"outcome":"created"}`,
|
||||
wantErrText: "user id",
|
||||
run: func(client *RESTClient) error {
|
||||
_, err := client.EnsureUserByEmail(context.Background(), common.Email("pilot@example.com"))
|
||||
_, err := client.EnsureUserByEmail(context.Background(), ports.EnsureUserInput{
|
||||
Email: common.Email("pilot@example.com"),
|
||||
RegistrationContext: &ports.RegistrationContext{
|
||||
PreferredLanguage: "en",
|
||||
TimeZone: restClientEnsureTimeZone,
|
||||
},
|
||||
})
|
||||
return err
|
||||
},
|
||||
},
|
||||
@@ -516,7 +542,22 @@ func TestRESTClientContextAndValidation(t *testing.T) {
|
||||
{
|
||||
name: "invalid email",
|
||||
run: func() error {
|
||||
_, err := client.EnsureUserByEmail(context.Background(), common.Email(" bad@example.com "))
|
||||
_, err := client.EnsureUserByEmail(context.Background(), ports.EnsureUserInput{
|
||||
Email: common.Email(" bad@example.com "),
|
||||
})
|
||||
return err
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid registration context",
|
||||
run: func() error {
|
||||
_, err := client.EnsureUserByEmail(context.Background(), ports.EnsureUserInput{
|
||||
Email: common.Email("pilot@example.com"),
|
||||
RegistrationContext: &ports.RegistrationContext{
|
||||
PreferredLanguage: " en ",
|
||||
TimeZone: restClientEnsureTimeZone,
|
||||
},
|
||||
})
|
||||
return err
|
||||
},
|
||||
},
|
||||
|
||||
@@ -69,13 +69,13 @@ func (d *StubDirectory) ExistsByUserID(ctx context.Context, userID common.UserID
|
||||
return ok, nil
|
||||
}
|
||||
|
||||
// EnsureUserByEmail returns an existing user for email, creates a new user
|
||||
// when registration is allowed, or reports a blocked outcome.
|
||||
func (d *StubDirectory) EnsureUserByEmail(ctx context.Context, email common.Email) (ports.EnsureUserResult, error) {
|
||||
// EnsureUserByEmail returns an existing user for input.Email, creates a new
|
||||
// user when registration is allowed, or reports a blocked outcome.
|
||||
func (d *StubDirectory) EnsureUserByEmail(ctx context.Context, input ports.EnsureUserInput) (ports.EnsureUserResult, error) {
|
||||
if err := validateContext(ctx, "ensure user by email"); err != nil {
|
||||
return ports.EnsureUserResult{}, err
|
||||
}
|
||||
if err := email.Validate(); err != nil {
|
||||
if err := input.Validate(); err != nil {
|
||||
return ports.EnsureUserResult{}, fmt.Errorf("ensure user by email: %w", err)
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ func (d *StubDirectory) EnsureUserByEmail(ctx context.Context, email common.Emai
|
||||
|
||||
d.ensureMapsLocked()
|
||||
|
||||
stored, ok := d.byEmail[email]
|
||||
stored, ok := d.byEmail[input.Email]
|
||||
if ok {
|
||||
if !stored.blockReasonCode.IsZero() {
|
||||
result := ports.EnsureUserResult{
|
||||
@@ -113,8 +113,8 @@ func (d *StubDirectory) EnsureUserByEmail(ctx context.Context, email common.Emai
|
||||
if err != nil {
|
||||
return ports.EnsureUserResult{}, fmt.Errorf("ensure user by email: %w", err)
|
||||
}
|
||||
d.byEmail[email] = entry{userID: userID}
|
||||
d.emailByUserID[userID] = email
|
||||
d.byEmail[input.Email] = entry{userID: userID}
|
||||
d.emailByUserID[userID] = input.Email
|
||||
|
||||
result := ports.EnsureUserResult{
|
||||
Outcome: ports.EnsureUserOutcomeCreated,
|
||||
|
||||
@@ -70,7 +70,13 @@ func TestStubDirectoryEnsureUserByEmail(t *testing.T) {
|
||||
directory := &StubDirectory{}
|
||||
require.NoError(t, directory.SeedExisting(common.Email("existing@example.com"), common.UserID("user-existing")))
|
||||
|
||||
result, err := directory.EnsureUserByEmail(context.Background(), common.Email("existing@example.com"))
|
||||
result, err := directory.EnsureUserByEmail(context.Background(), ports.EnsureUserInput{
|
||||
Email: common.Email("existing@example.com"),
|
||||
RegistrationContext: &ports.RegistrationContext{
|
||||
PreferredLanguage: "en",
|
||||
TimeZone: "Europe/Kaliningrad",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, ports.EnsureUserOutcomeExisting, result.Outcome)
|
||||
assert.Equal(t, common.UserID("user-existing"), result.UserID)
|
||||
@@ -82,7 +88,13 @@ func TestStubDirectoryEnsureUserByEmail(t *testing.T) {
|
||||
directory := &StubDirectory{}
|
||||
require.NoError(t, directory.SeedBlockedEmail(common.Email("blocked@example.com"), userresolution.BlockReasonCode("policy_block")))
|
||||
|
||||
result, err := directory.EnsureUserByEmail(context.Background(), common.Email("blocked@example.com"))
|
||||
result, err := directory.EnsureUserByEmail(context.Background(), ports.EnsureUserInput{
|
||||
Email: common.Email("blocked@example.com"),
|
||||
RegistrationContext: &ports.RegistrationContext{
|
||||
PreferredLanguage: "en",
|
||||
TimeZone: "Europe/Kaliningrad",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, ports.EnsureUserOutcomeBlocked, result.Outcome)
|
||||
assert.Equal(t, userresolution.BlockReasonCode("policy_block"), result.BlockReasonCode)
|
||||
@@ -94,12 +106,24 @@ func TestStubDirectoryEnsureUserByEmail(t *testing.T) {
|
||||
directory := &StubDirectory{}
|
||||
require.NoError(t, directory.QueueCreatedUserIDs(common.UserID("user-created")))
|
||||
|
||||
first, err := directory.EnsureUserByEmail(context.Background(), common.Email("created@example.com"))
|
||||
first, err := directory.EnsureUserByEmail(context.Background(), ports.EnsureUserInput{
|
||||
Email: common.Email("created@example.com"),
|
||||
RegistrationContext: &ports.RegistrationContext{
|
||||
PreferredLanguage: "en",
|
||||
TimeZone: "Europe/Kaliningrad",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, ports.EnsureUserOutcomeCreated, first.Outcome)
|
||||
assert.Equal(t, common.UserID("user-created"), first.UserID)
|
||||
|
||||
second, err := directory.EnsureUserByEmail(context.Background(), common.Email("created@example.com"))
|
||||
second, err := directory.EnsureUserByEmail(context.Background(), ports.EnsureUserInput{
|
||||
Email: common.Email("created@example.com"),
|
||||
RegistrationContext: &ports.RegistrationContext{
|
||||
PreferredLanguage: "fr",
|
||||
TimeZone: "Europe/Paris",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, ports.EnsureUserOutcomeExisting, second.Outcome)
|
||||
assert.Equal(t, common.UserID("user-created"), second.UserID)
|
||||
@@ -110,7 +134,13 @@ func TestStubDirectoryEnsureUserByEmail(t *testing.T) {
|
||||
|
||||
directory := &StubDirectory{}
|
||||
|
||||
result, err := directory.EnsureUserByEmail(context.Background(), common.Email("fallback@example.com"))
|
||||
result, err := directory.EnsureUserByEmail(context.Background(), ports.EnsureUserInput{
|
||||
Email: common.Email("fallback@example.com"),
|
||||
RegistrationContext: &ports.RegistrationContext{
|
||||
PreferredLanguage: "en",
|
||||
TimeZone: "Europe/Kaliningrad",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, ports.EnsureUserOutcomeCreated, result.Outcome)
|
||||
assert.Equal(t, common.UserID("user-1"), result.UserID)
|
||||
@@ -240,7 +270,9 @@ func TestStubDirectoryContextAndValidation(t *testing.T) {
|
||||
{
|
||||
name: "ensure cancelled context",
|
||||
run: func() error {
|
||||
_, err := directory.EnsureUserByEmail(cancelledCtx, common.Email("pilot@example.com"))
|
||||
_, err := directory.EnsureUserByEmail(cancelledCtx, ports.EnsureUserInput{
|
||||
Email: common.Email("pilot@example.com"),
|
||||
})
|
||||
return err
|
||||
},
|
||||
want: context.Canceled.Error(),
|
||||
|
||||
Reference in New Issue
Block a user