github.com/litesolutions/justifay-api@v1.0.0-2.0.20220707114139-46f28a909481/server/usergroup.go (about)

     1  package server
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"net/url"
     8  	"regexp"
     9  	"time"
    10  
    11  	uuid "github.com/google/uuid"
    12  	"github.com/goware/urlx"
    13  	"github.com/uptrace/bun"
    14  
    15  	"github.com/litesolutions/justifay-api/model"
    16  	pbUser "github.com/litesolutions/justifay-api/proto/user"
    17  )
    18  
    19  // // Server implements the UserService
    20  // type Server struct {
    21  // 	db *bun.DB
    22  // }
    23  
    24  // // New creates an instance of our server
    25  // func New(db *bun.DB) *Server {
    26  // 	return &Server{db: db}
    27  // }
    28  
    29  // AddUser gets a user to the in-memory store.
    30  func (s *Server) AddUserGroup(ctx context.Context, usergroup *pbUser.UserGroupCreateRequest) (*pbUser.UserRequest, error) {
    31  
    32  	requiredErr := s.checkRequiredAddUserGroupAttributes(ctx, usergroup)
    33  
    34  	if requiredErr != nil {
    35  		return nil, requiredErr
    36  	}
    37  
    38  	OwnerUUID, err := uuid.Parse(usergroup.Id)
    39  
    40  	if err != nil {
    41  		return nil, errors.New("supplied user_id is not a valid UUID")
    42  	}
    43  
    44  	existingGroupCount, _ := s.db.NewSelect().
    45  		Model((*model.UserGroup)(nil)).
    46  		Where("owner_id = ?", OwnerUUID).
    47  		Count(ctx)
    48  
    49  	owningUser := new(model.User)
    50  
    51  	err = s.db.NewSelect().
    52  		Model(owningUser).
    53  		Where("id = ?", OwnerUUID).
    54  		Scan(ctx)
    55  
    56  	if err != nil {
    57  		return nil, errors.New("supplied owner_id could not be found in Users")
    58  	}
    59  
    60  	if owningUser.RoleID == int32(model.UserRole) && existingGroupCount > 0 {
    61  		return nil, errors.New("supplied owner_id is a user and already has a user group profile")
    62  	}
    63  
    64  	group := new(model.GroupType)
    65  
    66  	err = s.db.NewSelect().
    67  		Model(group).
    68  		Where("name = ?", usergroup.GroupType).
    69  		Scan(ctx)
    70  
    71  	if err != nil {
    72  		return nil, errors.New("supplied group type is not valid")
    73  	}
    74  
    75  	AvatarUUID, err := uuid.Parse(usergroup.Avatar)
    76  
    77  	if usergroup.Avatar != "" && err != nil {
    78  		return nil, errors.New("supplied avatar is not a valid UUID")
    79  	}
    80  
    81  	BannerUUID, err := uuid.Parse(usergroup.Banner)
    82  
    83  	if usergroup.Banner != "" && err != nil {
    84  		return nil, errors.New("supplied banner is not a valid UUID")
    85  	}
    86  
    87  	newUserGroup := &model.UserGroup{
    88  		OwnerID:     OwnerUUID,
    89  		TypeID:      group.ID,
    90  		Type:        group,
    91  		DisplayName: usergroup.DisplayName,
    92  		Description: usergroup.Description,
    93  		ShortBio:    usergroup.ShortBio,
    94  		Avatar:      AvatarUUID,
    95  		Banner:      BannerUUID,
    96  		GroupEmail:  usergroup.GroupEmail,
    97  	}
    98  
    99  	if usergroup.Tags != nil {
   100  		tags := make([]model.Tag, len(usergroup.Tags))
   101  		names := make([]string, len(usergroup.Tags))
   102  		tagType := "genre" // defaults to genre tags for now
   103  
   104  		for i := range usergroup.Tags {
   105  			tag := model.Tag{
   106  				Name: usergroup.Tags[i],
   107  				Type: tagType,
   108  			}
   109  			tag.ID = uuid.Must(uuid.NewRandom())
   110  			names[i] = tag.Name
   111  			tags[i] = tag
   112  		}
   113  
   114  		existing := []model.Tag{}
   115  
   116  		// find existing tags
   117  		err := s.db.NewSelect().
   118  			Model(&existing).
   119  			Where("type = ? AND name IN (?)", tagType, bun.In(names)).
   120  			Scan(ctx)
   121  
   122  		if err != nil {
   123  			return nil, err
   124  		}
   125  
   126  		var result []uuid.UUID
   127  		var insert []model.Tag
   128  
   129  		for l := range tags {
   130  			var seen uuid.UUID
   131  
   132  			for e := range existing {
   133  				if existing[e].Name == tags[l].Name {
   134  					seen = existing[e].ID
   135  					break
   136  				}
   137  			}
   138  
   139  			if seen == uuid.Nil {
   140  				insert = append(insert, tags[l])
   141  				result = append(result, tags[l].ID)
   142  			} else {
   143  				result = append(result, seen)
   144  			}
   145  		}
   146  
   147  		if len(insert) > 0 {
   148  			_, err := s.db.
   149  				NewInsert().
   150  				Model(&insert).
   151  				Exec(ctx)
   152  
   153  			if err != nil {
   154  				return nil, err
   155  			}
   156  		}
   157  
   158  		newUserGroup.Tags = result
   159  	}
   160  
   161  	if usergroup.Links != nil {
   162  		uris := make([]string, len(usergroup.Links))
   163  		links := make([]model.Link, len(usergroup.Links))
   164  
   165  		for i := range usergroup.Links {
   166  			uri := usergroup.Links[i]
   167  
   168  			platform := s.getPlatform(uri)
   169  
   170  			link := model.Link{
   171  				URI:      usergroup.Links[i],
   172  				Platform: platform,
   173  			}
   174  			link.ID = uuid.Must(uuid.NewRandom())
   175  			uris[i] = link.URI
   176  			links[i] = link
   177  		}
   178  
   179  		existing := []model.Link{}
   180  
   181  		// find existing links
   182  		err = s.db.NewSelect().
   183  			Model(&existing).
   184  			Where("uri IN (?)", bun.In(uris)).
   185  			Scan(ctx)
   186  
   187  		if err != nil {
   188  			return nil, err
   189  		}
   190  
   191  		var result []uuid.UUID
   192  		var insert []model.Link
   193  
   194  		for l := range links {
   195  			var seen uuid.UUID
   196  
   197  			for e := range existing {
   198  				if existing[e].URI == links[l].URI {
   199  					seen = existing[e].ID
   200  					break
   201  				}
   202  			}
   203  
   204  			if seen == uuid.Nil {
   205  				insert = append(insert, links[l])
   206  				result = append(result, links[l].ID)
   207  			} else {
   208  				result = append(result, seen)
   209  			}
   210  		}
   211  
   212  		if len(insert) > 0 {
   213  			_, err := s.db.
   214  				NewInsert().
   215  				Model(&insert).
   216  				Exec(ctx)
   217  
   218  			if err != nil {
   219  				return nil, err
   220  			}
   221  		}
   222  
   223  		newUserGroup.Links = result
   224  	}
   225  
   226  	newUserGroup.ID = uuid.Must(uuid.NewRandom())
   227  	newUserGroup.CreatedAt = time.Now().UTC()
   228  
   229  	_, err = s.db.NewInsert().Model(newUserGroup).Exec(ctx)
   230  
   231  	if err != nil {
   232  		return nil, err
   233  	}
   234  
   235  	return &pbUser.UserRequest{Id: newUserGroup.ID.String()}, nil
   236  }
   237  
   238  // UpdateUser updates a users basic attributes
   239  func (s *Server) UpdateUserGroup(ctx context.Context, UserGroupUpdateRequest *pbUser.UserGroupUpdateRequest) (*pbUser.Empty, error) {
   240  
   241  	var updatedUserGroupValues = make(map[string]interface{})
   242  
   243  	if UserGroupUpdateRequest.GroupEmail != nil {
   244  		updatedUserGroupValues["group_email_address"] = *UserGroupUpdateRequest.GroupEmail
   245  		re := regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
   246  		if !re.MatchString(*UserGroupUpdateRequest.GroupEmail) {
   247  			return nil, errors.New("group email address must be a valid email")
   248  		}
   249  	}
   250  	if UserGroupUpdateRequest.DisplayName != nil {
   251  		updatedUserGroupValues["display_name"] = *UserGroupUpdateRequest.DisplayName
   252  	}
   253  	if UserGroupUpdateRequest.Description != nil {
   254  		updatedUserGroupValues["description"] = *UserGroupUpdateRequest.Description
   255  	}
   256  	if UserGroupUpdateRequest.ShortBio != nil {
   257  		updatedUserGroupValues["short_bio"] = *UserGroupUpdateRequest.ShortBio
   258  	}
   259  	if UserGroupUpdateRequest.GroupType != nil {
   260  		group := new(model.GroupType)
   261  
   262  		err := s.db.NewSelect().
   263  			Model(group).
   264  			Where("name = ?", UserGroupUpdateRequest.GroupType).
   265  			Scan(ctx)
   266  
   267  		if err != nil {
   268  			return nil, errors.New("supplied group type is not valid")
   269  		}
   270  
   271  		updatedUserGroupValues["type_id"] = group.ID
   272  	}
   273  	if UserGroupUpdateRequest.OwnerId != nil {
   274  		updatedUserGroupValues["owner_id"] = *UserGroupUpdateRequest.OwnerId
   275  	}
   276  	if UserGroupUpdateRequest.Avatar != nil {
   277  		updatedUserGroupValues["avatar"] = *UserGroupUpdateRequest.Avatar
   278  	}
   279  	if UserGroupUpdateRequest.Banner != nil {
   280  		updatedUserGroupValues["banner"] = *UserGroupUpdateRequest.Banner
   281  	}
   282  
   283  	if UserGroupUpdateRequest.Tags != nil {
   284  		tags := make([]model.Tag, len(UserGroupUpdateRequest.Tags))
   285  		names := make([]string, len(UserGroupUpdateRequest.Tags))
   286  		tagType := "genre" // defaults to genre tags for now
   287  
   288  		for i := range UserGroupUpdateRequest.Tags {
   289  			tag := model.Tag{
   290  				Name: UserGroupUpdateRequest.Tags[i],
   291  				Type: tagType,
   292  			}
   293  			tag.ID = uuid.Must(uuid.NewRandom())
   294  			names[i] = tag.Name
   295  			tags[i] = tag
   296  		}
   297  
   298  		existing := []model.Tag{}
   299  
   300  		// find existing tags
   301  		err := s.db.NewSelect().
   302  			Model(&existing).
   303  			Where("type = ? AND name IN (?)", tagType, bun.In(names)).
   304  			Scan(ctx)
   305  
   306  		if err != nil {
   307  			return nil, err
   308  		}
   309  
   310  		var result []uuid.UUID
   311  		var insert []model.Tag
   312  
   313  		for l := range tags {
   314  			var seen uuid.UUID
   315  
   316  			for e := range existing {
   317  				if existing[e].Name == tags[l].Name {
   318  					seen = existing[e].ID
   319  					break
   320  				}
   321  			}
   322  
   323  			if seen == uuid.Nil {
   324  				insert = append(insert, tags[l])
   325  				result = append(result, tags[l].ID)
   326  			} else {
   327  				result = append(result, seen)
   328  			}
   329  		}
   330  
   331  		if len(insert) > 0 {
   332  			_, err := s.db.
   333  				NewInsert().
   334  				Model(&insert).
   335  				Exec(ctx)
   336  
   337  			if err != nil {
   338  				return nil, err
   339  			}
   340  		}
   341  
   342  		updatedUserGroupValues["tags"] = result
   343  	}
   344  
   345  	if UserGroupUpdateRequest.Links != nil {
   346  		links := make([]model.Link, len(UserGroupUpdateRequest.Links))
   347  		uris := make([]string, len(UserGroupUpdateRequest.Links))
   348  
   349  		for i := range UserGroupUpdateRequest.Links {
   350  			uri := UserGroupUpdateRequest.Links[i]
   351  			platform := s.getPlatform(uri)
   352  
   353  			link := model.Link{
   354  				URI:      uri,
   355  				Platform: platform,
   356  			}
   357  			link.ID = uuid.Must(uuid.NewRandom())
   358  			uris[i] = link.URI
   359  			links[i] = link
   360  		}
   361  
   362  		existing := []model.Link{}
   363  
   364  		// find existing links
   365  		err := s.db.NewSelect().
   366  			Model(&existing).
   367  			Where("uri IN (?)", bun.In(uris)).
   368  			Scan(ctx)
   369  
   370  		if err != nil {
   371  			return nil, err
   372  		}
   373  
   374  		var result []uuid.UUID
   375  		var insert []model.Link
   376  
   377  		for l := range links {
   378  			var seen uuid.UUID
   379  
   380  			for e := range existing {
   381  				if existing[e].URI == links[l].URI {
   382  					seen = existing[e].ID
   383  					break
   384  				}
   385  			}
   386  
   387  			if seen == uuid.Nil {
   388  				insert = append(insert, links[l])
   389  				result = append(result, links[l].ID)
   390  			} else {
   391  				result = append(result, seen)
   392  			}
   393  		}
   394  
   395  		if len(insert) > 0 {
   396  			_, err := s.db.
   397  				NewInsert().
   398  				Model(&insert).
   399  				Exec(ctx)
   400  
   401  			if err != nil {
   402  				return nil, err
   403  			}
   404  		}
   405  
   406  		// TODO prune orphan links?
   407  
   408  		updatedUserGroupValues["links"] = result
   409  	}
   410  
   411  	updatedUserGroupValues["updated_at"] = time.Now().UTC()
   412  
   413  	rows, err := s.db.NewUpdate().Model(&updatedUserGroupValues).TableExpr("user_groups").Where("id = ?", UserGroupUpdateRequest.Id).Exec(ctx)
   414  
   415  	if err != nil {
   416  		return nil, err
   417  	}
   418  
   419  	number, _ := rows.RowsAffected()
   420  
   421  	if number == 0 {
   422  		return nil, errors.New("warning: no rows were updated")
   423  	}
   424  
   425  	return &pbUser.Empty{}, nil
   426  }
   427  
   428  // DeleteUser Deletes a user from the DB
   429  func (s *Server) DeleteUserGroup(ctx context.Context, usergroup *pbUser.UserGroupRequest) (*pbUser.Empty, error) {
   430  	u := new(model.UserGroup)
   431  
   432  	_, err := s.db.NewDelete().
   433  		Model(u).
   434  		Where("id = ?", usergroup.Id).
   435  		Exec(ctx)
   436  
   437  	if err != nil {
   438  		return nil, err
   439  	}
   440  
   441  	return &pbUser.Empty{}, nil
   442  }
   443  
   444  // GetUserGroup returns details of single user group
   445  func (s *Server) GetUserGroup(ctx context.Context, usergrouprequest *pbUser.UserGroupRequest) (*pbUser.UserGroupPublicResponse, error) {
   446  
   447  	usergroup := new(model.UserGroup)
   448  
   449  	err := s.db.NewSelect().
   450  		Model(usergroup).
   451  		Where("id = ?", usergrouprequest.Id).
   452  		Scan(ctx)
   453  
   454  	if err != nil {
   455  		return nil, err
   456  	}
   457  
   458  	group := new(model.GroupType)
   459  
   460  	err = s.db.NewSelect().
   461  		Model(group).
   462  		Where("id = ?", usergroup.TypeID).
   463  		Scan(ctx)
   464  
   465  	if err != nil {
   466  		return nil, errors.New("supplied group type is not valid")
   467  	}
   468  
   469  	links := []model.Link{}
   470  
   471  	if len(usergroup.Links) > 0 {
   472  		err = s.db.NewSelect().
   473  			Model(&links).
   474  			Where("id IN (?)", bun.In(usergroup.Links)).
   475  			Scan(ctx)
   476  
   477  		if err != nil {
   478  			return nil, err
   479  		}
   480  	}
   481  
   482  	usergroupLinks := []*pbUser.Link{}
   483  
   484  	if len(links) > 0 {
   485  		for i := range links {
   486  			usergroupLinks = append(usergroupLinks, &pbUser.Link{
   487  				Platform: links[i].Platform,
   488  				Uri:      links[i].URI,
   489  			})
   490  		}
   491  	}
   492  
   493  	return &pbUser.UserGroupPublicResponse{
   494  		DisplayName: usergroup.DisplayName,
   495  		GroupType:   group.Name,
   496  		ShortBio:    usergroup.ShortBio,
   497  		Description: usergroup.Description,
   498  		Links:       usergroupLinks,
   499  		Avatar:      uuid.UUID.String(usergroup.Avatar),
   500  		Banner:      uuid.UUID.String(usergroup.Banner),
   501  		GroupEmail:  usergroup.GroupEmail,
   502  	}, nil
   503  }
   504  
   505  // ListUsersUserGroups lists all the User Groups owned by the supplied User Id
   506  func (s *Server) ListUsersUserGroups(ctx context.Context, user *pbUser.UserRequest) (*pbUser.UserGroupListResponse, error) {
   507  
   508  	var usergroups []model.UserGroup
   509  	var results pbUser.UserGroupListResponse
   510  
   511  	err := s.db.NewSelect().
   512  		Model(&usergroups).
   513  		Where("owner_id = ?", user.Id).
   514  		Order("created_at ASC").
   515  		Scan(ctx)
   516  
   517  	if err != nil {
   518  		return nil, err
   519  	}
   520  
   521  	for _, usergroup := range usergroups {
   522  
   523  		group := new(model.GroupType)
   524  
   525  		err = s.db.NewSelect().
   526  			Model(group).
   527  			Where("id = ?", usergroup.TypeID).
   528  			Scan(ctx)
   529  
   530  		var result pbUser.UserGroupPrivateResponse
   531  		result.Id = uuid.UUID.String(usergroup.ID)
   532  		result.DisplayName = usergroup.DisplayName
   533  		result.GroupType = group.Name
   534  		result.ShortBio = usergroup.ShortBio
   535  		result.Description = usergroup.Description
   536  		result.Avatar = uuid.UUID.String(usergroup.Avatar)
   537  		result.Banner = uuid.UUID.String(usergroup.Banner)
   538  		result.GroupEmail = usergroup.GroupEmail
   539  		result.CreatedAt = usergroup.CreatedAt.UTC().String()
   540  		result.UpdatedAt = usergroup.UpdatedAt.UTC().String()
   541  
   542  		results.Usergroup = append(results.Usergroup, &result)
   543  	}
   544  
   545  	return &results, nil
   546  }
   547  
   548  func (s *Server) checkRequiredAddUserGroupAttributes(ctx context.Context, usergroup *pbUser.UserGroupCreateRequest) error {
   549  	if usergroup.Id == "" || usergroup.Id == uuid.Nil.String() || usergroup.DisplayName == "" {
   550  		var argument string
   551  		switch {
   552  		case usergroup.Id == "":
   553  			argument = "owner_id"
   554  		case usergroup.Id == uuid.Nil.String():
   555  			argument = "owner_id"
   556  		case usergroup.DisplayName == "":
   557  			argument = "display_name"
   558  		}
   559  		return fmt.Errorf("argument %v is required", argument)
   560  	}
   561  
   562  	if usergroup.GroupEmail != "" {
   563  		re := regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
   564  		if !re.MatchString(usergroup.GroupEmail) {
   565  			return errors.New("group email address must be a valid email")
   566  		}
   567  	}
   568  
   569  	if usergroup.Links != nil {
   570  		for i := range usergroup.Links {
   571  			uri := usergroup.Links[i]
   572  
   573  			_, err := url.ParseRequestURI(uri)
   574  
   575  			if err != nil {
   576  				return fmt.Errorf("invalid url %v", uri)
   577  			}
   578  		}
   579  	}
   580  
   581  	err := s.db.NewSelect().
   582  		Model(new(model.User)).
   583  		Where("id = ?", usergroup.Id).
   584  		Scan(ctx)
   585  
   586  	if err != nil {
   587  		return errors.New("supplied owner_id does not exist")
   588  	}
   589  
   590  	err = s.db.NewSelect().
   591  		Model(new(model.GroupType)).
   592  		Where("name = ?", usergroup.GroupType).
   593  		Scan(ctx)
   594  
   595  	if err != nil {
   596  		return errors.New("supplied group type does not exist")
   597  	}
   598  
   599  	return nil
   600  }
   601  
   602  func (s *Server) getPlatform(uri string) string {
   603  	parsed, _ := urlx.Parse(uri)
   604  	hostname := parsed.Hostname()
   605  	platform := ""
   606  
   607  	switch {
   608  	case hostname == "youtube.com" || hostname == "youtu.be":
   609  		platform = "youtube"
   610  	case hostname == "facebook.com":
   611  		platform = "facebook"
   612  	case hostname == "soundcloud.com":
   613  		platform = "soundcloud"
   614  	case hostname == "twitter.com":
   615  		platform = "twitter"
   616  	case hostname == "bandcamp.com":
   617  		platform = "bandcamp"
   618  	case hostname == "vimeo.com":
   619  		platform = "vimeo"
   620  	}
   621  
   622  	return platform
   623  }