Skip to content

Commit

Permalink
Support flags to configure the rendering-mode, prompt, screen and set…
Browse files Browse the repository at this point in the history
…tings-file (#1111)

* Support flags to configure the rendering-mode, prompt, screen and settings-file

* Update docs

* Add examples for ul customize
  • Loading branch information
ramya18101 authored Dec 19, 2024
1 parent fdef614 commit 01a9d56
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 62 deletions.
15 changes: 15 additions & 0 deletions docs/auth0_universal-login_customize.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,24 @@ auth0 universal-login customize [flags]
```
auth0 universal-login customize
auth0 ul customize
auth0 ul customize --rendering-mode standard
auth0 ul customize -r standard
auth0 ul customize --rendering-mode advanced --prompt login-id --screen login-id
auth0 ul customize --rendering-mode advanced --prompt login-id --screen login-id --settings-file settings.json
auth0 ul customize -r advanced -p login-id -s login-id -f settings.json
```


## Flags

```
-p, --prompt string Name of the prompt to customize.
-r, --rendering-mode string standardMode is recommended for customizating consistent, branded experience for users.
Alternatively, advancedMode is recommended for full customization/granular control of the login experience and to integrate own component design system
-s, --screen string Name of the screen to customize.
-f, --settings-file string File to save the rendering configs to.
```


## Inherited Flags
Expand Down
186 changes: 124 additions & 62 deletions internal/cli/universal_login_customize.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"net"
"net/http"
"net/url"
"os"
"strings"
"time"

Expand All @@ -22,7 +23,6 @@ import (
"github.com/auth0/auth0-cli/internal/ansi"
"github.com/auth0/auth0-cli/internal/auth0"
"github.com/auth0/auth0-cli/internal/display"
"github.com/auth0/auth0-cli/internal/prompt"
)

const (
Expand All @@ -36,6 +36,8 @@ const (
fetchPartialFeatureFlag = "FETCH_PARTIALS_FEATURE_FLAG"
errorMessageType = "ERROR"
successMessageType = "SUCCESS"
advancedMode = "advanced"
standardMode = "standard"
)

var (
Expand All @@ -46,6 +48,44 @@ var (
promptScreenSettings string
)

var (
renderingMode = Flag{
Name: "Rendering Mode",
LongForm: "rendering-mode",
ShortForm: "r",
Help: fmt.Sprintf(
"%s\n%s\n",
"standardMode is recommended for customizating consistent, branded experience for users.",
"Alternatively, advancedMode is recommended for full customization/granular control of the login experience and to integrate own component design system",
),
IsRequired: true,
}

promptName = Flag{
Name: "Prompt Name",
LongForm: "prompt",
ShortForm: "p",
Help: "Name of the prompt to customize.",
IsRequired: true,
}

screenName = Flag{
Name: "Screen Name",
LongForm: "screen",
ShortForm: "s",
Help: "Name of the screen to customize.",
IsRequired: true,
}

file = Flag{
Name: "File",
LongForm: "settings-file",
ShortForm: "f",
Help: "File to save the rendering configs to.",
IsRequired: false,
}
)

var allowedPromptsWithPartials = []management.PromptType{
management.PromptSignup,
management.PromptSignupID,
Expand Down Expand Up @@ -181,7 +221,18 @@ func (m *webSocketMessage) UnmarshalJSON(b []byte) error {
return nil
}

type customizationInputs struct {
promptName string
screenName string
filePath string
}

func customizeUniversalLoginCmd(cli *cli) *cobra.Command {
var (
selectedRenderingMode string
input customizationInputs
)

cmd := &cobra.Command{
Use: "customize",
Args: cobra.NoArgs,
Expand All @@ -194,25 +245,13 @@ func customizeUniversalLoginCmd(cli *cli) *cobra.Command {
"Choosing Advanced mode will open the default terminal editor, with the rendering configs:\n\n" +
"![storybook](settings.json)\n\nClosing the terminal editor will save the settings to your tenant.",
Example: ` auth0 universal-login customize
auth0 ul customize`,
auth0 ul customize
auth0 ul customize --rendering-mode standard
auth0 ul customize -r standard
auth0 ul customize --rendering-mode advanced --prompt login-id --screen login-id
auth0 ul customize --rendering-mode advanced --prompt login-id --screen login-id --settings-file settings.json
auth0 ul customize -r advanced -p login-id -s login-id -f settings.json`,
RunE: func(cmd *cobra.Command, args []string) error {
var (
selectedRenderingMode string
)

const advancedMode, standardMode = "advanced", "standard"
label := "Please choose the renderingMode: "
help := fmt.Sprintf(
"%s\n%s\n",
"standardMode is recommended for customizating consistent, branded experience for users.",
"Alternatively, advancedMode is recommended for full customization/granular control of the login experience and to integrate own component design system",
)

input := prompt.SelectInput("", label, help, []string{standardMode, advancedMode}, standardMode, true)
if err := prompt.AskOne(input, &selectedRenderingMode); err != nil {
return handleInputError(err)
}

ctx := cmd.Context()

if err := ensureCustomDomainIsEnabled(ctx, cli.api); err != nil {
Expand All @@ -223,87 +262,110 @@ func customizeUniversalLoginCmd(cli *cli) *cobra.Command {
return err
}

if err := renderingMode.Select(cmd, &selectedRenderingMode, []string{advancedMode, standardMode}, nil); err != nil {
return err
}

if selectedRenderingMode == advancedMode {
return advanceCustomize(cmd, cli)
return advanceCustomize(cmd, cli, input)
}

// RenderingMode as standard.
return startWebSocketServer(ctx, cli.api, cli.renderer, cli.tenant)
},
}

renderingMode.RegisterString(cmd, &selectedRenderingMode, "")
promptName.RegisterString(cmd, &input.promptName, "")
screenName.RegisterString(cmd, &input.screenName, "")
file.RegisterString(cmd, &input.filePath, "")

return cmd
}

func advanceCustomize(cmd *cobra.Command, cli *cli) error {
var (
headTags string
renderSettings = &management.PromptRendering{}
)

promptName, screenName, err := fetchPromptScreenInfo()
func advanceCustomize(cmd *cobra.Command, cli *cli, input customizationInputs) error {
err := fetchPromptScreenInfo(cmd, &input)
if err != nil {
return err
}

cli.renderer.Infof("Updating the rendering settings for the prompt: %s and for the screen: %s", ansi.Green(promptName), ansi.Green(screenName))
cli.renderer.Infof("Updating the rendering settings for the prompt: %s and for the screen: %s", ansi.Green(input.promptName), ansi.Green(input.screenName))

readRendering, err := cli.api.Prompt.ReadRendering(cmd.Context(), management.PromptType(promptName), management.ScreenName(screenName))
renderSettings, err := fetchRenderSettings(cmd, cli, input)
if err != nil {
return fmt.Errorf("failed to fetch the existing render settings: %w", err)
return err
}

if readRendering != nil {
jsonData, _ := json.MarshalIndent(readRendering, "", " ")
promptScreenSettings = string(jsonData)
if err = ansi.Waiting(func() error {
return cli.api.Prompt.UpdateRendering(cmd.Context(), management.PromptType(input.promptName), management.ScreenName(input.screenName), renderSettings)
}); err != nil {
return fmt.Errorf("failed to set the render settings: %w", err)
}

err = ruleScript.OpenEditor(cmd, &headTags, promptScreenSettings, promptName+"_"+screenName+".json", cli.ruleEditorHint)
if err != nil {
return fmt.Errorf("failed to capture input from the editor: %w", err)
}
cli.renderer.Infof("Successfully set the render settings")

err = json.Unmarshal([]byte(headTags), renderSettings)
if err != nil {
return fmt.Errorf("failed to unmarshal JSON input: %w", err)
}
return nil
}

if err := ansi.Waiting(func() error {
return cli.api.Prompt.UpdateRendering(cmd.Context(), management.PromptType(promptName), management.ScreenName(screenName), renderSettings)
}); err != nil {
return fmt.Errorf("failed to set the render settings: %w", err)
func fetchPromptScreenInfo(cmd *cobra.Command, input *customizationInputs) error {
if input.promptName == "" {
if err := promptName.Select(cmd, &input.promptName, fetchKeys(ScreenPromptMap), nil); err != nil {
return handleInputError(err)
}
}

cli.renderer.Infof("Successfully set the render settings")
if (input.screenName == "") && len(ScreenPromptMap[input.promptName]) > 1 {
if err := screenName.Select(cmd, &input.screenName, ScreenPromptMap[input.promptName], nil); err != nil {
return handleInputError(err)
}
} else {
input.screenName = ScreenPromptMap[input.promptName][0]
}

return nil
}

func fetchPromptScreenInfo() (string, string, error) {
func fetchRenderSettings(cmd *cobra.Command, cli *cli, input customizationInputs) (*management.PromptRendering, error) {
var (
promptName, screenName string
help = "Auth0 supports customizing for many prompts & screens"
headTags string
renderSettings = &management.PromptRendering{}
)

promptLabel := "Please choose the Prompt Name:"
if input.filePath != "" {
data, err := os.ReadFile(input.filePath)
if err != nil {
return nil, fmt.Errorf("unable to read file %q: %v", input.filePath, err)
}

// Validate JSON content.
if err := json.Unmarshal(data, &renderSettings); err != nil {
return nil, fmt.Errorf("file %q contains invalid JSON: %v", input.filePath, err)
}

PromptInput := prompt.SelectInput("", promptLabel, help, fetchKeys(ScreenPromptMap), "login-id", true)
if err := prompt.AskOne(PromptInput, &promptName); err != nil {
return "", "", handleInputError(err)
return renderSettings, nil
}

if len(ScreenPromptMap[promptName]) > 1 {
screenLabel := "Please choose the Screen Name:"
readRendering, err := cli.api.Prompt.ReadRendering(cmd.Context(), management.PromptType(input.promptName), management.ScreenName(input.screenName))
if err != nil {
return nil, fmt.Errorf("failed to fetch the existing render settings: %w", err)
}

screenInput := prompt.SelectInput("", screenLabel, help, ScreenPromptMap[promptName], ScreenPromptMap[promptName][0], true)
if err := prompt.AskOne(screenInput, &screenName); err != nil {
return "", "", handleInputError(err)
}
} else {
screenName = ScreenPromptMap[promptName][0]
if readRendering != nil {
jsonData, _ := json.MarshalIndent(readRendering, "", " ")
promptScreenSettings = string(jsonData)
}

err = ruleScript.OpenEditor(cmd, &headTags, promptScreenSettings, input.promptName+"_"+input.screenName+".json", cli.ruleEditorHint)
if err != nil {
return nil, fmt.Errorf("failed to capture input from the editor: %w", err)
}

err = json.Unmarshal([]byte(headTags), renderSettings)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal JSON input: %w", err)
}

return promptName, screenName, nil
return renderSettings, nil
}

// Utility function to get all keys from a map.
Expand Down
4 changes: 4 additions & 0 deletions internal/iostream/iostream.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ func PipedInput() []byte {
for {
input, err := reader.ReadBytes('\n')
if err == io.EOF {
// Handle the last partial line (if any) at EOF.
if len(input) > 0 {
pipedInput = append(pipedInput, input...)
}
break
} else if err != nil {
panic(auth0.Error(err, "unable to read from pipe"))
Expand Down

0 comments on commit 01a9d56

Please sign in to comment.