diff --git a/cmd/app/link.go b/cmd/app/link.go index 044f56c3..f5944800 100644 --- a/cmd/app/link.go +++ b/cmd/app/link.go @@ -97,11 +97,46 @@ func LinkCommandRunE(ctx context.Context, clients *shared.ClientFactory, app *ty // Add empty line between executed command and first output clients.IO.PrintInfo(ctx, false, "") - err = LinkExistingApp(ctx, clients, app, false) + // Header section + LinkAppHeaderSection(ctx, clients, false) + + // App Manifest section + manifestSource, err := clients.Config.ProjectConfig.GetManifestSource(ctx) + if err != nil { + return err + } + + configPath := filepath.Join(config.ProjectConfigDirName, config.ProjectConfigJSONFilename) + clients.IO.PrintInfo(ctx, false, "%s", style.Sectionf(style.TextSection{ + Emoji: "books", + Text: "App Manifest", + Secondary: []string{ + "Manifest source is gathered from " + style.Highlight(manifestSource.Human()), + "Manifest source is configured in " + style.Highlight(configPath), + }, + })) + + err = LinkExistingApp(ctx, clients, app) if err != nil { return err } + // App summary section + clients.IO.PrintInfo(ctx, false, "%s", style.Sectionf(style.TextSection{ + Emoji: "house", + Text: "App", + Secondary: FormatListSuccess([]types.App{*app}), + })) + + // Footer section + clients.IO.PrintInfo(ctx, false, "%s", style.Sectionf(style.TextSection{ + Emoji: "house_with_garden", + Text: "App Link", + Secondary: []string{ + "Added existing app to project", + }, + })) + return nil } @@ -126,46 +161,9 @@ func LinkAppHeaderSection(ctx context.Context, clients *shared.ClientFactory, sh })) } -// LinkExistingApp prompts for an existing App ID and saves the details to the project. -// When shouldConfirm is true, a confirmation prompt will ask the user if they want to -// link an existing app and additional information is included in the header. -// The shouldConfirm option is encouraged for third-party callers. -func LinkExistingApp(ctx context.Context, clients *shared.ClientFactory, app *types.App, shouldConfirm bool) (err error) { - // Header section - LinkAppHeaderSection(ctx, clients, shouldConfirm) - - // Confirm to add an existing app; useful for third-party callers - if shouldConfirm { - proceed, err := clients.IO.ConfirmPrompt(ctx, LinkAppConfirmPromptText, true) - if err != nil { - clients.IO.PrintDebug(ctx, "Error prompting to add an existing app: %s", err) - return err - } - - // Add newline to match the trailing newline inserted from the footer section - clients.IO.PrintInfo(ctx, false, "") - - if !proceed { - return nil - } - } - - // App Manifest section - manifestSource, err := clients.Config.ProjectConfig.GetManifestSource(ctx) - if err != nil { - return err - } - - configPath := filepath.Join(config.ProjectConfigDirName, config.ProjectConfigJSONFilename) - clients.IO.PrintInfo(ctx, false, "%s", style.Sectionf(style.TextSection{ - Emoji: "books", - Text: "App Manifest", - Secondary: []string{ - "Manifest source is gathered from " + style.Highlight(manifestSource.Human()), - "Manifest source is configured in " + style.Highlight(configPath), - }, - })) - +// LinkExistingApp resolves app details, validates the app, and saves it to the +// project. It produces no output — callers handle their own display. +func LinkExistingApp(ctx context.Context, clients *shared.ClientFactory, app *types.App) (err error) { // Prompt to get app details var auth *types.SlackAuth *app, auth, err = promptExistingApp(ctx, clients) @@ -186,28 +184,9 @@ func LinkExistingApp(ctx context.Context, clients *shared.ClientFactory, app *ty return err } - // Footer section - LinkAppFooterSection(ctx, clients, app) - return nil } -// LinkAppFooterSection displays the details of app that was added to the project. -func LinkAppFooterSection(ctx context.Context, clients *shared.ClientFactory, app *types.App) { - clients.IO.PrintInfo(ctx, false, "\n%s", style.Sectionf(style.TextSection{ - Emoji: "house", - Text: "App", - Secondary: formatListSuccess([]types.App{*app}), - })) - clients.IO.PrintInfo(ctx, false, "%s", style.Sectionf(style.TextSection{ - Emoji: "house_with_garden", - Text: "App Link", - Secondary: []string{ - "Added existing app to project", - }, - })) -} - // promptExistingApp gathers details to represent app information func promptExistingApp(ctx context.Context, clients *shared.ClientFactory) (types.App, *types.SlackAuth, error) { slackAuth, err := prompts.PromptTeamSlackAuth(ctx, clients, "Select the existing app team", nil) diff --git a/cmd/app/list.go b/cmd/app/list.go index 290eeb6e..216988c7 100644 --- a/cmd/app/list.go +++ b/cmd/app/list.go @@ -73,13 +73,13 @@ func runListCommand(cmd *cobra.Command, clients *shared.ClientFactory) error { clients.IO.PrintInfo(ctx, false, "\n%s", style.Sectionf(style.TextSection{ Emoji: "house_buildings", Text: "Apps", - Secondary: formatListSuccess(envs), + Secondary: FormatListSuccess(envs), })) return nil } -// formatListSuccess formats details about the list of project apps -func formatListSuccess(apps []types.App) (secondaryText []string) { +// FormatListSuccess formats details about the list of project apps +func FormatListSuccess(apps []types.App) (secondaryText []string) { for _, app := range apps { if app.AppID == "" { continue diff --git a/cmd/app/list_test.go b/cmd/app/list_test.go index caffcf6d..04d7546b 100644 --- a/cmd/app/list_test.go +++ b/cmd/app/list_test.go @@ -218,7 +218,7 @@ func TestAppsListFormat(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { listFlags = tc.Flags - formattedList := formatListSuccess(tc.Apps) + formattedList := FormatListSuccess(tc.Apps) for ii, value := range formattedList { formattedList[ii] = strings.TrimRight(value, ":") } diff --git a/cmd/project/create.go b/cmd/project/create.go index 1adff451..18f66d1d 100644 --- a/cmd/project/create.go +++ b/cmd/project/create.go @@ -229,9 +229,15 @@ func runCreateCommand(clients *shared.ClientFactory, cmd *cobra.Command, args [] defer func() { _ = os.Chdir(originalDir) }() - if err := app.LinkExistingApp(ctx, clients, &types.App{}, false); err != nil { + linkedApp := &types.App{} + if err := app.LinkExistingApp(ctx, clients, linkedApp); err != nil { return err } + clients.IO.PrintInfo(ctx, false, "%s", style.Sectionf(style.TextSection{ + Emoji: "house", + Text: "App", + Secondary: app.FormatListSuccess([]types.App{*linkedApp}), + })) } printCreateSuccess(ctx, clients, appDirPath) diff --git a/cmd/project/init.go b/cmd/project/init.go index 9f9d5f90..c4e541b8 100644 --- a/cmd/project/init.go +++ b/cmd/project/init.go @@ -109,11 +109,25 @@ func projectInitCommandRunE(clients *shared.ClientFactory, cmd *cobra.Command, a // Existing projects initialized always default to config.ManifestSourceLocal. _ = create.InstallProjectDependencies(ctx, clients, projectDirPath) - // Add an existing app to the project - err = app.LinkExistingApp(ctx, clients, &types.App{}, true) + // Prompt to add an existing app to the project + app.LinkAppHeaderSection(ctx, clients, true) + proceed, err := clients.IO.ConfirmPrompt(ctx, app.LinkAppConfirmPromptText, true) if err != nil { - // Display the error but continue to init - clients.IO.PrintError(ctx, "%s", err.Error()) + clients.IO.PrintDebug(ctx, "Error prompting to add an existing app: %s", err) + } + if proceed { + linkedApp := &types.App{} + err = app.LinkExistingApp(ctx, clients, linkedApp) + if err != nil { + // Display the error but continue to init + clients.IO.PrintError(ctx, "%s", err.Error()) + } else { + clients.IO.PrintInfo(ctx, false, "%s", style.Sectionf(style.TextSection{ + Emoji: "house", + Text: "App", + Secondary: app.FormatListSuccess([]types.App{*linkedApp}), + })) + } } printNextStepSection(ctx, clients, projectDirPath)