Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for new Prompts and Screens for Prompt Partial #437

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions management/management.gen.go

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

18 changes: 18 additions & 0 deletions management/management.gen_test.go

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

105 changes: 104 additions & 1 deletion management/prompt.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ const (

// PromptLoginPassword represents the login-password prompt.
PromptLoginPassword PromptType = "login-password"

// PromptLoginPasswordLess represents the login-passwordless prompt.
PromptLoginPasswordLess PromptType = "login-passwordless"
)

var allowedPromptsWithPartials = []PromptType{
Expand All @@ -33,11 +36,73 @@ var allowedPromptsWithPartials = []PromptType{
PromptLogin,
PromptLoginID,
PromptLoginPassword,
PromptLoginPasswordLess,
}

// PromptType defines the prompt that we are managing.
type PromptType string

// ScreenName is a type that represents the name of a screen.
type ScreenName string

// InsertionPoint is a type that represents the insertion point of a screen.
type InsertionPoint string

const (
// ScreenLogin represents the login screen.
ScreenLogin ScreenName = "login"

// ScreenLoginID represents the login-id screen.
ScreenLoginID ScreenName = "login-id"

// ScreenLoginPassword represents the login-password screen.
ScreenLoginPassword ScreenName = "login-password"

// ScreenSignup represents the signup screen.
ScreenSignup ScreenName = "signup"

// ScreenSignupID represents the signup-id screen.
ScreenSignupID ScreenName = "signup-id"

// ScreenSignupPassword represents the signup-password screen.
ScreenSignupPassword ScreenName = "signup-password"

// ScreenLoginPasswordlessSMSOTP represents the login-passwordless-sms-otp screen.
ScreenLoginPasswordlessSMSOTP ScreenName = "login-passwordless-sms-otp"

// ScreenLoginPasswordlessEmailCode represents the login-passwordless-email-code screen.
ScreenLoginPasswordlessEmailCode ScreenName = "login-passwordless-email-code"
)

const (
// InsertionPointFormContentStart represents the form-content-start insertion point.
InsertionPointFormContentStart InsertionPoint = "form-content-start"

// InsertionPointFormContentEnd represents the form-content-end insertion point.
InsertionPointFormContentEnd InsertionPoint = "form-content-end"

// InsertionPointFormFooterStart represents the form-footer-start insertion point.
InsertionPointFormFooterStart InsertionPoint = "form-footer-start"

// InsertionPointFormFooterEnd represents the form-footer-end insertion point.
InsertionPointFormFooterEnd InsertionPoint = "form-footer-end"

// InsertionPointSecondaryActionsStart represents the secondary-actions-start insertion point.
InsertionPointSecondaryActionsStart InsertionPoint = "secondary-actions-start"

// InsertionPointSecondaryActionsEnd represents the secondary-actions-end insertion point.
InsertionPointSecondaryActionsEnd InsertionPoint = "secondary-actions-end"
)

// ScreenPartials is a map of insertion points to partials.
type ScreenPartials struct {
// Define InsertionPoints for the screen partials here
Content map[InsertionPoint]string
}

// PromptScreenPartials is a map of screen names to insertion points.
type PromptScreenPartials map[ScreenName]map[InsertionPoint]string

// Prompt is used within the Login Page.
//
// See: https://auth0.com/docs/customize/universal-login-pages/customize-login-text-prompts
Expand All @@ -54,6 +119,8 @@ type Prompt struct {

// PromptPartials to be used for Custom Prompt Partials.
//
// Deprecated: Use [PromptScreenPartials] instead.
//
// See: https://auth0.com/docs/sign-up-prompt-customizations
type PromptPartials struct {
FormContentStart string `json:"form-content-start,omitempty"`
Expand Down Expand Up @@ -127,6 +194,10 @@ func (m *PromptManager) SetCustomText(ctx context.Context, p string, l string, b

// CreatePartials creates new custom prompt partials for a given segment.
//
// Deprecated: Use [ SetPartials ] instead. The [ SetPartials ] method is preferred for setting prompt partials and provides a more consistent API.
//
// To create a partial with a different screen name and prompt name, use the [ SetPartials ] method with the [PromptScreenPartials] struct.
//
// See: https://auth0.com/docs/sign-up-prompt-customizations#use-the-api-to-edit-custom-prompts
func (m *PromptManager) CreatePartials(ctx context.Context, c *PromptPartials, opts ...RequestOption) error {
if err := guardAgainstPromptTypesWithNoPartials(c.Prompt); err != nil {
Expand All @@ -138,6 +209,10 @@ func (m *PromptManager) CreatePartials(ctx context.Context, c *PromptPartials, o

// UpdatePartials updates custom prompt partials for a given segment.
//
// Deprecated: Use [ SetPartials ] instead. The [ SetPartials ] method offers more flexibility and is the recommended approach for updating prompt partials.
//
// To update a partial with a different screen name and prompt name, use the [ SetPartials ] method with the [PromptScreenPartials] struct.
//
// See: https://auth0.com/docs/sign-up-prompt-customizations#use-the-api-to-edit-custom-prompts
func (m *PromptManager) UpdatePartials(ctx context.Context, c *PromptPartials, opts ...RequestOption) error {
if err := guardAgainstPromptTypesWithNoPartials(c.Prompt); err != nil {
Expand All @@ -147,8 +222,36 @@ func (m *PromptManager) UpdatePartials(ctx context.Context, c *PromptPartials, o
return m.management.Request(ctx, "PUT", m.management.URI("prompts", string(c.Prompt), "partials"), c, opts...)
}

// GetPartials retrieves custom prompt partials for a given segment.
//
// See : https://auth0.com/docs/api/management/v2/prompts/get-partials
func (m *PromptManager) GetPartials(ctx context.Context, prompt PromptType, opts ...RequestOption) (c *PromptScreenPartials, err error) {
if err := guardAgainstPromptTypesWithNoPartials(prompt); err != nil {
return nil, err
}

err = m.management.Request(ctx, "GET", m.management.URI("prompts", string(prompt), "partials"), &c, opts...)

return
}

// SetPartials sets custom prompt partials for a given segment.
//
// See : https://auth0.com/docs/api/management/v2/prompts/put-partials
func (m *PromptManager) SetPartials(ctx context.Context, prompt PromptType, c *PromptScreenPartials, opts ...RequestOption) error {
if err := guardAgainstPromptTypesWithNoPartials(prompt); err != nil {
return err
}

return m.management.Request(ctx, "PUT", m.management.URI("prompts", string(prompt), "partials"), &c, opts...)
}

// ReadPartials reads custom prompt partials for a given segment.
//
// Deprecated: Use [ GetPartials ] instead. The [ GetPartials ] method provides the same functionality with improved support and additional features.
//
// If there are multiple screen partials for a prompt, this method will return only the first screen partial. To retrieve all screen partials for a prompt, use the [ GetPartials ] method.
//
// See: https://auth0.com/docs/sign-up-prompt-customizations#use-the-api-to-edit-custom-prompts
func (m *PromptManager) ReadPartials(ctx context.Context, prompt PromptType, opts ...RequestOption) (c *PromptPartials, err error) {
if err := guardAgainstPromptTypesWithNoPartials(prompt); err != nil {
Expand All @@ -174,7 +277,7 @@ func (m *PromptManager) DeletePartials(ctx context.Context, prompt PromptType, o
return err
}

return m.management.Request(ctx, "PUT", m.management.URI("prompts", string(prompt), "partials"), &PromptPartials{Prompt: prompt}, opts...)
return m.management.Request(ctx, "PUT", m.management.URI("prompts", string(prompt), "partials"), &PromptScreenPartials{}, opts...)
}

func guardAgainstPromptTypesWithNoPartials(prompt PromptType) error {
Expand Down
97 changes: 97 additions & 0 deletions management/prompt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,76 @@ func TestPromptCustomText(t *testing.T) {
assert.Equal(t, "Welcome", texts["login"].(map[string]interface{})["title"])
}

func TestPromptManager_GetPartials(t *testing.T) {
configureHTTPTestRecordings(t)

_ = givenACustomDomain(t)
_ = givenAUniversalLoginTemplate(t)

prompt := PromptLogin

expected := givenAPartialPrompt(t, prompt)

actual, err := api.Prompt.GetPartials(context.Background(), prompt)

assert.NoError(t, err)
assert.Equal(t, expected, actual)
}

func TestPromptManager_SetPartials(t *testing.T) {
configureHTTPTestRecordings(t)

_ = givenACustomDomain(t)
_ = givenAUniversalLoginTemplate(t)

prompt := PromptLoginPasswordLess

expected := &PromptScreenPartials{
ScreenLoginPasswordlessSMSOTP: {
InsertionPointFormContentStart: `<div>Form Content Start</div>`,
InsertionPointFormContentEnd: `<div>Form Content Start</div>`,
},
ScreenLoginPasswordlessEmailCode: {
InsertionPointFormContentStart: `<div>Form Content Start</div>`,
InsertionPointFormContentEnd: `<div>Form Content Start</div>`,
},
}

err := api.Prompt.SetPartials(context.Background(), prompt, expected)
assert.NoError(t, err)

t.Cleanup(func() {
cleanupPromptPartials(t, prompt)
})

actual, err := api.Prompt.GetPartials(context.Background(), prompt)

assert.NoError(t, err)
assert.Equal(t, expected, actual)
}

func TestPromptManager_GetPartialsGuardGuardError(t *testing.T) {
configureHTTPTestRecordings(t)

prompt := PromptType("OtherPrompt")

_, err := api.Prompt.GetPartials(context.Background(), prompt)
assert.Error(t, err)
assert.ErrorContains(t, err, "cannot customize partials for prompt")
}

func TestPromptManager_SetPartialsGuardGuardError(t *testing.T) {
configureHTTPTestRecordings(t)

prompt := PromptType("OtherPrompt")

expected := &PromptScreenPartials{}

err := api.Prompt.SetPartials(context.Background(), prompt, expected)
assert.Error(t, err)
assert.ErrorContains(t, err, "cannot customize partials for prompt")
}

func TestPromptManager_ReadPartials(t *testing.T) {
configureHTTPTestRecordings(t)

Expand Down Expand Up @@ -202,3 +272,30 @@ func deleteUniversalLoginTemplate(t *testing.T) {
err := api.Branding.DeleteUniversalLogin(context.Background())
assert.NoError(t, err)
}

func givenAPartialPrompt(t *testing.T, prompt PromptType) *PromptScreenPartials {
t.Helper()

partials := &PromptScreenPartials{
ScreenLogin: {
InsertionPointFormContentStart: `<div>Form Content Start</div>`,
InsertionPointFormContentEnd: `<div>Form Content Start</div>`,
},
}

err := api.Prompt.SetPartials(context.Background(), prompt, partials)
assert.NoError(t, err)

t.Cleanup(func() {
cleanupPromptPartials(t, prompt)
})

return partials
}

func cleanupPromptPartials(t *testing.T, prompt PromptType) {
t.Helper()

err := api.Prompt.DeletePartials(context.Background(), prompt)
assert.NoError(t, err)
}
Loading