Use case onboarding (#19446)
* Add use case onboarding feature flag * Add endpoints and storage for whether first admin completed setup * Add migration for FirstAdminSetupComplete Co-authored-by: Michael Kochell <6913320+mickmister@users.noreply.github.com> Co-authored-by: Mattermod <mattermod@users.noreply.github.com> Co-authored-by: Ben Schumacher <ben.schumacher@mattermost.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
dde7ce5535
Коммит
459f54ac9d
@@ -653,6 +653,7 @@ type AppIface interface {
|
||||
GetOAuthLoginEndpoint(w http.ResponseWriter, r *http.Request, service, teamID, action, redirectTo, loginHint string, isMobile bool) (string, *model.AppError)
|
||||
GetOAuthSignupEndpoint(w http.ResponseWriter, r *http.Request, service, teamID string) (string, *model.AppError)
|
||||
GetOAuthStateToken(token string) (*model.Token, *model.AppError)
|
||||
GetOnboarding() (*model.System, *model.AppError)
|
||||
GetOpenGraphMetadata(requestURL string) ([]byte, error)
|
||||
GetOrCreateDirectChannel(c *request.Context, userID, otherUserID string, channelOptions ...model.ChannelOption) (*model.Channel, *model.AppError)
|
||||
GetOutgoingWebhook(hookID string) (*model.OutgoingWebhook, *model.AppError)
|
||||
|
||||
@@ -17,6 +17,7 @@ const GuestRolesCreationMigrationKey = "GuestRolesCreationMigrationComplete"
|
||||
const SystemConsoleRolesCreationMigrationKey = "SystemConsoleRolesCreationMigrationComplete"
|
||||
const ContentExtractionConfigDefaultTrueMigrationKey = "ContentExtractionConfigDefaultTrueMigrationComplete"
|
||||
const PlaybookRolesCreationMigrationKey = "PlaybookRolesCreationMigrationComplete"
|
||||
const FirstAdminSetupCompleteKey = "FirstAdminSetupComplete"
|
||||
|
||||
// This function migrates the default built in roles from code/config to the database.
|
||||
func (a *App) DoAdvancedPermissionsMigration() {
|
||||
@@ -434,6 +435,50 @@ func (s *Server) doPlaybooksRolesCreationMigration() {
|
||||
|
||||
}
|
||||
|
||||
// arbitrary choice, though if there is an longstanding installation with less than 10 messages,
|
||||
// putting the first admin through onboarding shouldn't be very disruptive.
|
||||
const existingInstallationPostsThreshold = 10
|
||||
|
||||
func (s *Server) doFirstAdminSetupCompleteMigration() {
|
||||
// Don't run the migration until the flag is turned on.
|
||||
|
||||
if !s.Config().FeatureFlags.UseCaseOnboarding {
|
||||
return
|
||||
}
|
||||
|
||||
// If the migration is already marked as completed, don't do it again.
|
||||
if _, err := s.Store.System().GetByName(FirstAdminSetupCompleteKey); err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
teams, err := s.Store.Team().GetAll()
|
||||
if err != nil {
|
||||
// can not confirm that admin has started in this case.
|
||||
return
|
||||
}
|
||||
|
||||
if len(teams) == 0 {
|
||||
// No teams, and no existing preference. This is most likely a new instance.
|
||||
// So do not mark that the admin has already done the first time setup.
|
||||
return
|
||||
}
|
||||
|
||||
// if there are teams, then if this isn't a new installation, there should be posts
|
||||
postCount, err := s.Store.Post().AnalyticsPostCount("", false, false)
|
||||
if err != nil || postCount < existingInstallationPostsThreshold {
|
||||
return
|
||||
}
|
||||
|
||||
system := model.System{
|
||||
Name: FirstAdminSetupCompleteKey,
|
||||
Value: "true",
|
||||
}
|
||||
|
||||
if err := s.Store.System().Save(&system); err != nil {
|
||||
mlog.Critical("Failed to mark first admin setup migration as completed.", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) DoAppMigrations() {
|
||||
a.Srv().doAppMigrations()
|
||||
}
|
||||
@@ -451,4 +496,5 @@ func (s *Server) doAppMigrations() {
|
||||
}
|
||||
s.doContentExtractionConfigDefaultTrueMigration()
|
||||
s.doPlaybooksRolesCreationMigration()
|
||||
s.doFirstAdminSetupCompleteMigration()
|
||||
}
|
||||
|
||||
@@ -7,9 +7,12 @@ import (
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/app/request"
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||
"github.com/mattermost/mattermost-server/v6/store"
|
||||
)
|
||||
|
||||
func (a *App) CompleteOnboarding(c *request.Context, request *model.CompleteOnboardingRequest) *model.AppError {
|
||||
@@ -56,7 +59,33 @@ func (a *App) CompleteOnboarding(c *request.Context, request *model.CompleteOnbo
|
||||
}(pluginID)
|
||||
}
|
||||
|
||||
firstAdminCompleteSetupObj := model.System{
|
||||
Name: model.SystemFirstAdminCompleteSetup,
|
||||
Value: "true",
|
||||
}
|
||||
|
||||
if err := a.Srv().Store.System().SaveOrUpdate(&firstAdminCompleteSetupObj); err != nil {
|
||||
return model.NewAppError("setFirstAdminCompleteSetup", "api.error_set_first_admin_complete_setup", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) GetOnboarding() (*model.System, *model.AppError) {
|
||||
firstAdminCompleteSetupObj, err := a.Srv().Store.System().GetByName(model.SystemFirstAdminCompleteSetup)
|
||||
if err != nil {
|
||||
var nfErr *store.ErrNotFound
|
||||
switch {
|
||||
case errors.As(err, &nfErr):
|
||||
return &model.System{
|
||||
Name: model.SystemFirstAdminCompleteSetup,
|
||||
Value: "false",
|
||||
}, nil
|
||||
default:
|
||||
return nil, model.NewAppError("getFirstAdminCompleteSetup", "api.error_get_first_admin_complete_setup", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
return firstAdminCompleteSetupObj, nil
|
||||
}
|
||||
|
||||
@@ -7121,6 +7121,28 @@ func (a *OpenTracingAppLayer) GetOAuthStateToken(token string) (*model.Token, *m
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetOnboarding() (*model.System, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetOnboarding")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store.SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store.SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1 := a.app.GetOnboarding()
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetOpenGraphMetadata(requestURL string) ([]byte, error) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetOpenGraphMetadata")
|
||||
|
||||
Ссылка в новой задаче
Block a user