[MM-58745] export/import: enable exporting and importing bots canonically (#28214)
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
3049191e2f
Коммит
f41d2d7774
@@ -19,6 +19,7 @@ type LineImportData struct {
|
||||
Team *TeamImportData `json:"team,omitempty"`
|
||||
Channel *ChannelImportData `json:"channel,omitempty"`
|
||||
User *UserImportData `json:"user,omitempty"`
|
||||
Bot *BotImportData `json:"bot,omitempty"`
|
||||
Post *PostImportData `json:"post,omitempty"`
|
||||
DirectChannel *DirectChannelImportData `json:"direct_channel,omitempty"`
|
||||
DirectPost *DirectPostImportData `json:"direct_post,omitempty"`
|
||||
@@ -54,24 +55,28 @@ type ChannelImportData struct {
|
||||
DeletedAt *int64 `json:"deleted_at,omitempty"`
|
||||
}
|
||||
|
||||
type Avatar struct {
|
||||
ProfileImage *string `json:"profile_image,omitempty"`
|
||||
ProfileImageData *zip.File `json:"-"`
|
||||
}
|
||||
|
||||
type UserImportData struct {
|
||||
ProfileImage *string `json:"profile_image,omitempty"`
|
||||
ProfileImageData *zip.File `json:"-"`
|
||||
Username *string `json:"username"`
|
||||
Email *string `json:"email"`
|
||||
AuthService *string `json:"auth_service"`
|
||||
AuthData *string `json:"auth_data,omitempty"`
|
||||
Password *string `json:"password,omitempty"`
|
||||
Nickname *string `json:"nickname"`
|
||||
FirstName *string `json:"first_name"`
|
||||
LastName *string `json:"last_name"`
|
||||
Position *string `json:"position"`
|
||||
Roles *string `json:"roles"`
|
||||
Locale *string `json:"locale"`
|
||||
UseMarkdownPreview *string `json:"feature_enabled_markdown_preview,omitempty"`
|
||||
UseFormatting *string `json:"formatting,omitempty"`
|
||||
ShowUnreadSection *string `json:"show_unread_section,omitempty"`
|
||||
DeleteAt *int64 `json:"delete_at,omitempty"`
|
||||
Avatar
|
||||
Username *string `json:"username"`
|
||||
Email *string `json:"email"`
|
||||
AuthService *string `json:"auth_service"`
|
||||
AuthData *string `json:"auth_data,omitempty"`
|
||||
Password *string `json:"password,omitempty"`
|
||||
Nickname *string `json:"nickname"`
|
||||
FirstName *string `json:"first_name"`
|
||||
LastName *string `json:"last_name"`
|
||||
Position *string `json:"position"`
|
||||
Roles *string `json:"roles"`
|
||||
Locale *string `json:"locale"`
|
||||
UseMarkdownPreview *string `json:"feature_enabled_markdown_preview,omitempty"`
|
||||
UseFormatting *string `json:"formatting,omitempty"`
|
||||
ShowUnreadSection *string `json:"show_unread_section,omitempty"`
|
||||
DeleteAt *int64 `json:"delete_at,omitempty"`
|
||||
|
||||
SendOnCtrlEnter *string `json:"send_on_ctrl_enter,omitempty"`
|
||||
CodeBlockCtrlEnter *string `json:"code_block_ctrl_enter,omitempty"`
|
||||
@@ -97,6 +102,15 @@ type UserImportData struct {
|
||||
CustomStatus *model.CustomStatus `json:"custom_status,omitempty"`
|
||||
}
|
||||
|
||||
type BotImportData struct {
|
||||
Avatar
|
||||
Username *string `json:"username"`
|
||||
Owner *string `json:"owner"`
|
||||
DisplayName *string `json:"display_name"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
DeleteAt *int64 `json:"delete_at,omitempty"`
|
||||
}
|
||||
|
||||
type UserNotifyPropsImportData struct {
|
||||
Desktop *string `json:"desktop"`
|
||||
DesktopSound *string `json:"desktop_sound"`
|
||||
|
||||
@@ -192,6 +192,8 @@ func ValidateChannelImportData(data *ChannelImportData) *model.AppError {
|
||||
func ValidateUserImportData(data *UserImportData) *model.AppError {
|
||||
if data.ProfileImage != nil && data.ProfileImageData == nil {
|
||||
if _, err := os.Stat(*data.ProfileImage); os.IsNotExist(err) {
|
||||
return model.NewAppError("BulkImport", "app.import.validate_user_import_data.profile_image.error", nil, "", http.StatusNotFound).Wrap(err)
|
||||
} else if err != nil {
|
||||
return model.NewAppError("BulkImport", "app.import.validate_user_import_data.profile_image.error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
}
|
||||
}
|
||||
@@ -312,6 +314,34 @@ func ValidateUserImportData(data *UserImportData) *model.AppError {
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValidateBotImportData(data *BotImportData) *model.AppError {
|
||||
if data.ProfileImage != nil && data.ProfileImageData == nil {
|
||||
if _, err := os.Stat(*data.ProfileImage); os.IsNotExist(err) {
|
||||
return model.NewAppError("BulkImport", "app.import.validate_user_import_data.profile_image.error", nil, "", http.StatusNotFound).Wrap(err)
|
||||
} else if err != nil {
|
||||
return model.NewAppError("BulkImport", "app.import.validate_user_import_data.profile_image.error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
if data.Username == nil {
|
||||
return model.NewAppError("BulkImport", "app.import.validate_user_import_data.username_missing.error", nil, "", http.StatusBadRequest)
|
||||
} else if !model.IsValidUsername(*data.Username) {
|
||||
return model.NewAppError("BulkImport", "app.import.validate_user_import_data.username_invalid.error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if data.DisplayName != nil && utf8.RuneCountInString(*data.DisplayName) > model.UserFirstNameMaxRunes {
|
||||
return model.NewAppError("BulkImport", "app.import.validate_user_import_data.first_name_length.error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if data.Owner == nil {
|
||||
return model.NewAppError("BulkImport", "app.import.validate_bot_import_data.owner_missing.error", nil, "", http.StatusBadRequest)
|
||||
} else if !model.IsValidUsername(*data.Owner) {
|
||||
return model.NewAppError("BulkImport", "app.import.validate_user_import_data.username_invalid.error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var validAuthServices = []string{
|
||||
"",
|
||||
model.UserAuthServiceEmail,
|
||||
|
||||
@@ -532,17 +532,19 @@ func TestImportValidateUserImportData(t *testing.T) {
|
||||
// Test a valid User with all fields populated.
|
||||
testsDir, _ := fileutils.FindDir("tests")
|
||||
data = UserImportData{
|
||||
ProfileImage: model.NewPointer(filepath.Join(testsDir, "test.png")),
|
||||
Username: model.NewPointer("bob"),
|
||||
Email: model.NewPointer("bob@example.com"),
|
||||
AuthService: model.NewPointer("ldap"),
|
||||
AuthData: model.NewPointer("bob"),
|
||||
Nickname: model.NewPointer("BobNick"),
|
||||
FirstName: model.NewPointer("Bob"),
|
||||
LastName: model.NewPointer("Blob"),
|
||||
Position: model.NewPointer("The Boss"),
|
||||
Roles: model.NewPointer("system_user"),
|
||||
Locale: model.NewPointer("en"),
|
||||
Avatar: Avatar{
|
||||
ProfileImage: model.NewPointer(filepath.Join(testsDir, "test.png")),
|
||||
},
|
||||
Username: model.NewPointer("bob"),
|
||||
Email: model.NewPointer("bob@example.com"),
|
||||
AuthService: model.NewPointer("ldap"),
|
||||
AuthData: model.NewPointer("bob"),
|
||||
Nickname: model.NewPointer("BobNick"),
|
||||
FirstName: model.NewPointer("Bob"),
|
||||
LastName: model.NewPointer("Blob"),
|
||||
Position: model.NewPointer("The Boss"),
|
||||
Roles: model.NewPointer("system_user"),
|
||||
Locale: model.NewPointer("en"),
|
||||
}
|
||||
err = ValidateUserImportData(&data)
|
||||
require.Nil(t, err, "Validation failed but should have been valid.")
|
||||
@@ -671,6 +673,56 @@ func TestImportValidateUserAuth(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportValidateBotImportData(t *testing.T) {
|
||||
// Test with minimum required valid properties.
|
||||
data := BotImportData{
|
||||
Username: model.NewPointer("bob"),
|
||||
DisplayName: model.NewPointer("Display Name"),
|
||||
Owner: model.NewPointer("owner"),
|
||||
}
|
||||
err := ValidateBotImportData(&data)
|
||||
require.Nil(t, err, "Validation failed but should have been valid.")
|
||||
|
||||
// Test with various invalid names.
|
||||
data.Username = nil
|
||||
err = ValidateBotImportData(&data)
|
||||
require.NotNil(t, err, "Should have failed due to nil Username.")
|
||||
|
||||
data.Username = model.NewPointer("")
|
||||
err = ValidateBotImportData(&data)
|
||||
require.NotNil(t, err, "Should have failed due to 0 length Username.")
|
||||
|
||||
data.Username = model.NewPointer(strings.Repeat("abcdefghij", 7))
|
||||
err = ValidateBotImportData(&data)
|
||||
require.NotNil(t, err, "Should have failed due to too long Username.")
|
||||
|
||||
data.Username = model.NewPointer("i am a username with spaces and !!!")
|
||||
err = ValidateBotImportData(&data)
|
||||
require.NotNil(t, err, "Should have failed due to invalid characters in Username.")
|
||||
|
||||
data.Username = model.NewPointer("bob")
|
||||
|
||||
// Invalid Display Name.
|
||||
data.DisplayName = model.NewPointer(strings.Repeat("abcdefghij", 7))
|
||||
err = ValidateBotImportData(&data)
|
||||
require.NotNil(t, err, "Should have failed due to too long DisplayName.")
|
||||
|
||||
data.DisplayName = model.NewPointer("Display Name")
|
||||
|
||||
// Invalid Owner Name.
|
||||
data.Owner = nil
|
||||
err = ValidateBotImportData(&data)
|
||||
require.NotNil(t, err, "Should have failed due to too long DisplayName.")
|
||||
|
||||
data.Owner = model.NewPointer("")
|
||||
err = ValidateBotImportData(&data)
|
||||
require.NotNil(t, err, "Should have failed due to too long DisplayName.")
|
||||
|
||||
data.Owner = model.NewPointer(strings.Repeat("abcdefghij", 7))
|
||||
err = ValidateBotImportData(&data)
|
||||
require.NotNil(t, err, "Should have failed due to too long OwnerID.")
|
||||
}
|
||||
|
||||
func TestImportValidateUserTeamsImportData(t *testing.T) {
|
||||
// Invalid Name.
|
||||
data := []UserTeamImportData{
|
||||
|
||||
Ссылка в новой задаче
Block a user