main

mattermost/focalboard

Last updated at: 29/12/2023 09:48

board.go

TLDR

The file board.go contains definitions for several types, methods, and functions related to boards in a project. It includes the definition of the Board struct, methods to interact with board properties, definitions for board patches and board members, and functions for parsing JSON data into board-related structs.

Methods

GetPropertyString

This method takes a property name as input and returns its corresponding value as a string. It returns an error if the property does not exist or is not of type string.

Patch

This method takes a BoardPatch object as input and applies the specified updates to a Board. It returns an updated version of the board.

Classes

Board

This class represents a board in the project. It contains various properties such as ID, team ID, channel ID, created by, modified by, type, minimum role, title, description, icon, and more. It also includes methods for checking the validity of the board and parsing board data from JSON.

BoardPatch

This class represents a patch for modifying boards. It contains properties such as type, minimum role, title, description, icon, show description, channel ID, updated properties, deleted properties, updated card properties, and deleted card properties. It includes a method for checking the validity of the patch and a method for applying the patch to a board.

BoardMember

This class represents the membership information of a user on a board. It includes properties such as board ID, user ID, roles, minimum role, scheme admin, scheme editor, scheme commenter, scheme viewer, and synthetic.

package model

import (
	"encoding/json"
	"io"
	"time"
)

type BoardType string
type BoardRole string
type BoardSearchField string

const (
	BoardTypeOpen    BoardType = "O"
	BoardTypePrivate BoardType = "P"
)

const (
	BoardRoleNone      BoardRole = ""
	BoardRoleViewer    BoardRole = "viewer"
	BoardRoleCommenter BoardRole = "commenter"
	BoardRoleEditor    BoardRole = "editor"
	BoardRoleAdmin     BoardRole = "admin"
)

const (
	BoardSearchFieldNone         BoardSearchField = ""
	BoardSearchFieldTitle        BoardSearchField = "title"
	BoardSearchFieldPropertyName BoardSearchField = "property_name"
)

// Board groups a set of blocks and its layout
// swagger:model
type Board struct {
	// The ID for the board
	// required: true
	ID string `json:"id"`

	// The ID of the team that the board belongs to
	// required: true
	TeamID string `json:"teamId"`

	// The ID of the channel that the board was created from
	// required: false
	ChannelID string `json:"channelId"`

	// The ID of the user that created the board
	// required: true
	CreatedBy string `json:"createdBy"`

	// The ID of the last user that updated the board
	// required: true
	ModifiedBy string `json:"modifiedBy"`

	// The type of the board
	// required: true
	Type BoardType `json:"type"`

	// The minimum role applied when somebody joins the board
	// required: true
	MinimumRole BoardRole `json:"minimumRole"`

	// The title of the board
	// required: false
	Title string `json:"title"`

	// The description of the board
	// required: false
	Description string `json:"description"`

	// The icon of the board
	// required: false
	Icon string `json:"icon"`

	// Indicates if the board shows the description on the interface
	// required: false
	ShowDescription bool `json:"showDescription"`

	// Marks the template boards
	// required: false
	IsTemplate bool `json:"isTemplate"`

	// Marks the template boards
	// required: false
	TemplateVersion int `json:"templateVersion"`

	// The properties of the board
	// required: false
	Properties map[string]interface{} `json:"properties"`

	// The properties of the board cards
	// required: false
	CardProperties []map[string]interface{} `json:"cardProperties"`

	// The creation time in miliseconds since the current epoch
	// required: true
	CreateAt int64 `json:"createAt"`

	// The last modified time in miliseconds since the current epoch
	// required: true
	UpdateAt int64 `json:"updateAt"`

	// The deleted time in miliseconds since the current epoch. Set to indicate this block is deleted
	// required: false
	DeleteAt int64 `json:"deleteAt"`
}

// GetPropertyString returns the value of the specified property as a string,
// or error if the property does not exist or is not of type string.
func (b *Board) GetPropertyString(propName string) (string, error) {
	val, ok := b.Properties[propName]
	if !ok {
		return "", NewErrNotFound(propName)
	}

	s, ok := val.(string)
	if !ok {
		return "", ErrInvalidPropertyValueType
	}
	return s, nil
}

// BoardPatch is a patch for modify boards
// swagger:model
type BoardPatch struct {
	// The type of the board
	// required: false
	Type *BoardType `json:"type"`

	// The minimum role applied when somebody joins the board
	// required: false
	MinimumRole *BoardRole `json:"minimumRole"`

	// The title of the board
	// required: false
	Title *string `json:"title"`

	// The description of the board
	// required: false
	Description *string `json:"description"`

	// The icon of the board
	// required: false
	Icon *string `json:"icon"`

	// Indicates if the board shows the description on the interface
	// required: false
	ShowDescription *bool `json:"showDescription"`

	// Indicates if the board shows the description on the interface
	// required: false
	ChannelID *string `json:"channelId"`

	// The board updated properties
	// required: false
	UpdatedProperties map[string]interface{} `json:"updatedProperties"`

	// The board removed properties
	// required: false
	DeletedProperties []string `json:"deletedProperties"`

	// The board updated card properties
	// required: false
	UpdatedCardProperties []map[string]interface{} `json:"updatedCardProperties"`

	// The board removed card properties
	// required: false
	DeletedCardProperties []string `json:"deletedCardProperties"`
}

// BoardMember stores the information of the membership of a user on a board
// swagger:model
type BoardMember struct {
	// The ID of the board
	// required: true
	BoardID string `json:"boardId"`

	// The ID of the user
	// required: true
	UserID string `json:"userId"`

	// The independent roles of the user on the board
	// required: false
	Roles string `json:"roles"`

	// Minimum role because the board configuration
	// required: false
	MinimumRole string `json:"minimumRole"`

	// Marks the user as an admin of the board
	// required: true
	SchemeAdmin bool `json:"schemeAdmin"`

	// Marks the user as an editor of the board
	// required: true
	SchemeEditor bool `json:"schemeEditor"`

	// Marks the user as an commenter of the board
	// required: true
	SchemeCommenter bool `json:"schemeCommenter"`

	// Marks the user as an viewer of the board
	// required: true
	SchemeViewer bool `json:"schemeViewer"`

	// Marks the membership as generated by an access group
	// required: true
	Synthetic bool `json:"synthetic"`
}

// BoardMetadata contains metadata for a Board
// swagger:model
type BoardMetadata struct {
	// The ID for the board
	// required: true
	BoardID string `json:"boardId"`

	// The most recent time a descendant of this board was added, modified, or deleted
	// required: true
	DescendantLastUpdateAt int64 `json:"descendantLastUpdateAt"`

	// The earliest time a descendant of this board was added, modified, or deleted
	// required: true
	DescendantFirstUpdateAt int64 `json:"descendantFirstUpdateAt"`

	// The ID of the user that created the board
	// required: true
	CreatedBy string `json:"createdBy"`

	// The ID of the user that last modified the most recently modified descendant
	// required: true
	LastModifiedBy string `json:"lastModifiedBy"`
}

func BoardFromJSON(data io.Reader) *Board {
	var board *Board
	_ = json.NewDecoder(data).Decode(&board)
	return board
}

func BoardsFromJSON(data io.Reader) []*Board {
	var boards []*Board
	_ = json.NewDecoder(data).Decode(&boards)
	return boards
}

func BoardMemberFromJSON(data io.Reader) *BoardMember {
	var boardMember *BoardMember
	_ = json.NewDecoder(data).Decode(&boardMember)
	return boardMember
}

func BoardMembersFromJSON(data io.Reader) []*BoardMember {
	var boardMembers []*BoardMember
	_ = json.NewDecoder(data).Decode(&boardMembers)
	return boardMembers
}

func BoardMetadataFromJSON(data io.Reader) *BoardMetadata {
	var boardMetadata *BoardMetadata
	_ = json.NewDecoder(data).Decode(&boardMetadata)
	return boardMetadata
}

// Patch returns an updated version of the board.
func (p *BoardPatch) Patch(board *Board) *Board {
	if p.Type != nil {
		board.Type = *p.Type
	}

	if p.Title != nil {
		board.Title = *p.Title
	}

	if p.MinimumRole != nil {
		board.MinimumRole = *p.MinimumRole
	}

	if p.Description != nil {
		board.Description = *p.Description
	}

	if p.Icon != nil {
		board.Icon = *p.Icon
	}

	if p.ShowDescription != nil {
		board.ShowDescription = *p.ShowDescription
	}

	if p.ChannelID != nil {
		board.ChannelID = *p.ChannelID
	}

	for key, property := range p.UpdatedProperties {
		board.Properties[key] = property
	}

	for _, key := range p.DeletedProperties {
		delete(board.Properties, key)
	}

	if len(p.UpdatedCardProperties) != 0 || len(p.DeletedCardProperties) != 0 {
		// first we accumulate all properties indexed by, and maintain their order
		keyOrder := []string{}
		cardPropertyMap := map[string]map[string]interface{}{}
		for _, prop := range board.CardProperties {
			id, ok := prop["id"].(string)
			if !ok {
				// bad property, skipping
				continue
			}

			cardPropertyMap[id] = prop
			keyOrder = append(keyOrder, id)
		}

		// if there are properties marked for removal, we delete them
		for _, propertyID := range p.DeletedCardProperties {
			delete(cardPropertyMap, propertyID)
		}

		// if there are properties marked for update, we replace the
		// existing ones or add them
		for _, newprop := range p.UpdatedCardProperties {
			id, ok := newprop["id"].(string)
			if !ok {
				// bad new property, skipping
				continue
			}

			_, exists := cardPropertyMap[id]
			if !exists {
				keyOrder = append(keyOrder, id)
			}
			cardPropertyMap[id] = newprop
		}

		// and finally we flatten and save the updated properties
		newCardProperties := []map[string]interface{}{}
		for _, key := range keyOrder {
			p, exists := cardPropertyMap[key]
			if exists {
				newCardProperties = append(newCardProperties, p)
			}
		}

		board.CardProperties = newCardProperties
	}

	return board
}

func IsBoardTypeValid(t BoardType) bool {
	return t == BoardTypeOpen || t == BoardTypePrivate
}

func IsBoardMinimumRoleValid(r BoardRole) bool {
	return r == BoardRoleNone || r == BoardRoleAdmin || r == BoardRoleEditor || r == BoardRoleCommenter || r == BoardRoleViewer
}

func (p *BoardPatch) IsValid() error {
	if p.Type != nil && !IsBoardTypeValid(*p.Type) {
		return InvalidBoardErr{"invalid-board-type"}
	}

	if p.MinimumRole != nil && !IsBoardMinimumRoleValid(*p.MinimumRole) {
		return InvalidBoardErr{"invalid-board-minimum-role"}
	}

	return nil
}

type InvalidBoardErr struct {
	msg string
}

func (ibe InvalidBoardErr) Error() string {
	return ibe.msg
}

func (b *Board) IsValid() error {
	if b.TeamID == "" {
		return InvalidBoardErr{"empty-team-id"}
	}

	if !IsBoardTypeValid(b.Type) {
		return InvalidBoardErr{"invalid-board-type"}
	}

	if !IsBoardMinimumRoleValid(b.MinimumRole) {
		return InvalidBoardErr{"invalid-board-minimum-role"}
	}

	return nil
}

// BoardMemberHistoryEntry stores the information of the membership of a user on a board
// swagger:model
type BoardMemberHistoryEntry struct {
	// The ID of the board
	// required: true
	BoardID string `json:"boardId"`

	// The ID of the user
	// required: true
	UserID string `json:"userId"`

	// The action that added this history entry (created or deleted)
	// required: false
	Action string `json:"action"`

	// The insertion time
	// required: true
	InsertAt time.Time `json:"insertAt"`
}

func BoardSearchFieldFromString(field string) (BoardSearchField, error) {
	switch field {
	case string(BoardSearchFieldTitle):
		return BoardSearchFieldTitle, nil
	case string(BoardSearchFieldPropertyName):
		return BoardSearchFieldPropertyName, nil
	}
	return BoardSearchFieldNone, ErrInvalidBoardSearchField
}