github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/cmd/mattermost/commands/sampledata.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package commands
     5  
     6  import (
     7  	"encoding/json"
     8  	"errors"
     9  	"fmt"
    10  	"io/ioutil"
    11  	"math/rand"
    12  	"os"
    13  	"path"
    14  	"sort"
    15  	"strings"
    16  	"time"
    17  
    18  	"github.com/icrowley/fake"
    19  	"github.com/mattermost/mattermost-server/v5/app"
    20  	"github.com/mattermost/mattermost-server/v5/audit"
    21  	"github.com/mattermost/mattermost-server/v5/model"
    22  	"github.com/mattermost/mattermost-server/v5/utils"
    23  	"github.com/spf13/cobra"
    24  )
    25  
    26  const (
    27  	DEACTIVATED_USER = "deactivated"
    28  	GUEST_USER       = "guest"
    29  )
    30  
    31  var SampleDataCmd = &cobra.Command{
    32  	Use:   "sampledata",
    33  	Short: "Generate sample data",
    34  	RunE:  sampleDataCmdF,
    35  }
    36  
    37  func init() {
    38  	SampleDataCmd.Flags().Int64P("seed", "s", 1, "Seed used for generating the random data (Different seeds generate different data).")
    39  	SampleDataCmd.Flags().IntP("teams", "t", 2, "The number of sample teams.")
    40  	SampleDataCmd.Flags().Int("channels-per-team", 10, "The number of sample channels per team.")
    41  	SampleDataCmd.Flags().IntP("users", "u", 15, "The number of sample users.")
    42  	SampleDataCmd.Flags().IntP("guests", "g", 1, "The number of sample guests.")
    43  	SampleDataCmd.Flags().Int("deactivated-users", 0, "The number of deactivated users.")
    44  	SampleDataCmd.Flags().Int("team-memberships", 2, "The number of sample team memberships per user.")
    45  	SampleDataCmd.Flags().Int("channel-memberships", 5, "The number of sample channel memberships per user in a team.")
    46  	SampleDataCmd.Flags().Int("posts-per-channel", 100, "The number of sample post per channel.")
    47  	SampleDataCmd.Flags().Int("direct-channels", 30, "The number of sample direct message channels.")
    48  	SampleDataCmd.Flags().Int("posts-per-direct-channel", 15, "The number of sample posts per direct message channel.")
    49  	SampleDataCmd.Flags().Int("group-channels", 15, "The number of sample group message channels.")
    50  	SampleDataCmd.Flags().Int("posts-per-group-channel", 30, "The number of sample posts per group message channel.")
    51  	SampleDataCmd.Flags().IntP("workers", "w", 2, "How many workers to run during the import.")
    52  	SampleDataCmd.Flags().String("profile-images", "", "Optional. Path to folder with images to randomly pick as user profile image.")
    53  	SampleDataCmd.Flags().StringP("bulk", "b", "", "Optional. Path to write a JSONL bulk file instead of loading into the database.")
    54  	RootCmd.AddCommand(SampleDataCmd)
    55  }
    56  
    57  func randomPastTime(seconds int) int64 {
    58  	now := time.Now()
    59  	today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.FixedZone("UTC", 0))
    60  	return (today.Unix() * 1000) - int64(rand.Intn(seconds*1000))
    61  }
    62  
    63  func sortedRandomDates(size int) []int64 {
    64  	dates := make([]int64, size)
    65  	for i := 0; i < size; i++ {
    66  		dates[i] = randomPastTime(50000)
    67  	}
    68  	sort.Slice(dates, func(a, b int) bool { return dates[a] < dates[b] })
    69  	return dates
    70  }
    71  
    72  func randomEmoji() string {
    73  	emojis := []string{"+1", "-1", "heart", "blush"}
    74  	return emojis[rand.Intn(len(emojis))]
    75  }
    76  
    77  func randomReaction(users []string, parentCreateAt int64) app.ReactionImportData {
    78  	user := users[rand.Intn(len(users))]
    79  	emoji := randomEmoji()
    80  	date := parentCreateAt + int64(rand.Intn(100000))
    81  	return app.ReactionImportData{
    82  		User:      &user,
    83  		EmojiName: &emoji,
    84  		CreateAt:  &date,
    85  	}
    86  }
    87  
    88  func randomReply(users []string, parentCreateAt int64) app.ReplyImportData {
    89  	user := users[rand.Intn(len(users))]
    90  	message := randomMessage(users)
    91  	date := parentCreateAt + int64(rand.Intn(100000))
    92  	return app.ReplyImportData{
    93  		User:     &user,
    94  		Message:  &message,
    95  		CreateAt: &date,
    96  	}
    97  }
    98  
    99  func randomMessage(users []string) string {
   100  	var message string
   101  	switch rand.Intn(30) {
   102  	case 0:
   103  		mention := users[rand.Intn(len(users))]
   104  		message = "@" + mention + " " + fake.Sentence()
   105  	case 1:
   106  		switch rand.Intn(2) {
   107  		case 0:
   108  			mattermostVideos := []string{"Q4MgnxbpZas", "BFo7E9-Kc_E", "LsMLR-BHsKg", "MRmGDhlMhNA", "mUOPxT7VgWc"}
   109  			message = "https://www.youtube.com/watch?v=" + mattermostVideos[rand.Intn(len(mattermostVideos))]
   110  		case 1:
   111  			mattermostTweets := []string{"943119062334353408", "949370809528832005", "948539688171819009", "939122439115681792", "938061722027425797"}
   112  			message = "https://twitter.com/mattermosthq/status/" + mattermostTweets[rand.Intn(len(mattermostTweets))]
   113  		}
   114  	case 2:
   115  		message = ""
   116  		if rand.Intn(2) == 0 {
   117  			message += fake.Sentence()
   118  		}
   119  		for i := 0; i < rand.Intn(4)+1; i++ {
   120  			message += "\n  * " + fake.Word()
   121  		}
   122  	default:
   123  		if rand.Intn(2) == 0 {
   124  			message = fake.Sentence()
   125  		} else {
   126  			message = fake.Paragraph()
   127  		}
   128  		if rand.Intn(3) == 0 {
   129  			message += "\n" + fake.Sentence()
   130  		}
   131  		if rand.Intn(3) == 0 {
   132  			message += "\n" + fake.Sentence()
   133  		}
   134  		if rand.Intn(3) == 0 {
   135  			message += "\n" + fake.Sentence()
   136  		}
   137  	}
   138  	return message
   139  }
   140  
   141  func sampleDataCmdF(command *cobra.Command, args []string) error {
   142  	a, err := InitDBCommandContextCobra(command)
   143  	if err != nil {
   144  		return err
   145  	}
   146  	defer a.Srv().Shutdown()
   147  
   148  	seed, err := command.Flags().GetInt64("seed")
   149  	if err != nil {
   150  		return errors.New("Invalid seed parameter")
   151  	}
   152  	bulk, err := command.Flags().GetString("bulk")
   153  	if err != nil {
   154  		return errors.New("Invalid bulk parameter")
   155  	}
   156  	teams, err := command.Flags().GetInt("teams")
   157  	if err != nil || teams < 0 {
   158  		return errors.New("Invalid teams parameter")
   159  	}
   160  	channelsPerTeam, err := command.Flags().GetInt("channels-per-team")
   161  	if err != nil || channelsPerTeam < 0 {
   162  		return errors.New("Invalid channels-per-team parameter")
   163  	}
   164  	users, err := command.Flags().GetInt("users")
   165  	if err != nil || users < 0 {
   166  		return errors.New("Invalid users parameter")
   167  	}
   168  	deactivatedUsers, err := command.Flags().GetInt("deactivated-users")
   169  	if err != nil || deactivatedUsers < 0 {
   170  		return errors.New("Invalid deactivated-users parameter")
   171  	}
   172  	guests, err := command.Flags().GetInt("guests")
   173  	if err != nil || guests < 0 {
   174  		return errors.New("Invalid guests parameter")
   175  	}
   176  	teamMemberships, err := command.Flags().GetInt("team-memberships")
   177  	if err != nil || teamMemberships < 0 {
   178  		return errors.New("Invalid team-memberships parameter")
   179  	}
   180  	channelMemberships, err := command.Flags().GetInt("channel-memberships")
   181  	if err != nil || channelMemberships < 0 {
   182  		return errors.New("Invalid channel-memberships parameter")
   183  	}
   184  	postsPerChannel, err := command.Flags().GetInt("posts-per-channel")
   185  	if err != nil || postsPerChannel < 0 {
   186  		return errors.New("Invalid posts-per-channel parameter")
   187  	}
   188  	directChannels, err := command.Flags().GetInt("direct-channels")
   189  	if err != nil || directChannels < 0 {
   190  		return errors.New("Invalid direct-channels parameter")
   191  	}
   192  	postsPerDirectChannel, err := command.Flags().GetInt("posts-per-direct-channel")
   193  	if err != nil || postsPerDirectChannel < 0 {
   194  		return errors.New("Invalid posts-per-direct-channel parameter")
   195  	}
   196  	groupChannels, err := command.Flags().GetInt("group-channels")
   197  	if err != nil || groupChannels < 0 {
   198  		return errors.New("Invalid group-channels parameter")
   199  	}
   200  	postsPerGroupChannel, err := command.Flags().GetInt("posts-per-group-channel")
   201  	if err != nil || postsPerGroupChannel < 0 {
   202  		return errors.New("Invalid posts-per-group-channel parameter")
   203  	}
   204  	workers, err := command.Flags().GetInt("workers")
   205  	if err != nil {
   206  		return errors.New("Invalid workers parameter")
   207  	}
   208  	profileImagesPath, err := command.Flags().GetString("profile-images")
   209  	if err != nil {
   210  		return errors.New("Invalid profile-images parameter")
   211  	}
   212  	profileImages := []string{}
   213  	if profileImagesPath != "" {
   214  		var profileImagesStat os.FileInfo
   215  		profileImagesStat, err = os.Stat(profileImagesPath)
   216  		if os.IsNotExist(err) {
   217  			return errors.New("Profile images folder doesn't exists.")
   218  		}
   219  		if !profileImagesStat.IsDir() {
   220  			return errors.New("profile-images parameters must be a folder path.")
   221  		}
   222  		var profileImagesFiles []os.FileInfo
   223  		profileImagesFiles, err = ioutil.ReadDir(profileImagesPath)
   224  		if err != nil {
   225  			return errors.New("Invalid profile-images parameter")
   226  		}
   227  		for _, profileImage := range profileImagesFiles {
   228  			profileImages = append(profileImages, path.Join(profileImagesPath, profileImage.Name()))
   229  		}
   230  		sort.Strings(profileImages)
   231  	}
   232  
   233  	if workers < 1 {
   234  		return errors.New("You must have at least one worker.")
   235  	}
   236  	if teamMemberships > teams {
   237  		return errors.New("You can't have more team memberships than teams.")
   238  	}
   239  	if channelMemberships > channelsPerTeam {
   240  		return errors.New("You can't have more channel memberships than channels per team.")
   241  	}
   242  
   243  	var bulkFile *os.File
   244  	switch bulk {
   245  	case "":
   246  		bulkFile, err = ioutil.TempFile("", ".mattermost-sample-data-")
   247  		defer os.Remove(bulkFile.Name())
   248  		if err != nil {
   249  			return errors.New("Unable to open temporary file.")
   250  		}
   251  	case "-":
   252  		bulkFile = os.Stdout
   253  	default:
   254  		bulkFile, err = os.OpenFile(bulk, os.O_RDWR|os.O_CREATE, 0755)
   255  		if err != nil {
   256  			return errors.New("Unable to write into the \"" + bulk + "\" file.")
   257  		}
   258  	}
   259  
   260  	encoder := json.NewEncoder(bulkFile)
   261  	version := 1
   262  	encoder.Encode(app.LineImportData{Type: "version", Version: &version})
   263  
   264  	fake.Seed(seed)
   265  	rand.Seed(seed)
   266  
   267  	teamsAndChannels := make(map[string][]string)
   268  	for i := 0; i < teams; i++ {
   269  		teamLine := createTeam(i)
   270  		teamsAndChannels[*teamLine.Team.Name] = []string{}
   271  		encoder.Encode(teamLine)
   272  	}
   273  
   274  	teamsList := []string{}
   275  	for teamName := range teamsAndChannels {
   276  		teamsList = append(teamsList, teamName)
   277  	}
   278  	sort.Strings(teamsList)
   279  
   280  	for _, teamName := range teamsList {
   281  		for i := 0; i < channelsPerTeam; i++ {
   282  			channelLine := createChannel(i, teamName)
   283  			teamsAndChannels[teamName] = append(teamsAndChannels[teamName], *channelLine.Channel.Name)
   284  			encoder.Encode(channelLine)
   285  		}
   286  	}
   287  
   288  	allUsers := []string{}
   289  	for i := 0; i < users; i++ {
   290  		userLine := createUser(i, teamMemberships, channelMemberships, teamsAndChannels, profileImages, "")
   291  		encoder.Encode(userLine)
   292  		allUsers = append(allUsers, *userLine.User.Username)
   293  	}
   294  	for i := 0; i < guests; i++ {
   295  		userLine := createUser(i, teamMemberships, channelMemberships, teamsAndChannels, profileImages, GUEST_USER)
   296  		encoder.Encode(userLine)
   297  		allUsers = append(allUsers, *userLine.User.Username)
   298  	}
   299  	for i := 0; i < deactivatedUsers; i++ {
   300  		userLine := createUser(i, teamMemberships, channelMemberships, teamsAndChannels, profileImages, DEACTIVATED_USER)
   301  		encoder.Encode(userLine)
   302  		allUsers = append(allUsers, *userLine.User.Username)
   303  	}
   304  
   305  	for team, channels := range teamsAndChannels {
   306  		for _, channel := range channels {
   307  			dates := sortedRandomDates(postsPerChannel)
   308  
   309  			for i := 0; i < postsPerChannel; i++ {
   310  				postLine := createPost(team, channel, allUsers, dates[i])
   311  				encoder.Encode(postLine)
   312  			}
   313  		}
   314  	}
   315  
   316  	for i := 0; i < directChannels; i++ {
   317  		user1 := allUsers[rand.Intn(len(allUsers))]
   318  		user2 := allUsers[rand.Intn(len(allUsers))]
   319  		channelLine := createDirectChannel([]string{user1, user2})
   320  		encoder.Encode(channelLine)
   321  	}
   322  
   323  	for i := 0; i < directChannels; i++ {
   324  		user1 := allUsers[rand.Intn(len(allUsers))]
   325  		user2 := allUsers[rand.Intn(len(allUsers))]
   326  
   327  		dates := sortedRandomDates(postsPerDirectChannel)
   328  		for j := 0; j < postsPerDirectChannel; j++ {
   329  			postLine := createDirectPost([]string{user1, user2}, dates[j])
   330  			encoder.Encode(postLine)
   331  		}
   332  	}
   333  
   334  	for i := 0; i < groupChannels; i++ {
   335  		users := []string{}
   336  		totalUsers := 3 + rand.Intn(3)
   337  		for len(users) < totalUsers {
   338  			user := allUsers[rand.Intn(len(allUsers))]
   339  			if !utils.StringInSlice(user, users) {
   340  				users = append(users, user)
   341  			}
   342  		}
   343  		channelLine := createDirectChannel(users)
   344  		encoder.Encode(channelLine)
   345  	}
   346  
   347  	for i := 0; i < groupChannels; i++ {
   348  		users := []string{}
   349  		totalUsers := 3 + rand.Intn(3)
   350  		for len(users) < totalUsers {
   351  			user := allUsers[rand.Intn(len(allUsers))]
   352  			if !utils.StringInSlice(user, users) {
   353  				users = append(users, user)
   354  			}
   355  		}
   356  
   357  		dates := sortedRandomDates(postsPerGroupChannel)
   358  		for j := 0; j < postsPerGroupChannel; j++ {
   359  			postLine := createDirectPost(users, dates[j])
   360  			encoder.Encode(postLine)
   361  		}
   362  	}
   363  
   364  	if bulk == "" {
   365  		_, err := bulkFile.Seek(0, 0)
   366  		if err != nil {
   367  			return errors.New("Unable to read correctly the temporary file.")
   368  		}
   369  
   370  		var importErr *model.AppError
   371  		importErr, lineNumber := a.BulkImport(bulkFile, false, workers)
   372  		if importErr != nil {
   373  			return fmt.Errorf("%s: %s, %s (line: %d)", importErr.Where, importErr.Message, importErr.DetailedError, lineNumber)
   374  		}
   375  		auditRec := a.MakeAuditRecord("sampleData", audit.Success)
   376  		auditRec.AddMeta("file", bulkFile.Name())
   377  		a.LogAuditRec(auditRec, nil)
   378  	} else if bulk != "-" {
   379  		err := bulkFile.Close()
   380  		if err != nil {
   381  			return errors.New("Unable to close correctly the output file")
   382  		}
   383  	}
   384  
   385  	return nil
   386  }
   387  
   388  func createUser(idx int, teamMemberships int, channelMemberships int, teamsAndChannels map[string][]string, profileImages []string, userType string) app.LineImportData {
   389  	firstName := fake.FirstName()
   390  	lastName := fake.LastName()
   391  	position := fake.JobTitle()
   392  
   393  	username := fmt.Sprintf("%s.%s", strings.ToLower(firstName), strings.ToLower(lastName))
   394  	roles := "system_user"
   395  
   396  	var password string
   397  	var email string
   398  
   399  	switch userType {
   400  	case GUEST_USER:
   401  		password = fmt.Sprintf("SampleGu@st-%d", idx)
   402  		email = fmt.Sprintf("guest-%d@sample.mattermost.com", idx)
   403  		roles = "system_guest"
   404  		if idx == 0 {
   405  			username = "guest"
   406  			password = "SampleGu@st1"
   407  			email = "guest@sample.mattermost.com"
   408  		}
   409  	case DEACTIVATED_USER:
   410  		password = fmt.Sprintf("SampleDe@ctivated-%d", idx)
   411  		email = fmt.Sprintf("deactivated-%d@sample.mattermost.com", idx)
   412  	default:
   413  		password = fmt.Sprintf("SampleUs@r-%d", idx)
   414  		email = fmt.Sprintf("user-%d@sample.mattermost.com", idx)
   415  		if idx == 0 {
   416  			username = "sysadmin"
   417  			password = "Sys@dmin-sample1"
   418  			email = "sysadmin@sample.mattermost.com"
   419  		} else if idx == 1 {
   420  			username = "user-1"
   421  		}
   422  
   423  		if idx%5 == 0 {
   424  			roles = "system_admin system_user"
   425  		}
   426  	}
   427  
   428  	// The 75% of the users have custom profile image
   429  	var profileImage *string = nil
   430  	if rand.Intn(4) != 0 {
   431  		profileImageSelector := rand.Int()
   432  		if len(profileImages) > 0 {
   433  			profileImage = &profileImages[profileImageSelector%len(profileImages)]
   434  		}
   435  	}
   436  
   437  	useMilitaryTime := "false"
   438  	if idx != 0 && rand.Intn(2) == 0 {
   439  		useMilitaryTime = "true"
   440  	}
   441  
   442  	collapsePreviews := "false"
   443  	if idx != 0 && rand.Intn(2) == 0 {
   444  		collapsePreviews = "true"
   445  	}
   446  
   447  	messageDisplay := "clean"
   448  	if idx != 0 && rand.Intn(2) == 0 {
   449  		messageDisplay = "compact"
   450  	}
   451  
   452  	channelDisplayMode := "full"
   453  	if idx != 0 && rand.Intn(2) == 0 {
   454  		channelDisplayMode = "centered"
   455  	}
   456  
   457  	// Some users has nickname
   458  	nickname := ""
   459  	if rand.Intn(5) == 0 {
   460  		nickname = fake.Company()
   461  	}
   462  
   463  	// sysadmin, user-1 and user-2 users skip tutorial steps
   464  	// Other half of users also skip tutorial steps
   465  	tutorialStep := "999"
   466  	if idx > 2 {
   467  		switch rand.Intn(6) {
   468  		case 1:
   469  			tutorialStep = "1"
   470  		case 2:
   471  			tutorialStep = "2"
   472  		case 3:
   473  			tutorialStep = "3"
   474  		}
   475  	}
   476  
   477  	teams := []app.UserTeamImportData{}
   478  	possibleTeams := []string{}
   479  	for teamName := range teamsAndChannels {
   480  		possibleTeams = append(possibleTeams, teamName)
   481  	}
   482  	sort.Strings(possibleTeams)
   483  	for x := 0; x < teamMemberships; x++ {
   484  		if len(possibleTeams) == 0 {
   485  			break
   486  		}
   487  		position := rand.Intn(len(possibleTeams))
   488  		team := possibleTeams[position]
   489  		possibleTeams = append(possibleTeams[:position], possibleTeams[position+1:]...)
   490  		if teamChannels, err := teamsAndChannels[team]; err {
   491  			teams = append(teams, createTeamMembership(channelMemberships, teamChannels, &team, userType == GUEST_USER))
   492  		}
   493  	}
   494  
   495  	var deleteAt int64
   496  	if userType == DEACTIVATED_USER {
   497  		deleteAt = model.GetMillis()
   498  	}
   499  
   500  	user := app.UserImportData{
   501  		ProfileImage:       profileImage,
   502  		Username:           &username,
   503  		Email:              &email,
   504  		Password:           &password,
   505  		Nickname:           &nickname,
   506  		FirstName:          &firstName,
   507  		LastName:           &lastName,
   508  		Position:           &position,
   509  		Roles:              &roles,
   510  		Teams:              &teams,
   511  		UseMilitaryTime:    &useMilitaryTime,
   512  		CollapsePreviews:   &collapsePreviews,
   513  		MessageDisplay:     &messageDisplay,
   514  		ChannelDisplayMode: &channelDisplayMode,
   515  		TutorialStep:       &tutorialStep,
   516  		DeleteAt:           &deleteAt,
   517  	}
   518  	return app.LineImportData{
   519  		Type: "user",
   520  		User: &user,
   521  	}
   522  }
   523  
   524  func createTeamMembership(numOfchannels int, teamChannels []string, teamName *string, guest bool) app.UserTeamImportData {
   525  	roles := "team_user"
   526  	if guest {
   527  		roles = "team_guest"
   528  	} else if rand.Intn(5) == 0 {
   529  		roles = "team_user team_admin"
   530  	}
   531  	channels := []app.UserChannelImportData{}
   532  	teamChannelsCopy := append([]string(nil), teamChannels...)
   533  	for x := 0; x < numOfchannels; x++ {
   534  		if len(teamChannelsCopy) == 0 {
   535  			break
   536  		}
   537  		position := rand.Intn(len(teamChannelsCopy))
   538  		channelName := teamChannelsCopy[position]
   539  		teamChannelsCopy = append(teamChannelsCopy[:position], teamChannelsCopy[position+1:]...)
   540  		channels = append(channels, createChannelMembership(channelName, guest))
   541  	}
   542  
   543  	return app.UserTeamImportData{
   544  		Name:     teamName,
   545  		Roles:    &roles,
   546  		Channels: &channels,
   547  	}
   548  }
   549  
   550  func createChannelMembership(channelName string, guest bool) app.UserChannelImportData {
   551  	roles := "channel_user"
   552  	if guest {
   553  		roles = "channel_guest"
   554  	} else if rand.Intn(5) == 0 {
   555  		roles = "channel_user channel_admin"
   556  	}
   557  	favorite := rand.Intn(5) == 0
   558  
   559  	return app.UserChannelImportData{
   560  		Name:     &channelName,
   561  		Roles:    &roles,
   562  		Favorite: &favorite,
   563  	}
   564  }
   565  
   566  func getSampleTeamName(idx int) string {
   567  	for {
   568  		name := fmt.Sprintf("%s-%d", fake.Word(), idx)
   569  		if !model.IsReservedTeamName(name) {
   570  			return name
   571  		}
   572  	}
   573  }
   574  
   575  func createTeam(idx int) app.LineImportData {
   576  	displayName := fake.Word()
   577  	name := getSampleTeamName(idx)
   578  	allowOpenInvite := rand.Intn(2) == 0
   579  
   580  	description := fake.Paragraph()
   581  	if len(description) > 255 {
   582  		description = description[0:255]
   583  	}
   584  
   585  	teamType := "O"
   586  	if rand.Intn(2) == 0 {
   587  		teamType = "I"
   588  	}
   589  
   590  	team := app.TeamImportData{
   591  		DisplayName:     &displayName,
   592  		Name:            &name,
   593  		AllowOpenInvite: &allowOpenInvite,
   594  		Description:     &description,
   595  		Type:            &teamType,
   596  	}
   597  	return app.LineImportData{
   598  		Type: "team",
   599  		Team: &team,
   600  	}
   601  }
   602  
   603  func createChannel(idx int, teamName string) app.LineImportData {
   604  	displayName := fake.Word()
   605  	name := fmt.Sprintf("%s-%d", fake.Word(), idx)
   606  	header := fake.Paragraph()
   607  	purpose := fake.Paragraph()
   608  
   609  	if len(purpose) > 250 {
   610  		purpose = purpose[0:250]
   611  	}
   612  
   613  	channelType := "P"
   614  	if rand.Intn(2) == 0 {
   615  		channelType = "O"
   616  	}
   617  
   618  	channel := app.ChannelImportData{
   619  		Team:        &teamName,
   620  		Name:        &name,
   621  		DisplayName: &displayName,
   622  		Type:        &channelType,
   623  		Header:      &header,
   624  		Purpose:     &purpose,
   625  	}
   626  	return app.LineImportData{
   627  		Type:    "channel",
   628  		Channel: &channel,
   629  	}
   630  }
   631  
   632  func createPost(team string, channel string, allUsers []string, createAt int64) app.LineImportData {
   633  	message := randomMessage(allUsers)
   634  	create_at := createAt
   635  	user := allUsers[rand.Intn(len(allUsers))]
   636  
   637  	// Some messages are flagged by an user
   638  	flagged_by := []string{}
   639  	if rand.Intn(10) == 0 {
   640  		flagged_by = append(flagged_by, allUsers[rand.Intn(len(allUsers))])
   641  	}
   642  
   643  	reactions := []app.ReactionImportData{}
   644  	if rand.Intn(10) == 0 {
   645  		for {
   646  			reactions = append(reactions, randomReaction(allUsers, create_at))
   647  			if rand.Intn(3) == 0 {
   648  				break
   649  			}
   650  		}
   651  	}
   652  
   653  	replies := []app.ReplyImportData{}
   654  	if rand.Intn(10) == 0 {
   655  		for {
   656  			replies = append(replies, randomReply(allUsers, create_at))
   657  			if rand.Intn(4) == 0 {
   658  				break
   659  			}
   660  		}
   661  	}
   662  
   663  	post := app.PostImportData{
   664  		Team:      &team,
   665  		Channel:   &channel,
   666  		User:      &user,
   667  		Message:   &message,
   668  		CreateAt:  &create_at,
   669  		FlaggedBy: &flagged_by,
   670  		Reactions: &reactions,
   671  		Replies:   &replies,
   672  	}
   673  	return app.LineImportData{
   674  		Type: "post",
   675  		Post: &post,
   676  	}
   677  }
   678  
   679  func createDirectChannel(members []string) app.LineImportData {
   680  	header := fake.Sentence()
   681  
   682  	channel := app.DirectChannelImportData{
   683  		Members: &members,
   684  		Header:  &header,
   685  	}
   686  	return app.LineImportData{
   687  		Type:          "direct_channel",
   688  		DirectChannel: &channel,
   689  	}
   690  }
   691  
   692  func createDirectPost(members []string, createAt int64) app.LineImportData {
   693  	message := randomMessage(members)
   694  	create_at := createAt
   695  	user := members[rand.Intn(len(members))]
   696  
   697  	// Some messages are flagged by an user
   698  	flagged_by := []string{}
   699  	if rand.Intn(10) == 0 {
   700  		flagged_by = append(flagged_by, members[rand.Intn(len(members))])
   701  	}
   702  
   703  	reactions := []app.ReactionImportData{}
   704  	if rand.Intn(10) == 0 {
   705  		for {
   706  			reactions = append(reactions, randomReaction(members, create_at))
   707  			if rand.Intn(3) == 0 {
   708  				break
   709  			}
   710  		}
   711  	}
   712  
   713  	replies := []app.ReplyImportData{}
   714  	if rand.Intn(10) == 0 {
   715  		for {
   716  			replies = append(replies, randomReply(members, create_at))
   717  			if rand.Intn(4) == 0 {
   718  				break
   719  			}
   720  		}
   721  	}
   722  
   723  	post := app.DirectPostImportData{
   724  		ChannelMembers: &members,
   725  		User:           &user,
   726  		Message:        &message,
   727  		CreateAt:       &create_at,
   728  		FlaggedBy:      &flagged_by,
   729  		Reactions:      &reactions,
   730  		Replies:        &replies,
   731  	}
   732  	return app.LineImportData{
   733  		Type:       "direct_post",
   734  		DirectPost: &post,
   735  	}
   736  }