1
0
mirror of https://github.com/mattermost/focalboard.git synced 2025-02-10 19:41:29 +02:00

Workspace backend support

This commit is contained in:
Chen-I Lim 2021-03-26 11:01:54 -07:00
parent 65be22fa15
commit 725971784b
40 changed files with 1675 additions and 732 deletions

View File

@ -3,6 +3,7 @@ package api
import (
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"log"
@ -15,6 +16,7 @@ import (
"github.com/gorilla/mux"
"github.com/mattermost/focalboard/server/app"
"github.com/mattermost/focalboard/server/model"
"github.com/mattermost/focalboard/server/services/store"
"github.com/mattermost/focalboard/server/utils"
)
@ -23,18 +25,30 @@ const (
HEADER_REQUESTED_WITH_XML = "XMLHttpRequest"
)
const (
ERROR_NO_WORKSPACE_CODE = 1000
ERROR_NO_WORKSPACE_MESSAGE = "No workspace"
)
// ----------------------------------------------------------------------------------------------------
// REST APIs
type API struct {
appBuilder func() *app.App
singleUserToken string
type WorkspaceAuthenticator interface {
DoesUserHaveWorkspaceAccess(session *model.Session, workspaceID string) bool
}
func NewAPI(appBuilder func() *app.App, singleUserToken string) *API {
type API struct {
appBuilder func() *app.App
authService string
singleUserToken string
WorkspaceAuthenticator WorkspaceAuthenticator
}
func NewAPI(appBuilder func() *app.App, singleUserToken string, authService string) *API {
return &API{
appBuilder: appBuilder,
singleUserToken: singleUserToken,
authService: authService,
}
}
@ -46,11 +60,21 @@ func (a *API) RegisterRoutes(r *mux.Router) {
apiv1 := r.PathPrefix("/api/v1").Subrouter()
apiv1.Use(a.requireCSRFToken)
apiv1.HandleFunc("/blocks", a.sessionRequired(a.handleGetBlocks)).Methods("GET")
apiv1.HandleFunc("/blocks", a.sessionRequired(a.handlePostBlocks)).Methods("POST")
apiv1.HandleFunc("/blocks/{blockID}", a.sessionRequired(a.handleDeleteBlock)).Methods("DELETE")
apiv1.HandleFunc("/blocks/{blockID}/subtree", a.attachSession(a.handleGetSubTree, false)).Methods("GET")
apiv1.HandleFunc("/workspaces/{workspaceID}/blocks", a.sessionRequired(a.handleGetBlocks)).Methods("GET")
apiv1.HandleFunc("/workspaces/{workspaceID}/blocks", a.sessionRequired(a.handlePostBlocks)).Methods("POST")
apiv1.HandleFunc("/workspaces/{workspaceID}/blocks/{blockID}", a.sessionRequired(a.handleDeleteBlock)).Methods("DELETE")
apiv1.HandleFunc("/workspaces/{workspaceID}/blocks/{blockID}/subtree", a.attachSession(a.handleGetSubTree, false)).Methods("GET")
apiv1.HandleFunc("/workspaces/{workspaceID}/blocks/export", a.sessionRequired(a.handleExport)).Methods("GET")
apiv1.HandleFunc("/workspaces/{workspaceID}/blocks/import", a.sessionRequired(a.handleImport)).Methods("POST")
apiv1.HandleFunc("/workspaces/{workspaceID}/sharing/{rootID}", a.sessionRequired(a.handlePostSharing)).Methods("POST")
apiv1.HandleFunc("/workspaces/{workspaceID}/sharing/{rootID}", a.sessionRequired(a.handleGetSharing)).Methods("GET")
apiv1.HandleFunc("/workspaces/{workspaceID}", a.sessionRequired(a.handleGetWorkspace)).Methods("GET")
apiv1.HandleFunc("/workspaces/{workspaceID}/regenerate_signup_token", a.sessionRequired(a.handlePostWorkspaceRegenerateSignupToken)).Methods("POST")
// User APIs
apiv1.HandleFunc("/users/me", a.sessionRequired(a.handleGetMe)).Methods("GET")
apiv1.HandleFunc("/users/{userID}", a.sessionRequired(a.handleGetUser)).Methods("GET")
apiv1.HandleFunc("/users/{userID}/changepassword", a.sessionRequired(a.handleChangePassword)).Methods("POST")
@ -60,15 +84,6 @@ func (a *API) RegisterRoutes(r *mux.Router) {
apiv1.HandleFunc("/files", a.sessionRequired(a.handleUploadFile)).Methods("POST")
apiv1.HandleFunc("/blocks/export", a.sessionRequired(a.handleExport)).Methods("GET")
apiv1.HandleFunc("/blocks/import", a.sessionRequired(a.handleImport)).Methods("POST")
apiv1.HandleFunc("/sharing/{rootID}", a.sessionRequired(a.handlePostSharing)).Methods("POST")
apiv1.HandleFunc("/sharing/{rootID}", a.sessionRequired(a.handleGetSharing)).Methods("GET")
apiv1.HandleFunc("/workspace", a.sessionRequired(a.handleGetWorkspace)).Methods("GET")
apiv1.HandleFunc("/workspace/regenerate_signup_token", a.sessionRequired(a.handlePostWorkspaceRegenerateSignupToken)).Methods("POST")
// Get Files API
files := r.PathPrefix("/files").Subrouter()
@ -101,8 +116,38 @@ func (a *API) checkCSRFToken(r *http.Request) bool {
return false
}
func (a *API) getContainer(r *http.Request) (*store.Container, error) {
if a.WorkspaceAuthenticator == nil {
// Native auth: always use root workspace
container := store.Container{
WorkspaceID: "",
}
return &container, nil
}
vars := mux.Vars(r)
workspaceID := vars["workspaceID"]
if workspaceID == "" || workspaceID == "0" {
// When authenticating, a workspace is required
return nil, errors.New("No workspace specified")
}
ctx := r.Context()
session := ctx.Value("session").(*model.Session)
if !a.WorkspaceAuthenticator.DoesUserHaveWorkspaceAccess(session, workspaceID) {
return nil, errors.New("Access denied to workspace")
}
container := store.Container{
WorkspaceID: workspaceID,
}
return &container, nil
}
func (a *API) handleGetBlocks(w http.ResponseWriter, r *http.Request) {
// swagger:operation GET /api/v1/blocks getBlocks
// swagger:operation GET /api/v1/workspaces/{workspaceID}/blocks getBlocks
//
// Returns blocks
//
@ -110,6 +155,11 @@ func (a *API) handleGetBlocks(w http.ResponseWriter, r *http.Request) {
// produces:
// - application/json
// parameters:
// - name: workspaceID
// in: path
// description: Workspace ID
// required: true
// type: string
// - name: parent_id
// in: query
// description: ID of parent block, omit to specify all blocks
@ -137,8 +187,13 @@ func (a *API) handleGetBlocks(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
parentID := query.Get("parent_id")
blockType := query.Get("type")
container, err := a.getContainer(r)
if err != nil {
noContainerErrorResponse(w, err)
return
}
blocks, err := a.app().GetBlocks(parentID, blockType)
blocks, err := a.app().GetBlocks(*container, parentID, blockType)
if err != nil {
errorResponse(w, http.StatusInternalServerError, "", err)
return
@ -169,7 +224,7 @@ func stampModifiedByUser(r *http.Request, blocks []model.Block) {
}
func (a *API) handlePostBlocks(w http.ResponseWriter, r *http.Request) {
// swagger:operation POST /api/v1/blocks updateBlocks
// swagger:operation POST /api/v1/workspaces/{workspaceID}/blocks updateBlocks
//
// Insert or update blocks
//
@ -177,6 +232,11 @@ func (a *API) handlePostBlocks(w http.ResponseWriter, r *http.Request) {
// produces:
// - application/json
// parameters:
// - name: workspaceID
// in: path
// description: Workspace ID
// required: true
// type: string
// - name: Body
// in: body
// description: array of blocks to insert or update
@ -195,6 +255,12 @@ func (a *API) handlePostBlocks(w http.ResponseWriter, r *http.Request) {
// schema:
// "$ref": "#/definitions/ErrorResponse"
container, err := a.getContainer(r)
if err != nil {
noContainerErrorResponse(w, err)
return
}
requestBody, err := ioutil.ReadAll(r.Body)
if err != nil {
errorResponse(w, http.StatusInternalServerError, "", err)
@ -232,7 +298,7 @@ func (a *API) handlePostBlocks(w http.ResponseWriter, r *http.Request) {
stampModifiedByUser(r, blocks)
err = a.app().InsertBlocks(blocks)
err = a.app().InsertBlocks(*container, blocks)
if err != nil {
errorResponse(w, http.StatusInternalServerError, "", err)
return
@ -338,7 +404,7 @@ func (a *API) handleGetMe(w http.ResponseWriter, r *http.Request) {
}
func (a *API) handleDeleteBlock(w http.ResponseWriter, r *http.Request) {
// swagger:operation DELETE /api/v1/blocks/{blockID} deleteBlock
// swagger:operation DELETE /api/v1/workspaces/{workspaceID}/blocks/{blockID} deleteBlock
//
// Deletes a block
//
@ -346,6 +412,11 @@ func (a *API) handleDeleteBlock(w http.ResponseWriter, r *http.Request) {
// produces:
// - application/json
// parameters:
// - name: workspaceID
// in: path
// description: Workspace ID
// required: true
// type: string
// - name: blockID
// in: path
// description: ID of block to delete
@ -368,7 +439,13 @@ func (a *API) handleDeleteBlock(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
blockID := vars["blockID"]
err := a.app().DeleteBlock(blockID, userID)
container, err := a.getContainer(r)
if err != nil {
noContainerErrorResponse(w, err)
return
}
err = a.app().DeleteBlock(*container, blockID, userID)
if err != nil {
errorResponse(w, http.StatusInternalServerError, "", err)
@ -380,7 +457,7 @@ func (a *API) handleDeleteBlock(w http.ResponseWriter, r *http.Request) {
}
func (a *API) handleGetSubTree(w http.ResponseWriter, r *http.Request) {
// swagger:operation GET /api/v1/blocks/{blockID}/subtree getSubTree
// swagger:operation GET /api/v1/workspaces/{workspaceID}/blocks/{blockID}/subtree getSubTree
//
// Returns the blocks of a subtree
//
@ -388,6 +465,11 @@ func (a *API) handleGetSubTree(w http.ResponseWriter, r *http.Request) {
// produces:
// - application/json
// parameters:
// - name: workspaceID
// in: path
// description: Workspace ID
// required: true
// type: string
// - name: blockID
// in: path
// description: The ID of the root block of the subtree
@ -417,6 +499,12 @@ func (a *API) handleGetSubTree(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
blockID := vars["blockID"]
container, err := a.getContainer(r)
if err != nil {
noContainerErrorResponse(w, err)
return
}
// If not authenticated (no session), check that block is publicly shared
ctx := r.Context()
session, _ := ctx.Value("session").(*model.Session)
@ -430,7 +518,7 @@ func (a *API) handleGetSubTree(w http.ResponseWriter, r *http.Request) {
return
}
isValid, err := a.app().IsValidReadToken(blockID, readToken)
isValid, err := a.app().IsValidReadToken(*container, blockID, readToken)
if err != nil {
errorResponse(w, http.StatusInternalServerError, "", err)
return
@ -454,7 +542,7 @@ func (a *API) handleGetSubTree(w http.ResponseWriter, r *http.Request) {
return
}
blocks, err := a.app().GetSubTree(blockID, int(levels))
blocks, err := a.app().GetSubTree(*container, blockID, int(levels))
if err != nil {
errorResponse(w, http.StatusInternalServerError, "", err)
return
@ -471,13 +559,19 @@ func (a *API) handleGetSubTree(w http.ResponseWriter, r *http.Request) {
}
func (a *API) handleExport(w http.ResponseWriter, r *http.Request) {
// swagger:operation GET /api/v1/blocks/export exportBlocks
// swagger:operation GET /api/v1/workspaces/{workspaceID}/blocks/export exportBlocks
//
// Returns all blocks
//
// ---
// produces:
// - application/json
// parameters:
// - name: workspaceID
// in: path
// description: Workspace ID
// required: true
// type: string
// security:
// - BearerAuth: []
// responses:
@ -492,7 +586,13 @@ func (a *API) handleExport(w http.ResponseWriter, r *http.Request) {
// schema:
// "$ref": "#/definitions/ErrorResponse"
blocks, err := a.app().GetAllBlocks()
container, err := a.getContainer(r)
if err != nil {
noContainerErrorResponse(w, err)
return
}
blocks, err := a.app().GetAllBlocks(*container)
if err != nil {
errorResponse(w, http.StatusInternalServerError, "", err)
return
@ -556,7 +656,7 @@ func arrayContainsBlockWithID(array []model.Block, blockID string) bool {
}
func (a *API) handleImport(w http.ResponseWriter, r *http.Request) {
// swagger:operation POST /api/v1/blocks/import importBlocks
// swagger:operation POST /api/v1/workspaces/{workspaceID}/blocks/import importBlocks
//
// Import blocks
//
@ -564,6 +664,11 @@ func (a *API) handleImport(w http.ResponseWriter, r *http.Request) {
// produces:
// - application/json
// parameters:
// - name: workspaceID
// in: path
// description: Workspace ID
// required: true
// type: string
// - name: Body
// in: body
// description: array of blocks to import
@ -582,6 +687,12 @@ func (a *API) handleImport(w http.ResponseWriter, r *http.Request) {
// schema:
// "$ref": "#/definitions/ErrorResponse"
container, err := a.getContainer(r)
if err != nil {
noContainerErrorResponse(w, err)
return
}
requestBody, err := ioutil.ReadAll(r.Body)
if err != nil {
errorResponse(w, http.StatusInternalServerError, "", err)
@ -598,7 +709,7 @@ func (a *API) handleImport(w http.ResponseWriter, r *http.Request) {
stampModifiedByUser(r, blocks)
err = a.app().InsertBlocks(blocks)
err = a.app().InsertBlocks(*container, blocks)
if err != nil {
errorResponse(w, http.StatusInternalServerError, "", err)
return
@ -611,7 +722,7 @@ func (a *API) handleImport(w http.ResponseWriter, r *http.Request) {
// Sharing
func (a *API) handleGetSharing(w http.ResponseWriter, r *http.Request) {
// swagger:operation GET /api/v1/sharing/{rootID} getSharing
// swagger:operation GET /api/v1/workspaces/{workspaceID}/sharing/{rootID} getSharing
//
// Returns sharing information for a root block
//
@ -619,6 +730,11 @@ func (a *API) handleGetSharing(w http.ResponseWriter, r *http.Request) {
// produces:
// - application/json
// parameters:
// - name: workspaceID
// in: path
// description: Workspace ID
// required: true
// type: string
// - name: rootID
// in: path
// description: ID of the root block
@ -639,7 +755,13 @@ func (a *API) handleGetSharing(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
rootID := vars["rootID"]
sharing, err := a.app().GetSharing(rootID)
container, err := a.getContainer(r)
if err != nil {
noContainerErrorResponse(w, err)
return
}
sharing, err := a.app().GetSharing(*container, rootID)
if err != nil {
errorResponse(w, http.StatusInternalServerError, "", err)
return
@ -656,7 +778,7 @@ func (a *API) handleGetSharing(w http.ResponseWriter, r *http.Request) {
}
func (a *API) handlePostSharing(w http.ResponseWriter, r *http.Request) {
// swagger:operation POST /api/v1/sharing/{rootID} postSharing
// swagger:operation POST /api/v1/workspaces/{workspaceID}/sharing/{rootID} postSharing
//
// Sets sharing information for a root block
//
@ -664,6 +786,11 @@ func (a *API) handlePostSharing(w http.ResponseWriter, r *http.Request) {
// produces:
// - application/json
// parameters:
// - name: workspaceID
// in: path
// description: Workspace ID
// required: true
// type: string
// - name: rootID
// in: path
// description: ID of the root block
@ -685,6 +812,12 @@ func (a *API) handlePostSharing(w http.ResponseWriter, r *http.Request) {
// schema:
// "$ref": "#/definitions/ErrorResponse"
container, err := a.getContainer(r)
if err != nil {
noContainerErrorResponse(w, err)
return
}
requestBody, err := ioutil.ReadAll(r.Body)
if err != nil {
errorResponse(w, http.StatusInternalServerError, "", err)
@ -708,7 +841,7 @@ func (a *API) handlePostSharing(w http.ResponseWriter, r *http.Request) {
}
sharing.ModifiedBy = userID
err = a.app().UpsertSharing(sharing)
err = a.app().UpsertSharing(*container, sharing)
if err != nil {
errorResponse(w, http.StatusInternalServerError, "", err)
return
@ -721,13 +854,19 @@ func (a *API) handlePostSharing(w http.ResponseWriter, r *http.Request) {
// Workspace
func (a *API) handleGetWorkspace(w http.ResponseWriter, r *http.Request) {
// swagger:operation GET /api/v1/workspace getWorkspace
// swagger:operation GET /api/v1/workspaces/{workspaceID} getWorkspace
//
// Returns information of the root workspace
//
// ---
// produces:
// - application/json
// parameters:
// - name: workspaceID
// in: path
// description: Workspace ID
// required: true
// type: string
// security:
// - BearerAuth: []
// responses:
@ -740,10 +879,28 @@ func (a *API) handleGetWorkspace(w http.ResponseWriter, r *http.Request) {
// schema:
// "$ref": "#/definitions/ErrorResponse"
workspace, err := a.app().GetRootWorkspace()
if err != nil {
errorResponse(w, http.StatusInternalServerError, "", err)
return
var workspace *model.Workspace
var err error
if a.WorkspaceAuthenticator != nil {
vars := mux.Vars(r)
workspaceID := vars["workspaceID"]
ctx := r.Context()
session := ctx.Value("session").(*model.Session)
if !a.WorkspaceAuthenticator.DoesUserHaveWorkspaceAccess(session, workspaceID) {
errorResponse(w, http.StatusUnauthorized, "", nil)
return
}
workspace = &model.Workspace{
ID: workspaceID,
}
} else {
workspace, err = a.app().GetRootWorkspace()
if err != nil {
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
}
workspaceData, err := json.Marshal(workspace)
@ -756,13 +913,19 @@ func (a *API) handleGetWorkspace(w http.ResponseWriter, r *http.Request) {
}
func (a *API) handlePostWorkspaceRegenerateSignupToken(w http.ResponseWriter, r *http.Request) {
// swagger:operation POST /api/v1/workspace/regenerate_signup_token regenerateSignupToken
// swagger:operation POST /api/v1/workspaces/{workspaceID}/regenerate_signup_token regenerateSignupToken
//
// Regenerates the signup token for the root workspace
//
// ---
// produces:
// - application/json
// parameters:
// - name: workspaceID
// in: path
// description: Workspace ID
// required: true
// type: string
// security:
// - BearerAuth: []
// responses:
@ -910,7 +1073,7 @@ func jsonBytesResponse(w http.ResponseWriter, code int, json []byte) {
func errorResponse(w http.ResponseWriter, code int, message string, sourceError error) {
log.Printf("API ERROR %d, err: %v\n", code, sourceError)
w.Header().Set("Content-Type", "application/json")
data, err := json.Marshal(model.ErrorResponse{Error: message})
data, err := json.Marshal(model.ErrorResponse{Error: message, ErrorCode: code})
if err != nil {
data = []byte("{}")
}
@ -918,6 +1081,21 @@ func errorResponse(w http.ResponseWriter, code int, message string, sourceError
w.Write(data)
}
func errorResponseWithCode(w http.ResponseWriter, statusCode int, errorCode int, message string, sourceError error) {
log.Printf("API ERROR status %d, errorCode: %d, err: %v\n", statusCode, errorCode, sourceError)
w.Header().Set("Content-Type", "application/json")
data, err := json.Marshal(model.ErrorResponse{Error: message, ErrorCode: errorCode})
if err != nil {
data = []byte("{}")
}
w.WriteHeader(statusCode)
w.Write(data)
}
func noContainerErrorResponse(w http.ResponseWriter, sourceError error) {
errorResponseWithCode(w, http.StatusBadRequest, ERROR_NO_WORKSPACE_CODE, ERROR_NO_WORKSPACE_MESSAGE, sourceError)
}
func addUserID(rw http.ResponseWriter, req *http.Request, next http.Handler) {
ctx := context.WithValue(req.Context(), "userid", req.Header.Get("userid"))
req = req.WithContext(ctx)

View File

@ -358,11 +358,13 @@ func (a *API) attachSession(handler func(w http.ResponseWriter, r *http.Request)
now := time.Now().Unix()
session := &model.Session{
ID: "single-user",
Token: token,
UserID: "single-user",
CreateAt: now,
UpdateAt: now,
ID: "single-user",
Token: token,
UserID: "single-user",
AuthService: a.authService,
Props: map[string]interface{}{},
CreateAt: now,
UpdateAt: now,
}
ctx := context.WithValue(r.Context(), "session", session)
handler(w, r.WithContext(ctx))
@ -379,6 +381,14 @@ func (a *API) attachSession(handler func(w http.ResponseWriter, r *http.Request)
handler(w, r)
return
}
authService := session.AuthService
if authService != a.authService {
log.Printf(`Session '%s' authService mismatch '%s' instead of '%s'`, session.ID, authService, a.authService)
errorResponse(w, http.StatusUnauthorized, "", err)
return
}
ctx := context.WithValue(r.Context(), "session", session)
handler(w, r.WithContext(ctx))
}

View File

@ -6,6 +6,7 @@ import (
"github.com/google/uuid"
"github.com/mattermost/focalboard/server/model"
"github.com/mattermost/focalboard/server/services/auth"
"github.com/mattermost/focalboard/server/services/store"
"github.com/pkg/errors"
)
@ -16,8 +17,8 @@ func (a *App) GetSession(token string) (*model.Session, error) {
}
// IsValidReadToken validates the read token for a block
func (a *App) IsValidReadToken(blockID, readToken string) (bool, error) {
return a.auth.IsValidReadToken(blockID, readToken)
func (a *App) IsValidReadToken(c store.Container, blockID string, readToken string) (bool, error) {
return a.auth.IsValidReadToken(c, blockID, readToken)
}
// GetRegisteredUserCount returns the number of registered users
@ -83,11 +84,17 @@ func (a *App) Login(username, email, password, mfaToken string) (string, error)
return "", errors.New("invalid username or password")
}
authService := user.AuthService
if authService == "" {
authService = "native"
}
session := model.Session{
ID: uuid.New().String(),
Token: uuid.New().String(),
UserID: user.ID,
Props: map[string]interface{}{},
ID: uuid.New().String(),
Token: uuid.New().String(),
UserID: user.ID,
AuthService: authService,
Props: map[string]interface{}{},
}
err := a.store.CreateSession(&session)
if err != nil {
@ -133,7 +140,7 @@ func (a *App) RegisterUser(username, email, password string) error {
Email: email,
Password: auth.HashPassword(password),
MfaSecret: "",
AuthService: "",
AuthService: a.config.AuthMode,
AuthData: "",
Props: map[string]interface{}{},
})

View File

@ -2,33 +2,34 @@ package app
import (
"github.com/mattermost/focalboard/server/model"
"github.com/mattermost/focalboard/server/services/store"
)
func (a *App) GetBlocks(parentID, blockType string) ([]model.Block, error) {
func (a *App) GetBlocks(c store.Container, parentID string, blockType string) ([]model.Block, error) {
if len(blockType) > 0 && len(parentID) > 0 {
return a.store.GetBlocksWithParentAndType(parentID, blockType)
return a.store.GetBlocksWithParentAndType(c, parentID, blockType)
}
if len(blockType) > 0 {
return a.store.GetBlocksWithType(blockType)
return a.store.GetBlocksWithType(c, blockType)
}
return a.store.GetBlocksWithParent(parentID)
return a.store.GetBlocksWithParent(c, parentID)
}
func (a *App) GetRootID(blockID string) (string, error) {
return a.store.GetRootID(blockID)
func (a *App) GetRootID(c store.Container, blockID string) (string, error) {
return a.store.GetRootID(c, blockID)
}
func (a *App) GetParentID(blockID string) (string, error) {
return a.store.GetParentID(blockID)
func (a *App) GetParentID(c store.Container, blockID string) (string, error) {
return a.store.GetParentID(c, blockID)
}
func (a *App) InsertBlock(block model.Block) error {
return a.store.InsertBlock(block)
func (a *App) InsertBlock(c store.Container, block model.Block) error {
return a.store.InsertBlock(c, block)
}
func (a *App) InsertBlocks(blocks []model.Block) error {
func (a *App) InsertBlocks(c store.Container, blocks []model.Block) error {
blockIDsToNotify := []string{}
uniqueBlockIDs := make(map[string]bool)
@ -43,7 +44,7 @@ func (a *App) InsertBlocks(blocks []model.Block) error {
blockIDsToNotify = append(blockIDsToNotify, block.ParentID)
}
err := a.store.InsertBlock(block)
err := a.store.InsertBlock(c, block)
if err != nil {
return err
}
@ -55,21 +56,21 @@ func (a *App) InsertBlocks(blocks []model.Block) error {
return nil
}
func (a *App) GetSubTree(blockID string, levels int) ([]model.Block, error) {
func (a *App) GetSubTree(c store.Container, blockID string, levels int) ([]model.Block, error) {
// Only 2 or 3 levels are supported for now
if levels >= 3 {
return a.store.GetSubTree3(blockID)
return a.store.GetSubTree3(c, blockID)
}
return a.store.GetSubTree2(blockID)
return a.store.GetSubTree2(c, blockID)
}
func (a *App) GetAllBlocks() ([]model.Block, error) {
return a.store.GetAllBlocks()
func (a *App) GetAllBlocks(c store.Container) ([]model.Block, error) {
return a.store.GetAllBlocks(c)
}
func (a *App) DeleteBlock(blockID, modifiedBy string) error {
func (a *App) DeleteBlock(c store.Container, blockID string, modifiedBy string) error {
blockIDsToNotify := []string{blockID}
parentID, err := a.GetParentID(blockID)
parentID, err := a.GetParentID(c, blockID)
if err != nil {
return err
}
@ -78,7 +79,7 @@ func (a *App) DeleteBlock(blockID, modifiedBy string) error {
blockIDsToNotify = append(blockIDsToNotify, parentID)
}
err = a.store.DeleteBlock(blockID, modifiedBy)
err = a.store.DeleteBlock(c, blockID, modifiedBy)
if err != nil {
return err
}

View File

@ -7,6 +7,7 @@ import (
"github.com/golang/mock/gomock"
"github.com/mattermost/focalboard/server/auth"
"github.com/mattermost/focalboard/server/services/config"
st "github.com/mattermost/focalboard/server/services/store"
"github.com/mattermost/focalboard/server/services/store/mockstore"
"github.com/mattermost/focalboard/server/services/webhook"
"github.com/mattermost/focalboard/server/ws"
@ -25,16 +26,19 @@ func TestGetParentID(t *testing.T) {
webhook := webhook.NewClient(&cfg)
app := New(&cfg, store, auth, wsserver, &mocks.FileBackend{}, webhook)
container := st.Container{
WorkspaceID: "",
}
t.Run("success query", func(t *testing.T) {
store.EXPECT().GetParentID(gomock.Eq("test-id")).Return("test-parent-id", nil)
result, err := app.GetParentID("test-id")
store.EXPECT().GetParentID(gomock.Eq(container), gomock.Eq("test-id")).Return("test-parent-id", nil)
result, err := app.GetParentID(container, "test-id")
require.NoError(t, err)
require.Equal(t, "test-parent-id", result)
})
t.Run("fail query", func(t *testing.T) {
store.EXPECT().GetParentID(gomock.Eq("test-id")).Return("", errors.New("block-not-found"))
_, err := app.GetParentID("test-id")
store.EXPECT().GetParentID(gomock.Eq(container), gomock.Eq("test-id")).Return("", errors.New("block-not-found"))
_, err := app.GetParentID(container, "test-id")
require.Error(t, err)
require.Equal(t, "block-not-found", err.Error())
})

View File

@ -4,10 +4,11 @@ import (
"database/sql"
"github.com/mattermost/focalboard/server/model"
"github.com/mattermost/focalboard/server/services/store"
)
func (a *App) GetSharing(rootID string) (*model.Sharing, error) {
sharing, err := a.store.GetSharing(rootID)
func (a *App) GetSharing(c store.Container, rootID string) (*model.Sharing, error) {
sharing, err := a.store.GetSharing(c, rootID)
if err == sql.ErrNoRows {
return nil, nil
}
@ -17,6 +18,6 @@ func (a *App) GetSharing(rootID string) (*model.Sharing, error) {
return sharing, nil
}
func (a *App) UpsertSharing(sharing model.Sharing) error {
return a.store.UpsertSharing(sharing)
func (a *App) UpsertSharing(c store.Container, sharing model.Sharing) error {
return a.store.UpsertSharing(c, sharing)
}

View File

@ -38,13 +38,13 @@ func (a *Auth) GetSession(token string) (*model.Session, error) {
}
// IsValidReadToken validates the read token for a block
func (a *Auth) IsValidReadToken(blockID, readToken string) (bool, error) {
rootID, err := a.store.GetRootID(blockID)
func (a *Auth) IsValidReadToken(c store.Container, blockID string, readToken string) (bool, error) {
rootID, err := a.store.GetRootID(c, blockID)
if err != nil {
return false, err
}
sharing, err := a.store.GetSharing(rootID)
sharing, err := a.store.GetSharing(c, rootID)
if err == sql.ErrNoRows {
return false, nil
}

View File

@ -138,7 +138,7 @@ func (c *Client) doApiRequestReader(method, url string, data io.Reader, etag str
}
func (c *Client) GetBlocksRoute() string {
return "/blocks"
return "/workspaces/0/blocks"
}
func (c *Client) GetBlockRoute(id string) string {
@ -192,7 +192,7 @@ func (c *Client) GetSubtree(blockID string) ([]model.Block, *Response) {
// Sharing
func (c *Client) GetSharingRoute(rootID string) string {
return fmt.Sprintf("/sharing/%s", rootID)
return fmt.Sprintf("/workspaces/0/sharing/%s", rootID)
}
func (c *Client) GetSharing(rootID string) (*model.Sharing, *Response) {

View File

@ -17,6 +17,7 @@ cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZ
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.63.0/go.mod h1:GmezbQc7T2snqkEXWfZ0sy0VfkB/ivI2DdtJL2DEmlg=
cloud.google.com/go v0.64.0/go.mod h1:xfORb36jGvE+6EexW71nMEtL025s3x6xvuYUKM4JLv4=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
@ -1171,7 +1172,10 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr
golang.org/x/oauth2 v0.0.0-20190319182350-c85d3e98c914/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84 h1:duBc5zuJsmJXYOVVE/6PxejI+N3AaCqKjtsoLn1Je5Q=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -1364,6 +1368,7 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=

View File

@ -3,6 +3,6 @@ module github.com/mattermost/focalboard/server
go 1.15
require (
github.com/golang/mock v1.4.4
github.com/golang/mock v1.5.0
github.com/jteeuwen/go-bindata v3.0.7+incompatible // indirect
)

View File

@ -1,11 +1,24 @@
github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/jteeuwen/go-bindata v3.0.7+incompatible h1:91Uy4d9SYVr1kyTJ15wJsog+esAZZl7JmEfTkwmhJts=
github.com/jteeuwen/go-bindata v3.0.7+incompatible/go.mod h1:JVvhzYOiGBnFSYRyV00iY8q7/0PThjIYav1p9h5dmKs=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262 h1:qsl9y/CJx34tuA7QCPNp86JNJe4spst6Ff8MjvPUdPg=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e h1:aZzprAO9/8oim3qStq3wc1Xuxx4QmAGriC4VU4ojemQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -6,4 +6,8 @@ type ErrorResponse struct {
// The error message
// required: false
Error string `json:"error"`
// The error code
// required: false
ErrorCode int `json:"errorCode"`
}

View File

@ -45,10 +45,11 @@ type User struct {
}
type Session struct {
ID string `json:"id"`
Token string `json:"token"`
UserID string `json:"user_id"`
Props map[string]interface{} `json:"props"`
CreateAt int64 `json:"create_at,omitempty"`
UpdateAt int64 `json:"update_at,omitempty"`
ID string `json:"id"`
Token string `json:"token"`
UserID string `json:"user_id"`
AuthService string `json:"authService"`
Props map[string]interface{} `json:"props"`
CreateAt int64 `json:"create_at,omitempty"`
UpdateAt int64 `json:"update_at,omitempty"`
}

View File

@ -0,0 +1,5 @@
package server
func (s *Server) initHandlers() {
// Additional init handlers go here
}

View File

@ -43,6 +43,8 @@ type Server struct {
localRouter *mux.Router
localModeServer *http.Server
api *api.API
appBuilder func() *app.App
}
func New(cfg *config.Configuration, singleUserToken string) (*Server, error) {
@ -74,7 +76,7 @@ func New(cfg *config.Configuration, singleUserToken string) (*Server, error) {
webhookClient := webhook.NewClient(cfg)
appBuilder := func() *app.App { return app.New(cfg, store, auth, wsServer, filesBackend, webhookClient) }
api := api.NewAPI(appBuilder, singleUserToken)
api := api.NewAPI(appBuilder, singleUserToken, cfg.AuthMode)
// Local router for admin APIs
localRouter := mux.NewRouter()
@ -150,7 +152,7 @@ func New(cfg *config.Configuration, singleUserToken string) (*Server, error) {
}
})
return &Server{
server := Server{
config: cfg,
wsServer: wsServer,
webServer: webServer,
@ -159,7 +161,13 @@ func New(cfg *config.Configuration, singleUserToken string) (*Server, error) {
telemetry: telemetryService,
logger: logger,
localRouter: localRouter,
}, nil
api: api,
appBuilder: appBuilder,
}
server.initHandlers()
return &server, nil
}
func (s *Server) Start() error {

View File

@ -18,6 +18,7 @@ type Configuration struct {
DBType string `json:"dbtype" mapstructure:"dbtype"`
DBConfigString string `json:"dbconfig" mapstructure:"dbconfig"`
UseSSL bool `json:"useSSL" mapstructure:"useSSL"`
SecureCookie bool `json:"secureCookie" mapstructure:"secureCookie"`
WebPath string `json:"webpath" mapstructure:"webpath"`
FilesPath string `json:"filespath" mapstructure:"filespath"`
Telemetry bool `json:"telemetry" mapstructure:"telemetry"`
@ -28,6 +29,11 @@ type Configuration struct {
LocalOnly bool `json:"localonly" mapstructure:"localonly"`
EnableLocalMode bool `json:"enableLocalMode" mapstructure:"enableLocalMode"`
LocalModeSocketLocation string `json:"localModeSocketLocation" mapstructure:"localModeSocketLocation"`
AuthMode string `json:"authMode" mapstructure:"authMode"`
MattermostURL string `json:"mattermostURL" mapstructure:"mattermostURL"`
MattermostClientID string `json:"mattermostClientID" mapstructure:"mattermostClientID"`
MattermostClientSecret string `json:"mattermostClientSecret" mapstructure:"mattermostClientSecret"`
}
// ReadConfigFile read the configuration from the filesystem.
@ -39,6 +45,7 @@ func ReadConfigFile() (*Configuration, error) {
viper.SetDefault("Port", DefaultPort)
viper.SetDefault("DBType", "sqlite3")
viper.SetDefault("DBConfigString", "./octo.db")
viper.SetDefault("SecureCookie", false)
viper.SetDefault("WebPath", "./pack")
viper.SetDefault("FilesPath", "./files")
viper.SetDefault("Telemetry", true)
@ -49,6 +56,8 @@ func ReadConfigFile() (*Configuration, error) {
viper.SetDefault("EnableLocalMode", false)
viper.SetDefault("LocalModeSocketLocation", "/var/tmp/focalboard_local.socket")
viper.SetDefault("AuthMode", "native")
err := viper.ReadInConfig() // Find and read the config file
if err != nil { // Handle errors reading the config file
return nil, err
@ -62,7 +71,16 @@ func ReadConfigFile() (*Configuration, error) {
}
log.Println("readConfigFile")
log.Printf("%+v", configuration)
log.Printf("%+v", removeSecurityData(configuration))
return &configuration, nil
}
func removeSecurityData(config Configuration) Configuration {
clean := config
clean.Secret = "hidden"
clean.MattermostClientID = "hidden"
clean.MattermostClientSecret = "hidden"
return clean
}

View File

@ -5,35 +5,37 @@
package mockstore
import (
reflect "reflect"
gomock "github.com/golang/mock/gomock"
model "github.com/mattermost/focalboard/server/model"
reflect "reflect"
store "github.com/mattermost/focalboard/server/services/store"
)
// MockStore is a mock of Store interface
// MockStore is a mock of Store interface.
type MockStore struct {
ctrl *gomock.Controller
recorder *MockStoreMockRecorder
}
// MockStoreMockRecorder is the mock recorder for MockStore
// MockStoreMockRecorder is the mock recorder for MockStore.
type MockStoreMockRecorder struct {
mock *MockStore
}
// NewMockStore creates a new mock instance
// NewMockStore creates a new mock instance.
func NewMockStore(ctrl *gomock.Controller) *MockStore {
mock := &MockStore{ctrl: ctrl}
mock.recorder = &MockStoreMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockStore) EXPECT() *MockStoreMockRecorder {
return m.recorder
}
// CleanUpSessions mocks base method
// CleanUpSessions mocks base method.
func (m *MockStore) CleanUpSessions(arg0 int64) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CleanUpSessions", arg0)
@ -41,13 +43,13 @@ func (m *MockStore) CleanUpSessions(arg0 int64) error {
return ret0
}
// CleanUpSessions indicates an expected call of CleanUpSessions
// CleanUpSessions indicates an expected call of CleanUpSessions.
func (mr *MockStoreMockRecorder) CleanUpSessions(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CleanUpSessions", reflect.TypeOf((*MockStore)(nil).CleanUpSessions), arg0)
}
// CreateSession mocks base method
// CreateSession mocks base method.
func (m *MockStore) CreateSession(arg0 *model.Session) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateSession", arg0)
@ -55,13 +57,13 @@ func (m *MockStore) CreateSession(arg0 *model.Session) error {
return ret0
}
// CreateSession indicates an expected call of CreateSession
// CreateSession indicates an expected call of CreateSession.
func (mr *MockStoreMockRecorder) CreateSession(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSession", reflect.TypeOf((*MockStore)(nil).CreateSession), arg0)
}
// CreateUser mocks base method
// CreateUser mocks base method.
func (m *MockStore) CreateUser(arg0 *model.User) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateUser", arg0)
@ -69,27 +71,27 @@ func (m *MockStore) CreateUser(arg0 *model.User) error {
return ret0
}
// CreateUser indicates an expected call of CreateUser
// CreateUser indicates an expected call of CreateUser.
func (mr *MockStoreMockRecorder) CreateUser(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateUser", reflect.TypeOf((*MockStore)(nil).CreateUser), arg0)
}
// DeleteBlock mocks base method
func (m *MockStore) DeleteBlock(arg0, arg1 string) error {
// DeleteBlock mocks base method.
func (m *MockStore) DeleteBlock(arg0 store.Container, arg1, arg2 string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DeleteBlock", arg0, arg1)
ret := m.ctrl.Call(m, "DeleteBlock", arg0, arg1, arg2)
ret0, _ := ret[0].(error)
return ret0
}
// DeleteBlock indicates an expected call of DeleteBlock
func (mr *MockStoreMockRecorder) DeleteBlock(arg0, arg1 interface{}) *gomock.Call {
// DeleteBlock indicates an expected call of DeleteBlock.
func (mr *MockStoreMockRecorder) DeleteBlock(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteBlock", reflect.TypeOf((*MockStore)(nil).DeleteBlock), arg0, arg1)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteBlock", reflect.TypeOf((*MockStore)(nil).DeleteBlock), arg0, arg1, arg2)
}
// DeleteSession mocks base method
// DeleteSession mocks base method.
func (m *MockStore) DeleteSession(arg0 string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DeleteSession", arg0)
@ -97,13 +99,13 @@ func (m *MockStore) DeleteSession(arg0 string) error {
return ret0
}
// DeleteSession indicates an expected call of DeleteSession
// DeleteSession indicates an expected call of DeleteSession.
func (mr *MockStoreMockRecorder) DeleteSession(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteSession", reflect.TypeOf((*MockStore)(nil).DeleteSession), arg0)
}
// GetActiveUserCount mocks base method
// GetActiveUserCount mocks base method.
func (m *MockStore) GetActiveUserCount(arg0 int64) (int, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetActiveUserCount", arg0)
@ -112,88 +114,88 @@ func (m *MockStore) GetActiveUserCount(arg0 int64) (int, error) {
return ret0, ret1
}
// GetActiveUserCount indicates an expected call of GetActiveUserCount
// GetActiveUserCount indicates an expected call of GetActiveUserCount.
func (mr *MockStoreMockRecorder) GetActiveUserCount(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActiveUserCount", reflect.TypeOf((*MockStore)(nil).GetActiveUserCount), arg0)
}
// GetAllBlocks mocks base method
func (m *MockStore) GetAllBlocks() ([]model.Block, error) {
// GetAllBlocks mocks base method.
func (m *MockStore) GetAllBlocks(arg0 store.Container) ([]model.Block, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetAllBlocks")
ret := m.ctrl.Call(m, "GetAllBlocks", arg0)
ret0, _ := ret[0].([]model.Block)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetAllBlocks indicates an expected call of GetAllBlocks
func (mr *MockStoreMockRecorder) GetAllBlocks() *gomock.Call {
// GetAllBlocks indicates an expected call of GetAllBlocks.
func (mr *MockStoreMockRecorder) GetAllBlocks(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllBlocks", reflect.TypeOf((*MockStore)(nil).GetAllBlocks))
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllBlocks", reflect.TypeOf((*MockStore)(nil).GetAllBlocks), arg0)
}
// GetBlocksWithParent mocks base method
func (m *MockStore) GetBlocksWithParent(arg0 string) ([]model.Block, error) {
// GetBlocksWithParent mocks base method.
func (m *MockStore) GetBlocksWithParent(arg0 store.Container, arg1 string) ([]model.Block, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetBlocksWithParent", arg0)
ret := m.ctrl.Call(m, "GetBlocksWithParent", arg0, arg1)
ret0, _ := ret[0].([]model.Block)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetBlocksWithParent indicates an expected call of GetBlocksWithParent
func (mr *MockStoreMockRecorder) GetBlocksWithParent(arg0 interface{}) *gomock.Call {
// GetBlocksWithParent indicates an expected call of GetBlocksWithParent.
func (mr *MockStoreMockRecorder) GetBlocksWithParent(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlocksWithParent", reflect.TypeOf((*MockStore)(nil).GetBlocksWithParent), arg0)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlocksWithParent", reflect.TypeOf((*MockStore)(nil).GetBlocksWithParent), arg0, arg1)
}
// GetBlocksWithParentAndType mocks base method
func (m *MockStore) GetBlocksWithParentAndType(arg0, arg1 string) ([]model.Block, error) {
// GetBlocksWithParentAndType mocks base method.
func (m *MockStore) GetBlocksWithParentAndType(arg0 store.Container, arg1, arg2 string) ([]model.Block, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetBlocksWithParentAndType", arg0, arg1)
ret := m.ctrl.Call(m, "GetBlocksWithParentAndType", arg0, arg1, arg2)
ret0, _ := ret[0].([]model.Block)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetBlocksWithParentAndType indicates an expected call of GetBlocksWithParentAndType
func (mr *MockStoreMockRecorder) GetBlocksWithParentAndType(arg0, arg1 interface{}) *gomock.Call {
// GetBlocksWithParentAndType indicates an expected call of GetBlocksWithParentAndType.
func (mr *MockStoreMockRecorder) GetBlocksWithParentAndType(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlocksWithParentAndType", reflect.TypeOf((*MockStore)(nil).GetBlocksWithParentAndType), arg0, arg1)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlocksWithParentAndType", reflect.TypeOf((*MockStore)(nil).GetBlocksWithParentAndType), arg0, arg1, arg2)
}
// GetBlocksWithType mocks base method
func (m *MockStore) GetBlocksWithType(arg0 string) ([]model.Block, error) {
// GetBlocksWithType mocks base method.
func (m *MockStore) GetBlocksWithType(arg0 store.Container, arg1 string) ([]model.Block, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetBlocksWithType", arg0)
ret := m.ctrl.Call(m, "GetBlocksWithType", arg0, arg1)
ret0, _ := ret[0].([]model.Block)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetBlocksWithType indicates an expected call of GetBlocksWithType
func (mr *MockStoreMockRecorder) GetBlocksWithType(arg0 interface{}) *gomock.Call {
// GetBlocksWithType indicates an expected call of GetBlocksWithType.
func (mr *MockStoreMockRecorder) GetBlocksWithType(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlocksWithType", reflect.TypeOf((*MockStore)(nil).GetBlocksWithType), arg0)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlocksWithType", reflect.TypeOf((*MockStore)(nil).GetBlocksWithType), arg0, arg1)
}
// GetParentID mocks base method
func (m *MockStore) GetParentID(arg0 string) (string, error) {
// GetParentID mocks base method.
func (m *MockStore) GetParentID(arg0 store.Container, arg1 string) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetParentID", arg0)
ret := m.ctrl.Call(m, "GetParentID", arg0, arg1)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetParentID indicates an expected call of GetParentID
func (mr *MockStoreMockRecorder) GetParentID(arg0 interface{}) *gomock.Call {
// GetParentID indicates an expected call of GetParentID.
func (mr *MockStoreMockRecorder) GetParentID(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetParentID", reflect.TypeOf((*MockStore)(nil).GetParentID), arg0)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetParentID", reflect.TypeOf((*MockStore)(nil).GetParentID), arg0, arg1)
}
// GetRegisteredUserCount mocks base method
// GetRegisteredUserCount mocks base method.
func (m *MockStore) GetRegisteredUserCount() (int, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetRegisteredUserCount")
@ -202,28 +204,28 @@ func (m *MockStore) GetRegisteredUserCount() (int, error) {
return ret0, ret1
}
// GetRegisteredUserCount indicates an expected call of GetRegisteredUserCount
// GetRegisteredUserCount indicates an expected call of GetRegisteredUserCount.
func (mr *MockStoreMockRecorder) GetRegisteredUserCount() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRegisteredUserCount", reflect.TypeOf((*MockStore)(nil).GetRegisteredUserCount))
}
// GetRootID mocks base method
func (m *MockStore) GetRootID(arg0 string) (string, error) {
// GetRootID mocks base method.
func (m *MockStore) GetRootID(arg0 store.Container, arg1 string) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetRootID", arg0)
ret := m.ctrl.Call(m, "GetRootID", arg0, arg1)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetRootID indicates an expected call of GetRootID
func (mr *MockStoreMockRecorder) GetRootID(arg0 interface{}) *gomock.Call {
// GetRootID indicates an expected call of GetRootID.
func (mr *MockStoreMockRecorder) GetRootID(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRootID", reflect.TypeOf((*MockStore)(nil).GetRootID), arg0)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRootID", reflect.TypeOf((*MockStore)(nil).GetRootID), arg0, arg1)
}
// GetSession mocks base method
// GetSession mocks base method.
func (m *MockStore) GetSession(arg0 string, arg1 int64) (*model.Session, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetSession", arg0, arg1)
@ -232,58 +234,58 @@ func (m *MockStore) GetSession(arg0 string, arg1 int64) (*model.Session, error)
return ret0, ret1
}
// GetSession indicates an expected call of GetSession
// GetSession indicates an expected call of GetSession.
func (mr *MockStoreMockRecorder) GetSession(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSession", reflect.TypeOf((*MockStore)(nil).GetSession), arg0, arg1)
}
// GetSharing mocks base method
func (m *MockStore) GetSharing(arg0 string) (*model.Sharing, error) {
// GetSharing mocks base method.
func (m *MockStore) GetSharing(arg0 store.Container, arg1 string) (*model.Sharing, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetSharing", arg0)
ret := m.ctrl.Call(m, "GetSharing", arg0, arg1)
ret0, _ := ret[0].(*model.Sharing)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetSharing indicates an expected call of GetSharing
func (mr *MockStoreMockRecorder) GetSharing(arg0 interface{}) *gomock.Call {
// GetSharing indicates an expected call of GetSharing.
func (mr *MockStoreMockRecorder) GetSharing(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSharing", reflect.TypeOf((*MockStore)(nil).GetSharing), arg0)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSharing", reflect.TypeOf((*MockStore)(nil).GetSharing), arg0, arg1)
}
// GetSubTree2 mocks base method
func (m *MockStore) GetSubTree2(arg0 string) ([]model.Block, error) {
// GetSubTree2 mocks base method.
func (m *MockStore) GetSubTree2(arg0 store.Container, arg1 string) ([]model.Block, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetSubTree2", arg0)
ret := m.ctrl.Call(m, "GetSubTree2", arg0, arg1)
ret0, _ := ret[0].([]model.Block)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetSubTree2 indicates an expected call of GetSubTree2
func (mr *MockStoreMockRecorder) GetSubTree2(arg0 interface{}) *gomock.Call {
// GetSubTree2 indicates an expected call of GetSubTree2.
func (mr *MockStoreMockRecorder) GetSubTree2(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubTree2", reflect.TypeOf((*MockStore)(nil).GetSubTree2), arg0)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubTree2", reflect.TypeOf((*MockStore)(nil).GetSubTree2), arg0, arg1)
}
// GetSubTree3 mocks base method
func (m *MockStore) GetSubTree3(arg0 string) ([]model.Block, error) {
// GetSubTree3 mocks base method.
func (m *MockStore) GetSubTree3(arg0 store.Container, arg1 string) ([]model.Block, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetSubTree3", arg0)
ret := m.ctrl.Call(m, "GetSubTree3", arg0, arg1)
ret0, _ := ret[0].([]model.Block)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetSubTree3 indicates an expected call of GetSubTree3
func (mr *MockStoreMockRecorder) GetSubTree3(arg0 interface{}) *gomock.Call {
// GetSubTree3 indicates an expected call of GetSubTree3.
func (mr *MockStoreMockRecorder) GetSubTree3(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubTree3", reflect.TypeOf((*MockStore)(nil).GetSubTree3), arg0)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubTree3", reflect.TypeOf((*MockStore)(nil).GetSubTree3), arg0, arg1)
}
// GetSystemSettings mocks base method
// GetSystemSettings mocks base method.
func (m *MockStore) GetSystemSettings() (map[string]string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetSystemSettings")
@ -292,13 +294,13 @@ func (m *MockStore) GetSystemSettings() (map[string]string, error) {
return ret0, ret1
}
// GetSystemSettings indicates an expected call of GetSystemSettings
// GetSystemSettings indicates an expected call of GetSystemSettings.
func (mr *MockStoreMockRecorder) GetSystemSettings() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSystemSettings", reflect.TypeOf((*MockStore)(nil).GetSystemSettings))
}
// GetUserByEmail mocks base method
// GetUserByEmail mocks base method.
func (m *MockStore) GetUserByEmail(arg0 string) (*model.User, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetUserByEmail", arg0)
@ -307,13 +309,13 @@ func (m *MockStore) GetUserByEmail(arg0 string) (*model.User, error) {
return ret0, ret1
}
// GetUserByEmail indicates an expected call of GetUserByEmail
// GetUserByEmail indicates an expected call of GetUserByEmail.
func (mr *MockStoreMockRecorder) GetUserByEmail(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserByEmail", reflect.TypeOf((*MockStore)(nil).GetUserByEmail), arg0)
}
// GetUserById mocks base method
// GetUserById mocks base method.
func (m *MockStore) GetUserById(arg0 string) (*model.User, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetUserById", arg0)
@ -322,13 +324,13 @@ func (m *MockStore) GetUserById(arg0 string) (*model.User, error) {
return ret0, ret1
}
// GetUserById indicates an expected call of GetUserById
// GetUserById indicates an expected call of GetUserById.
func (mr *MockStoreMockRecorder) GetUserById(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserById", reflect.TypeOf((*MockStore)(nil).GetUserById), arg0)
}
// GetUserByUsername mocks base method
// GetUserByUsername mocks base method.
func (m *MockStore) GetUserByUsername(arg0 string) (*model.User, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetUserByUsername", arg0)
@ -337,13 +339,13 @@ func (m *MockStore) GetUserByUsername(arg0 string) (*model.User, error) {
return ret0, ret1
}
// GetUserByUsername indicates an expected call of GetUserByUsername
// GetUserByUsername indicates an expected call of GetUserByUsername.
func (mr *MockStoreMockRecorder) GetUserByUsername(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserByUsername", reflect.TypeOf((*MockStore)(nil).GetUserByUsername), arg0)
}
// GetWorkspace mocks base method
// GetWorkspace mocks base method.
func (m *MockStore) GetWorkspace(arg0 string) (*model.Workspace, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetWorkspace", arg0)
@ -352,27 +354,27 @@ func (m *MockStore) GetWorkspace(arg0 string) (*model.Workspace, error) {
return ret0, ret1
}
// GetWorkspace indicates an expected call of GetWorkspace
// GetWorkspace indicates an expected call of GetWorkspace.
func (mr *MockStoreMockRecorder) GetWorkspace(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkspace", reflect.TypeOf((*MockStore)(nil).GetWorkspace), arg0)
}
// InsertBlock mocks base method
func (m *MockStore) InsertBlock(arg0 model.Block) error {
// InsertBlock mocks base method.
func (m *MockStore) InsertBlock(arg0 store.Container, arg1 model.Block) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "InsertBlock", arg0)
ret := m.ctrl.Call(m, "InsertBlock", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// InsertBlock indicates an expected call of InsertBlock
func (mr *MockStoreMockRecorder) InsertBlock(arg0 interface{}) *gomock.Call {
// InsertBlock indicates an expected call of InsertBlock.
func (mr *MockStoreMockRecorder) InsertBlock(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertBlock", reflect.TypeOf((*MockStore)(nil).InsertBlock), arg0)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertBlock", reflect.TypeOf((*MockStore)(nil).InsertBlock), arg0, arg1)
}
// RefreshSession mocks base method
// RefreshSession mocks base method.
func (m *MockStore) RefreshSession(arg0 *model.Session) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RefreshSession", arg0)
@ -380,13 +382,13 @@ func (m *MockStore) RefreshSession(arg0 *model.Session) error {
return ret0
}
// RefreshSession indicates an expected call of RefreshSession
// RefreshSession indicates an expected call of RefreshSession.
func (mr *MockStoreMockRecorder) RefreshSession(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshSession", reflect.TypeOf((*MockStore)(nil).RefreshSession), arg0)
}
// SetSystemSetting mocks base method
// SetSystemSetting mocks base method.
func (m *MockStore) SetSystemSetting(arg0, arg1 string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SetSystemSetting", arg0, arg1)
@ -394,13 +396,13 @@ func (m *MockStore) SetSystemSetting(arg0, arg1 string) error {
return ret0
}
// SetSystemSetting indicates an expected call of SetSystemSetting
// SetSystemSetting indicates an expected call of SetSystemSetting.
func (mr *MockStoreMockRecorder) SetSystemSetting(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSystemSetting", reflect.TypeOf((*MockStore)(nil).SetSystemSetting), arg0, arg1)
}
// Shutdown mocks base method
// Shutdown mocks base method.
func (m *MockStore) Shutdown() error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Shutdown")
@ -408,13 +410,13 @@ func (m *MockStore) Shutdown() error {
return ret0
}
// Shutdown indicates an expected call of Shutdown
// Shutdown indicates an expected call of Shutdown.
func (mr *MockStoreMockRecorder) Shutdown() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Shutdown", reflect.TypeOf((*MockStore)(nil).Shutdown))
}
// UpdateSession mocks base method
// UpdateSession mocks base method.
func (m *MockStore) UpdateSession(arg0 *model.Session) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateSession", arg0)
@ -422,13 +424,13 @@ func (m *MockStore) UpdateSession(arg0 *model.Session) error {
return ret0
}
// UpdateSession indicates an expected call of UpdateSession
// UpdateSession indicates an expected call of UpdateSession.
func (mr *MockStoreMockRecorder) UpdateSession(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateSession", reflect.TypeOf((*MockStore)(nil).UpdateSession), arg0)
}
// UpdateUser mocks base method
// UpdateUser mocks base method.
func (m *MockStore) UpdateUser(arg0 *model.User) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateUser", arg0)
@ -436,13 +438,13 @@ func (m *MockStore) UpdateUser(arg0 *model.User) error {
return ret0
}
// UpdateUser indicates an expected call of UpdateUser
// UpdateUser indicates an expected call of UpdateUser.
func (mr *MockStoreMockRecorder) UpdateUser(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateUser", reflect.TypeOf((*MockStore)(nil).UpdateUser), arg0)
}
// UpdateUserPassword mocks base method
// UpdateUserPassword mocks base method.
func (m *MockStore) UpdateUserPassword(arg0, arg1 string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateUserPassword", arg0, arg1)
@ -450,13 +452,13 @@ func (m *MockStore) UpdateUserPassword(arg0, arg1 string) error {
return ret0
}
// UpdateUserPassword indicates an expected call of UpdateUserPassword
// UpdateUserPassword indicates an expected call of UpdateUserPassword.
func (mr *MockStoreMockRecorder) UpdateUserPassword(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateUserPassword", reflect.TypeOf((*MockStore)(nil).UpdateUserPassword), arg0, arg1)
}
// UpdateUserPasswordByID mocks base method
// UpdateUserPasswordByID mocks base method.
func (m *MockStore) UpdateUserPasswordByID(arg0, arg1 string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateUserPasswordByID", arg0, arg1)
@ -464,27 +466,27 @@ func (m *MockStore) UpdateUserPasswordByID(arg0, arg1 string) error {
return ret0
}
// UpdateUserPasswordByID indicates an expected call of UpdateUserPasswordByID
// UpdateUserPasswordByID indicates an expected call of UpdateUserPasswordByID.
func (mr *MockStoreMockRecorder) UpdateUserPasswordByID(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateUserPasswordByID", reflect.TypeOf((*MockStore)(nil).UpdateUserPasswordByID), arg0, arg1)
}
// UpsertSharing mocks base method
func (m *MockStore) UpsertSharing(arg0 model.Sharing) error {
// UpsertSharing mocks base method.
func (m *MockStore) UpsertSharing(arg0 store.Container, arg1 model.Sharing) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpsertSharing", arg0)
ret := m.ctrl.Call(m, "UpsertSharing", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// UpsertSharing indicates an expected call of UpsertSharing
func (mr *MockStoreMockRecorder) UpsertSharing(arg0 interface{}) *gomock.Call {
// UpsertSharing indicates an expected call of UpsertSharing.
func (mr *MockStoreMockRecorder) UpsertSharing(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertSharing", reflect.TypeOf((*MockStore)(nil).UpsertSharing), arg0)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertSharing", reflect.TypeOf((*MockStore)(nil).UpsertSharing), arg0, arg1)
}
// UpsertWorkspaceSettings mocks base method
// UpsertWorkspaceSettings mocks base method.
func (m *MockStore) UpsertWorkspaceSettings(arg0 model.Workspace) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpsertWorkspaceSettings", arg0)
@ -492,13 +494,13 @@ func (m *MockStore) UpsertWorkspaceSettings(arg0 model.Workspace) error {
return ret0
}
// UpsertWorkspaceSettings indicates an expected call of UpsertWorkspaceSettings
// UpsertWorkspaceSettings indicates an expected call of UpsertWorkspaceSettings.
func (mr *MockStoreMockRecorder) UpsertWorkspaceSettings(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertWorkspaceSettings", reflect.TypeOf((*MockStore)(nil).UpsertWorkspaceSettings), arg0)
}
// UpsertWorkspaceSignupToken mocks base method
// UpsertWorkspaceSignupToken mocks base method.
func (m *MockStore) UpsertWorkspaceSignupToken(arg0 model.Workspace) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpsertWorkspaceSignupToken", arg0)
@ -506,7 +508,7 @@ func (m *MockStore) UpsertWorkspaceSignupToken(arg0 model.Workspace) error {
return ret0
}
// UpsertWorkspaceSignupToken indicates an expected call of UpsertWorkspaceSignupToken
// UpsertWorkspaceSignupToken indicates an expected call of UpsertWorkspaceSignupToken.
func (mr *MockStoreMockRecorder) UpsertWorkspaceSignupToken(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertWorkspaceSignupToken", reflect.TypeOf((*MockStore)(nil).UpsertWorkspaceSignupToken), arg0)

View File

@ -10,19 +10,21 @@ import (
sq "github.com/Masterminds/squirrel"
_ "github.com/lib/pq"
"github.com/mattermost/focalboard/server/model"
"github.com/mattermost/focalboard/server/services/store"
_ "github.com/mattn/go-sqlite3"
)
func (s *SQLStore) latestsBlocksSubquery() sq.SelectBuilder {
func (s *SQLStore) latestsBlocksSubquery(c store.Container) sq.SelectBuilder {
internalQuery := sq.Select("*", "ROW_NUMBER() OVER (PARTITION BY id ORDER BY insert_at DESC) AS rn").From("blocks")
return sq.Select("*").
FromSelect(internalQuery, "a").
Where(sq.Eq{"rn": 1}).
Where(sq.Eq{"delete_at": 0})
Where(sq.Eq{"delete_at": 0}).
Where(sq.Eq{"coalesce(workspace_id, \"\")": c.WorkspaceID})
}
func (s *SQLStore) GetBlocksWithParentAndType(parentID, blockType string) ([]model.Block, error) {
func (s *SQLStore) GetBlocksWithParentAndType(c store.Container, parentID string, blockType string) ([]model.Block, error) {
query := s.getQueryBuilder().
Select(
"id",
@ -37,7 +39,7 @@ func (s *SQLStore) GetBlocksWithParentAndType(parentID, blockType string) ([]mod
"update_at",
"delete_at",
).
FromSelect(s.latestsBlocksSubquery(), "latest").
FromSelect(s.latestsBlocksSubquery(c), "latest").
Where(sq.Eq{"parent_id": parentID}).
Where(sq.Eq{"type": blockType})
@ -51,7 +53,7 @@ func (s *SQLStore) GetBlocksWithParentAndType(parentID, blockType string) ([]mod
return blocksFromRows(rows)
}
func (s *SQLStore) GetBlocksWithParent(parentID string) ([]model.Block, error) {
func (s *SQLStore) GetBlocksWithParent(c store.Container, parentID string) ([]model.Block, error) {
query := s.getQueryBuilder().
Select(
"id",
@ -66,7 +68,7 @@ func (s *SQLStore) GetBlocksWithParent(parentID string) ([]model.Block, error) {
"update_at",
"delete_at",
).
FromSelect(s.latestsBlocksSubquery(), "latest").
FromSelect(s.latestsBlocksSubquery(c), "latest").
Where(sq.Eq{"parent_id": parentID})
rows, err := query.Query()
@ -79,7 +81,7 @@ func (s *SQLStore) GetBlocksWithParent(parentID string) ([]model.Block, error) {
return blocksFromRows(rows)
}
func (s *SQLStore) GetBlocksWithType(blockType string) ([]model.Block, error) {
func (s *SQLStore) GetBlocksWithType(c store.Container, blockType string) ([]model.Block, error) {
query := s.getQueryBuilder().
Select(
"id",
@ -94,7 +96,7 @@ func (s *SQLStore) GetBlocksWithType(blockType string) ([]model.Block, error) {
"update_at",
"delete_at",
).
FromSelect(s.latestsBlocksSubquery(), "latest").
FromSelect(s.latestsBlocksSubquery(c), "latest").
Where(sq.Eq{"type": blockType})
rows, err := query.Query()
@ -108,7 +110,7 @@ func (s *SQLStore) GetBlocksWithType(blockType string) ([]model.Block, error) {
}
// GetSubTree2 returns blocks within 2 levels of the given blockID
func (s *SQLStore) GetSubTree2(blockID string) ([]model.Block, error) {
func (s *SQLStore) GetSubTree2(c store.Container, blockID string) ([]model.Block, error) {
query := s.getQueryBuilder().
Select(
"id",
@ -123,7 +125,7 @@ func (s *SQLStore) GetSubTree2(blockID string) ([]model.Block, error) {
"update_at",
"delete_at",
).
FromSelect(s.latestsBlocksSubquery(), "latest").
FromSelect(s.latestsBlocksSubquery(c), "latest").
Where(sq.Or{sq.Eq{"id": blockID}, sq.Eq{"parent_id": blockID}})
rows, err := query.Query()
@ -137,7 +139,7 @@ func (s *SQLStore) GetSubTree2(blockID string) ([]model.Block, error) {
}
// GetSubTree3 returns blocks within 3 levels of the given blockID
func (s *SQLStore) GetSubTree3(blockID string) ([]model.Block, error) {
func (s *SQLStore) GetSubTree3(c store.Container, blockID string) ([]model.Block, error) {
// This first subquery returns repeated blocks
subquery1 := sq.Select(
"l3.id",
@ -152,9 +154,9 @@ func (s *SQLStore) GetSubTree3(blockID string) ([]model.Block, error) {
"l3.update_at",
"l3.delete_at",
).
FromSelect(s.latestsBlocksSubquery(), "l1").
JoinClause(s.latestsBlocksSubquery().Prefix("JOIN (").Suffix(") l2 on l2.parent_id = l1.id or l2.id = l1.id")).
JoinClause(s.latestsBlocksSubquery().Prefix("JOIN (").Suffix(") l3 on l3.parent_id = l2.id or l3.id = l2.id")).
FromSelect(s.latestsBlocksSubquery(c), "l1").
JoinClause(s.latestsBlocksSubquery(c).Prefix("JOIN (").Suffix(") l2 on l2.parent_id = l1.id or l2.id = l1.id")).
JoinClause(s.latestsBlocksSubquery(c).Prefix("JOIN (").Suffix(") l3 on l3.parent_id = l2.id or l3.id = l2.id")).
Where(sq.Eq{"l1.id": blockID})
// This second subquery is used to return distinct blocks
@ -188,7 +190,7 @@ func (s *SQLStore) GetSubTree3(blockID string) ([]model.Block, error) {
return blocksFromRows(rows)
}
func (s *SQLStore) GetAllBlocks() ([]model.Block, error) {
func (s *SQLStore) GetAllBlocks(c store.Container) ([]model.Block, error) {
query := s.getQueryBuilder().
Select(
"id",
@ -203,7 +205,7 @@ func (s *SQLStore) GetAllBlocks() ([]model.Block, error) {
"update_at",
"delete_at",
).
FromSelect(s.latestsBlocksSubquery(), "latest")
FromSelect(s.latestsBlocksSubquery(c), "latest")
rows, err := query.Query()
if err != nil {
@ -262,9 +264,9 @@ func blocksFromRows(rows *sql.Rows) ([]model.Block, error) {
return results, nil
}
func (s *SQLStore) GetRootID(blockID string) (string, error) {
func (s *SQLStore) GetRootID(c store.Container, blockID string) (string, error) {
query := s.getQueryBuilder().Select("root_id").
FromSelect(s.latestsBlocksSubquery(), "latest").
FromSelect(s.latestsBlocksSubquery(c), "latest").
Where(sq.Eq{"id": blockID})
row := query.QueryRow()
@ -279,9 +281,9 @@ func (s *SQLStore) GetRootID(blockID string) (string, error) {
return rootID, nil
}
func (s *SQLStore) GetParentID(blockID string) (string, error) {
func (s *SQLStore) GetParentID(c store.Container, blockID string) (string, error) {
query := s.getQueryBuilder().Select("parent_id").
FromSelect(s.latestsBlocksSubquery(), "latest").
FromSelect(s.latestsBlocksSubquery(c), "latest").
Where(sq.Eq{"id": blockID})
row := query.QueryRow()
@ -296,7 +298,7 @@ func (s *SQLStore) GetParentID(blockID string) (string, error) {
return parentID, nil
}
func (s *SQLStore) InsertBlock(block model.Block) error {
func (s *SQLStore) InsertBlock(c store.Container, block model.Block) error {
if block.RootID == "" {
return errors.New("rootId is nil")
}
@ -308,6 +310,7 @@ func (s *SQLStore) InsertBlock(block model.Block) error {
query := s.getQueryBuilder().Insert("blocks").
Columns(
"workspace_id",
"id",
"parent_id",
"root_id",
@ -320,6 +323,7 @@ func (s *SQLStore) InsertBlock(block model.Block) error {
"update_at",
"delete_at",
).Values(
c.WorkspaceID,
block.ID,
block.ParentID,
block.RootID,
@ -341,16 +345,18 @@ func (s *SQLStore) InsertBlock(block model.Block) error {
return nil
}
func (s *SQLStore) DeleteBlock(blockID, modifiedBy string) error {
func (s *SQLStore) DeleteBlock(c store.Container, blockID string, modifiedBy string) error {
now := time.Now().Unix()
query := s.getQueryBuilder().Insert("blocks").
Columns(
"workspace_id",
"id",
"modified_by",
"update_at",
"delete_at",
).
Values(
c.WorkspaceID,
blockID,
modifiedBy,
now,

View File

@ -1,13 +1,11 @@
// Code generated by go-bindata. DO NOT EDIT.
// sources:
// templates/templates.json (22.926kB)
// templates/templates.json
package initializations
import (
"bytes"
"compress/gzip"
"crypto/sha256"
"fmt"
"io"
"io/ioutil"
@ -20,7 +18,7 @@ import (
func bindataRead(data []byte, name string) ([]byte, error) {
gz, err := gzip.NewReader(bytes.NewBuffer(data))
if err != nil {
return nil, fmt.Errorf("read %q: %w", name, err)
return nil, fmt.Errorf("Read %q: %v", name, err)
}
var buf bytes.Buffer
@ -28,7 +26,7 @@ func bindataRead(data []byte, name string) ([]byte, error) {
clErr := gz.Close()
if err != nil {
return nil, fmt.Errorf("read %q: %w", name, err)
return nil, fmt.Errorf("Read %q: %v", name, err)
}
if clErr != nil {
return nil, err
@ -38,9 +36,8 @@ func bindataRead(data []byte, name string) ([]byte, error) {
}
type asset struct {
bytes []byte
info os.FileInfo
digest [sha256.Size]byte
bytes []byte
info os.FileInfo
}
type bindataFileInfo struct {
@ -84,8 +81,8 @@ func templatesJson() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "templates.json", size: 22926, mode: os.FileMode(0644), modTime: time.Unix(1610369786, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x3, 0x15, 0x49, 0xe, 0xb2, 0xe7, 0x7, 0xd0, 0x6e, 0x35, 0x6f, 0xd0, 0x76, 0xe7, 0x1d, 0x9d, 0xc7, 0xa0, 0x55, 0x1, 0x25, 0x51, 0x9e, 0xd5, 0xf0, 0x81, 0x4c, 0x91, 0xd7, 0x33, 0x37, 0x3b}}
info := bindataFileInfo{name: "templates.json", size: 22926, mode: os.FileMode(420), modTime: time.Unix(1611791102, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -93,8 +90,8 @@ func templatesJson() (*asset, error) {
// It returns an error if the asset could not be found or
// could not be loaded.
func Asset(name string) ([]byte, error) {
canonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[canonicalName]; ok {
cannonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok {
a, err := f()
if err != nil {
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
@ -104,12 +101,6 @@ func Asset(name string) ([]byte, error) {
return nil, fmt.Errorf("Asset %s not found", name)
}
// AssetString returns the asset contents as a string (instead of a []byte).
func AssetString(name string) (string, error) {
data, err := Asset(name)
return string(data), err
}
// MustAsset is like Asset but panics when Asset would return an error.
// It simplifies safe initialization of global variables.
func MustAsset(name string) []byte {
@ -121,18 +112,12 @@ func MustAsset(name string) []byte {
return a
}
// MustAssetString is like AssetString but panics when Asset would return an
// error. It simplifies safe initialization of global variables.
func MustAssetString(name string) string {
return string(MustAsset(name))
}
// AssetInfo loads and returns the asset info for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
func AssetInfo(name string) (os.FileInfo, error) {
canonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[canonicalName]; ok {
cannonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok {
a, err := f()
if err != nil {
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
@ -142,33 +127,6 @@ func AssetInfo(name string) (os.FileInfo, error) {
return nil, fmt.Errorf("AssetInfo %s not found", name)
}
// AssetDigest returns the digest of the file with the given name. It returns an
// error if the asset could not be found or the digest could not be loaded.
func AssetDigest(name string) ([sha256.Size]byte, error) {
canonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[canonicalName]; ok {
a, err := f()
if err != nil {
return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s can't read by error: %v", name, err)
}
return a.digest, nil
}
return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s not found", name)
}
// Digests returns a map of all known files and their checksums.
func Digests() (map[string][sha256.Size]byte, error) {
mp := make(map[string][sha256.Size]byte, len(_bindata))
for name := range _bindata {
a, err := _bindata[name]()
if err != nil {
return nil, err
}
mp[name] = a.digest
}
return mp, nil
}
// AssetNames returns the names of the assets.
func AssetNames() []string {
names := make([]string, 0, len(_bindata))
@ -183,9 +141,6 @@ var _bindata = map[string]func() (*asset, error){
"templates.json": templatesJson,
}
// AssetDebug is true if the assets were built with the debug flag enabled.
const AssetDebug = false
// AssetDir returns the file names below a certain
// directory embedded in the file by go-bindata.
// For example if you run go-bindata on data/... and data contains the
@ -195,15 +150,15 @@ const AssetDebug = false
// img/
// a.png
// b.png
// then AssetDir("data") would return []string{"foo.txt", "img"},
// AssetDir("data/img") would return []string{"a.png", "b.png"},
// AssetDir("foo.txt") and AssetDir("notexist") would return an error, and
// then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
// AssetDir("") will return []string{"data"}.
func AssetDir(name string) ([]string, error) {
node := _bintree
if len(name) != 0 {
canonicalName := strings.Replace(name, "\\", "/", -1)
pathList := strings.Split(canonicalName, "/")
cannonicalName := strings.Replace(name, "\\", "/", -1)
pathList := strings.Split(cannonicalName, "/")
for _, p := range pathList {
node = node.Children[p]
if node == nil {
@ -225,12 +180,11 @@ type bintree struct {
Func func() (*asset, error)
Children map[string]*bintree
}
var _bintree = &bintree{nil, map[string]*bintree{
"templates.json": {templatesJson, map[string]*bintree{}},
"templates.json": &bintree{templatesJson, map[string]*bintree{}},
}}
// RestoreAsset restores an asset under the given directory.
// RestoreAsset restores an asset under the given directory
func RestoreAsset(dir, name string) error {
data, err := Asset(name)
if err != nil {
@ -248,10 +202,14 @@ func RestoreAsset(dir, name string) error {
if err != nil {
return err
}
return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
if err != nil {
return err
}
return nil
}
// RestoreAssets restores an asset under the given directory recursively.
// RestoreAssets restores an asset under the given directory recursively
func RestoreAssets(dir, name string) error {
children, err := AssetDir(name)
// File
@ -269,6 +227,7 @@ func RestoreAssets(dir, name string) error {
}
func _filePath(dir, name string) string {
canonicalName := strings.Replace(name, "\\", "/", -1)
return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...)
cannonicalName := strings.Replace(name, "\\", "/", -1)
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
}

View File

@ -5,6 +5,7 @@ import (
"log"
"github.com/mattermost/focalboard/server/model"
"github.com/mattermost/focalboard/server/services/store"
"github.com/mattermost/focalboard/server/services/store/sqlstore/initializations"
)
@ -32,10 +33,14 @@ func (s *SQLStore) importInitialTemplates() error {
return err
}
globalContainer := store.Container{
WorkspaceID: "",
}
log.Printf("Inserting %d blocks", len(archive.Blocks))
for _, block := range archive.Blocks {
// log.Printf("\t%v %v %v", block.ID, block.Type, block.Title)
err := s.InsertBlock(block)
err := s.InsertBlock(globalContainer, block)
if err != nil {
return err
}

View File

@ -14,6 +14,8 @@
// postgres_files/000006_sharing_table.up.sql
// postgres_files/000007_workspaces_table.down.sql
// postgres_files/000007_workspaces_table.up.sql
// postgres_files/000008_teams.down.sql
// postgres_files/000008_teams.up.sql
package postgres
import (
@ -214,7 +216,7 @@ func _000004_auth_tableDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "000004_auth_table.down.sql", size: 39, mode: os.FileMode(420), modTime: time.Unix(1610481092, 0)}
info := bindataFileInfo{name: "000004_auth_table.down.sql", size: 39, mode: os.FileMode(420), modTime: time.Unix(1611791102, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -234,7 +236,7 @@ func _000004_auth_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "000004_auth_table.up.sql", size: 491, mode: os.FileMode(420), modTime: time.Unix(1610481092, 0)}
info := bindataFileInfo{name: "000004_auth_table.up.sql", size: 491, mode: os.FileMode(420), modTime: time.Unix(1611791102, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -254,7 +256,7 @@ func _000005_blocks_modifiedbyDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "000005_blocks_modifiedby.down.sql", size: 44, mode: os.FileMode(420), modTime: time.Unix(1610481092, 0)}
info := bindataFileInfo{name: "000005_blocks_modifiedby.down.sql", size: 44, mode: os.FileMode(420), modTime: time.Unix(1611791102, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -274,7 +276,7 @@ func _000005_blocks_modifiedbyUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "000005_blocks_modifiedby.up.sql", size: 55, mode: os.FileMode(420), modTime: time.Unix(1610481092, 0)}
info := bindataFileInfo{name: "000005_blocks_modifiedby.up.sql", size: 55, mode: os.FileMode(420), modTime: time.Unix(1611791102, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -294,7 +296,7 @@ func _000006_sharing_tableDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "000006_sharing_table.down.sql", size: 20, mode: os.FileMode(420), modTime: time.Unix(1610576067, 0)}
info := bindataFileInfo{name: "000006_sharing_table.down.sql", size: 20, mode: os.FileMode(420), modTime: time.Unix(1611791102, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -314,7 +316,7 @@ func _000006_sharing_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "000006_sharing_table.up.sql", size: 159, mode: os.FileMode(420), modTime: time.Unix(1610576067, 0)}
info := bindataFileInfo{name: "000006_sharing_table.up.sql", size: 159, mode: os.FileMode(420), modTime: time.Unix(1611791102, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -334,7 +336,7 @@ func _000007_workspaces_tableDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "000007_workspaces_table.down.sql", size: 23, mode: os.FileMode(420), modTime: time.Unix(1610576169, 0)}
info := bindataFileInfo{name: "000007_workspaces_table.down.sql", size: 23, mode: os.FileMode(420), modTime: time.Unix(1611791102, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -354,7 +356,47 @@ func _000007_workspaces_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "000007_workspaces_table.up.sql", size: 179, mode: os.FileMode(420), modTime: time.Unix(1610577228, 0)}
info := bindataFileInfo{name: "000007_workspaces_table.up.sql", size: 179, mode: os.FileMode(420), modTime: time.Unix(1611791102, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var __000008_teamsDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\xf4\x09\x71\x0d\x52\x08\x71\x74\xf2\x71\x55\x48\xca\xc9\x4f\xce\x2e\xe6\x72\x09\xf2\x0f\x50\x70\xf6\xf7\x09\xf5\xf5\x53\x28\xcf\x2f\xca\x2e\x2e\x48\x4c\x4e\x8d\xcf\x4c\xb1\xe6\xe2\x42\x56\x5d\x9c\x91\x58\x94\x99\x97\x4e\xb4\xf2\xd4\xe2\xe2\xcc\xfc\x3c\x54\xe3\x13\x4b\x4b\x32\xe2\x8b\x53\x8b\xca\x32\x93\x53\xad\xb9\x00\x01\x00\x00\xff\xff\xdd\xff\x41\x9f\x8c\x00\x00\x00")
func _000008_teamsDownSqlBytes() ([]byte, error) {
return bindataRead(
__000008_teamsDownSql,
"000008_teams.down.sql",
)
}
func _000008_teamsDownSql() (*asset, error) {
bytes, err := _000008_teamsDownSqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "000008_teams.down.sql", size: 140, mode: os.FileMode(420), modTime: time.Unix(1616709037, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var __000008_teamsUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\xf4\x09\x71\x0d\x52\x08\x71\x74\xf2\x71\x55\x48\xca\xc9\x4f\xce\x2e\xe6\x72\x74\x71\x51\x70\xf6\xf7\x09\xf5\xf5\x53\x28\xcf\x2f\xca\x2e\x2e\x48\x4c\x4e\x8d\xcf\x4c\x51\x08\x73\x0c\x72\xf6\x70\x0c\xd2\x30\x36\xd3\xb4\xe6\xe2\x42\xd6\x58\x9c\x91\x58\x94\x99\x97\x4e\x8e\xce\xd4\xe2\xe2\xcc\xfc\x3c\x14\x4b\x13\x4b\x4b\x32\xe2\x8b\x53\x8b\xca\x32\x93\x53\xe1\x5a\x8d\x0c\x34\xad\xb9\x00\x01\x00\x00\xff\xff\xba\x55\x30\xd8\xad\x00\x00\x00")
func _000008_teamsUpSqlBytes() ([]byte, error) {
return bindataRead(
__000008_teamsUpSql,
"000008_teams.up.sql",
)
}
func _000008_teamsUpSql() (*asset, error) {
bytes, err := _000008_teamsUpSqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "000008_teams.up.sql", size: 173, mode: os.FileMode(420), modTime: time.Unix(1616709035, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -425,6 +467,8 @@ var _bindata = map[string]func() (*asset, error){
"000006_sharing_table.up.sql": _000006_sharing_tableUpSql,
"000007_workspaces_table.down.sql": _000007_workspaces_tableDownSql,
"000007_workspaces_table.up.sql": _000007_workspaces_tableUpSql,
"000008_teams.down.sql": _000008_teamsDownSql,
"000008_teams.up.sql": _000008_teamsUpSql,
}
// AssetDir returns the file names below a certain
@ -481,6 +525,8 @@ var _bintree = &bintree{nil, map[string]*bintree{
"000006_sharing_table.up.sql": &bintree{_000006_sharing_tableUpSql, map[string]*bintree{}},
"000007_workspaces_table.down.sql": &bintree{_000007_workspaces_tableDownSql, map[string]*bintree{}},
"000007_workspaces_table.up.sql": &bintree{_000007_workspaces_tableUpSql, map[string]*bintree{}},
"000008_teams.down.sql": &bintree{_000008_teamsDownSql, map[string]*bintree{}},
"000008_teams.up.sql": &bintree{_000008_teamsUpSql, map[string]*bintree{}},
}}
// RestoreAsset restores an asset under the given directory

View File

@ -0,0 +1,8 @@
ALTER TABLE blocks
DROP COLUMN workspace_id;
ALTER TABLE sharing
DROP COLUMN workspace_id;
ALTER TABLE sessions
DROP COLUMN auth_service;

View File

@ -0,0 +1,8 @@
ALTER TABLE blocks
ADD COLUMN workspace_id VARCHAR(36);
ALTER TABLE sharing
ADD COLUMN workspace_id VARCHAR(36);
ALTER TABLE sessions
ADD COLUMN auth_service VARCHAR(20);

View File

@ -14,6 +14,8 @@
// sqlite_files/000006_sharing_table.up.sql
// sqlite_files/000007_workspaces_table.down.sql
// sqlite_files/000007_workspaces_table.up.sql
// sqlite_files/000008_teams.down.sql
// sqlite_files/000008_teams.up.sql
package sqlite
import (
@ -214,7 +216,7 @@ func _000004_auth_tableDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "000004_auth_table.down.sql", size: 39, mode: os.FileMode(420), modTime: time.Unix(1610481092, 0)}
info := bindataFileInfo{name: "000004_auth_table.down.sql", size: 39, mode: os.FileMode(420), modTime: time.Unix(1611791102, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -234,7 +236,7 @@ func _000004_auth_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "000004_auth_table.up.sql", size: 491, mode: os.FileMode(420), modTime: time.Unix(1610481092, 0)}
info := bindataFileInfo{name: "000004_auth_table.up.sql", size: 491, mode: os.FileMode(420), modTime: time.Unix(1611791102, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -254,7 +256,7 @@ func _000005_blocks_modifiedbyDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "000005_blocks_modifiedby.down.sql", size: 44, mode: os.FileMode(420), modTime: time.Unix(1610481092, 0)}
info := bindataFileInfo{name: "000005_blocks_modifiedby.down.sql", size: 44, mode: os.FileMode(420), modTime: time.Unix(1611791102, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -274,7 +276,7 @@ func _000005_blocks_modifiedbyUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "000005_blocks_modifiedby.up.sql", size: 55, mode: os.FileMode(420), modTime: time.Unix(1610481092, 0)}
info := bindataFileInfo{name: "000005_blocks_modifiedby.up.sql", size: 55, mode: os.FileMode(420), modTime: time.Unix(1611791102, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -294,7 +296,7 @@ func _000006_sharing_tableDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "000006_sharing_table.down.sql", size: 20, mode: os.FileMode(420), modTime: time.Unix(1610576067, 0)}
info := bindataFileInfo{name: "000006_sharing_table.down.sql", size: 20, mode: os.FileMode(420), modTime: time.Unix(1611791102, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -314,7 +316,7 @@ func _000006_sharing_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "000006_sharing_table.up.sql", size: 159, mode: os.FileMode(420), modTime: time.Unix(1610576067, 0)}
info := bindataFileInfo{name: "000006_sharing_table.up.sql", size: 159, mode: os.FileMode(420), modTime: time.Unix(1611791102, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -334,7 +336,7 @@ func _000007_workspaces_tableDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "000007_workspaces_table.down.sql", size: 23, mode: os.FileMode(420), modTime: time.Unix(1610576588, 0)}
info := bindataFileInfo{name: "000007_workspaces_table.down.sql", size: 23, mode: os.FileMode(420), modTime: time.Unix(1611791102, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -354,7 +356,47 @@ func _000007_workspaces_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "000007_workspaces_table.up.sql", size: 179, mode: os.FileMode(420), modTime: time.Unix(1610577231, 0)}
info := bindataFileInfo{name: "000007_workspaces_table.up.sql", size: 179, mode: os.FileMode(420), modTime: time.Unix(1611791102, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var __000008_teamsDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\xf4\x09\x71\x0d\x52\x08\x71\x74\xf2\x71\x55\x48\xca\xc9\x4f\xce\x2e\xe6\x72\x09\xf2\x0f\x50\x70\xf6\xf7\x09\xf5\xf5\x53\x28\xcf\x2f\xca\x2e\x2e\x48\x4c\x4e\x8d\xcf\x4c\xb1\xe6\xe2\x42\x56\x5d\x9c\x91\x58\x94\x99\x97\x4e\xb4\xf2\xd4\xe2\xe2\xcc\xfc\x3c\x54\xe3\x13\x4b\x4b\x32\xe2\x8b\x53\x8b\xca\x32\x93\x53\xad\xb9\x00\x01\x00\x00\xff\xff\xdd\xff\x41\x9f\x8c\x00\x00\x00")
func _000008_teamsDownSqlBytes() ([]byte, error) {
return bindataRead(
__000008_teamsDownSql,
"000008_teams.down.sql",
)
}
func _000008_teamsDownSql() (*asset, error) {
bytes, err := _000008_teamsDownSqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "000008_teams.down.sql", size: 140, mode: os.FileMode(420), modTime: time.Unix(1616709044, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var __000008_teamsUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\xf4\x09\x71\x0d\x52\x08\x71\x74\xf2\x71\x55\x48\xca\xc9\x4f\xce\x2e\xe6\x72\x74\x71\x51\x70\xf6\xf7\x09\xf5\xf5\x53\x28\xcf\x2f\xca\x2e\x2e\x48\x4c\x4e\x8d\xcf\x4c\x51\x08\x73\x0c\x72\xf6\x70\x0c\xd2\x30\x36\xd3\xb4\xe6\xe2\x42\xd6\x58\x9c\x91\x58\x94\x99\x97\x4e\x8e\xce\xd4\xe2\xe2\xcc\xfc\x3c\x14\x4b\x13\x4b\x4b\x32\xe2\x8b\x53\x8b\xca\x32\x93\x53\xe1\x5a\x8d\x0c\x34\xad\xb9\x00\x01\x00\x00\xff\xff\xba\x55\x30\xd8\xad\x00\x00\x00")
func _000008_teamsUpSqlBytes() ([]byte, error) {
return bindataRead(
__000008_teamsUpSql,
"000008_teams.up.sql",
)
}
func _000008_teamsUpSql() (*asset, error) {
bytes, err := _000008_teamsUpSqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "000008_teams.up.sql", size: 173, mode: os.FileMode(420), modTime: time.Unix(1616709052, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -425,6 +467,8 @@ var _bindata = map[string]func() (*asset, error){
"000006_sharing_table.up.sql": _000006_sharing_tableUpSql,
"000007_workspaces_table.down.sql": _000007_workspaces_tableDownSql,
"000007_workspaces_table.up.sql": _000007_workspaces_tableUpSql,
"000008_teams.down.sql": _000008_teamsDownSql,
"000008_teams.up.sql": _000008_teamsUpSql,
}
// AssetDir returns the file names below a certain
@ -481,6 +525,8 @@ var _bintree = &bintree{nil, map[string]*bintree{
"000006_sharing_table.up.sql": &bintree{_000006_sharing_tableUpSql, map[string]*bintree{}},
"000007_workspaces_table.down.sql": &bintree{_000007_workspaces_tableDownSql, map[string]*bintree{}},
"000007_workspaces_table.up.sql": &bintree{_000007_workspaces_tableUpSql, map[string]*bintree{}},
"000008_teams.down.sql": &bintree{_000008_teamsDownSql, map[string]*bintree{}},
"000008_teams.up.sql": &bintree{_000008_teamsUpSql, map[string]*bintree{}},
}}
// RestoreAsset restores an asset under the given directory

View File

@ -0,0 +1,8 @@
ALTER TABLE blocks
DROP COLUMN workspace_id;
ALTER TABLE sharing
DROP COLUMN workspace_id;
ALTER TABLE sessions
DROP COLUMN auth_service;

View File

@ -0,0 +1,8 @@
ALTER TABLE blocks
ADD COLUMN workspace_id VARCHAR(36);
ALTER TABLE sharing
ADD COLUMN workspace_id VARCHAR(36);
ALTER TABLE sessions
ADD COLUMN auth_service VARCHAR(20);

View File

@ -28,7 +28,7 @@ func (s *SQLStore) GetActiveUserCount(updatedSecondsAgo int64) (int, error) {
func (s *SQLStore) GetSession(token string, expireTime int64) (*model.Session, error) {
query := s.getQueryBuilder().
Select("id", "token", "user_id", "props").
Select("id", "token", "user_id", "auth_service", "props").
From("sessions").
Where(sq.Eq{"token": token}).
Where(sq.Gt{"update_at": time.Now().Unix() - expireTime})
@ -37,7 +37,7 @@ func (s *SQLStore) GetSession(token string, expireTime int64) (*model.Session, e
session := model.Session{}
var propsBytes []byte
err := row.Scan(&session.ID, &session.Token, &session.UserID, &propsBytes)
err := row.Scan(&session.ID, &session.Token, &session.UserID, &session.AuthService, &propsBytes)
if err != nil {
return nil, err
}
@ -59,8 +59,8 @@ func (s *SQLStore) CreateSession(session *model.Session) error {
}
query := s.getQueryBuilder().Insert("sessions").
Columns("id", "token", "user_id", "props", "create_at", "update_at").
Values(session.ID, session.Token, session.UserID, propsBytes, now, now)
Columns("id", "token", "user_id", "auth_service", "props", "create_at", "update_at").
Values(session.ID, session.Token, session.UserID, session.AuthService, propsBytes, now, now)
_, err = query.Exec()
return err

View File

@ -4,11 +4,12 @@ import (
"time"
"github.com/mattermost/focalboard/server/model"
"github.com/mattermost/focalboard/server/services/store"
sq "github.com/Masterminds/squirrel"
)
func (s *SQLStore) UpsertSharing(sharing model.Sharing) error {
func (s *SQLStore) UpsertSharing(c store.Container, sharing model.Sharing) error {
now := time.Now().Unix()
query := s.getQueryBuilder().
@ -33,7 +34,7 @@ func (s *SQLStore) UpsertSharing(sharing model.Sharing) error {
return err
}
func (s *SQLStore) GetSharing(rootID string) (*model.Sharing, error) {
func (s *SQLStore) GetSharing(c store.Container, rootID string) (*model.Sharing, error) {
query := s.getQueryBuilder().
Select(
"id",

View File

@ -3,18 +3,24 @@ package store
import "github.com/mattermost/focalboard/server/model"
// Conainer represents a container in a store
// Using a struct to make extending this easier in the future
type Container struct {
WorkspaceID string
}
// Store represents the abstraction of the data storage.
type Store interface {
GetBlocksWithParentAndType(parentID, blockType string) ([]model.Block, error)
GetBlocksWithParent(parentID string) ([]model.Block, error)
GetBlocksWithType(blockType string) ([]model.Block, error)
GetSubTree2(blockID string) ([]model.Block, error)
GetSubTree3(blockID string) ([]model.Block, error)
GetAllBlocks() ([]model.Block, error)
GetRootID(blockID string) (string, error)
GetParentID(blockID string) (string, error)
InsertBlock(block model.Block) error
DeleteBlock(blockID, modifiedBy string) error
GetBlocksWithParentAndType(c Container, parentID string, blockType string) ([]model.Block, error)
GetBlocksWithParent(c Container, parentID string) ([]model.Block, error)
GetBlocksWithType(c Container, blockType string) ([]model.Block, error)
GetSubTree2(c Container, blockID string) ([]model.Block, error)
GetSubTree3(c Container, blockID string) ([]model.Block, error)
GetAllBlocks(c Container) ([]model.Block, error)
GetRootID(c Container, blockID string) (string, error)
GetParentID(c Container, blockID string) (string, error)
InsertBlock(c Container, block model.Block) error
DeleteBlock(c Container, blockID string, modifiedBy string) error
Shutdown() error
@ -38,8 +44,8 @@ type Store interface {
DeleteSession(sessionId string) error
CleanUpSessions(expireTime int64) error
UpsertSharing(sharing model.Sharing) error
GetSharing(rootID string) (*model.Sharing, error)
UpsertSharing(c Container, sharing model.Sharing) error
GetSharing(c Container, rootID string) (*model.Sharing, error)
UpsertWorkspaceSignupToken(workspace model.Workspace) error
UpsertWorkspaceSettings(workspace model.Workspace) error

View File

@ -10,42 +10,46 @@ import (
)
func StoreTestBlocksStore(t *testing.T, setup func(t *testing.T) (store.Store, func())) {
container := store.Container{
WorkspaceID: "",
}
t.Run("InsertBlock", func(t *testing.T) {
store, tearDown := setup(t)
defer tearDown()
testInsertBlock(t, store)
testInsertBlock(t, store, container)
})
t.Run("DeleteBlock", func(t *testing.T) {
store, tearDown := setup(t)
defer tearDown()
testDeleteBlock(t, store)
testDeleteBlock(t, store, container)
})
t.Run("GetSubTree2", func(t *testing.T) {
store, tearDown := setup(t)
defer tearDown()
testGetSubTree2(t, store)
testGetSubTree2(t, store, container)
})
t.Run("GetSubTree3", func(t *testing.T) {
store, tearDown := setup(t)
defer tearDown()
testGetSubTree3(t, store)
testGetSubTree3(t, store, container)
})
t.Run("GetParentID", func(t *testing.T) {
store, tearDown := setup(t)
defer tearDown()
testGetParentID(t, store)
testGetParentID(t, store, container)
})
t.Run("GetRootID", func(t *testing.T) {
store, tearDown := setup(t)
defer tearDown()
testGetRootID(t, store)
testGetRootID(t, store, container)
})
}
func testInsertBlock(t *testing.T, store store.Store) {
func testInsertBlock(t *testing.T, store store.Store, container store.Container) {
userID := "user-id"
blocks, err := store.GetAllBlocks()
blocks, err := store.GetAllBlocks(container)
require.NoError(t, err)
initialCount := len(blocks)
@ -56,10 +60,10 @@ func testInsertBlock(t *testing.T, store store.Store) {
ModifiedBy: userID,
}
err := store.InsertBlock(block)
err := store.InsertBlock(container, block)
require.NoError(t, err)
blocks, err := store.GetAllBlocks()
blocks, err := store.GetAllBlocks(container)
require.NoError(t, err)
require.Len(t, blocks, initialCount+1)
})
@ -71,10 +75,10 @@ func testInsertBlock(t *testing.T, store store.Store) {
ModifiedBy: userID,
}
err := store.InsertBlock(block)
err := store.InsertBlock(container, block)
require.Error(t, err)
blocks, err := store.GetAllBlocks()
blocks, err := store.GetAllBlocks(container)
require.NoError(t, err)
require.Len(t, blocks, initialCount+1)
})
@ -87,19 +91,19 @@ func testInsertBlock(t *testing.T, store store.Store) {
Fields: map[string]interface{}{"no-serialiable-value": t.Run},
}
err := store.InsertBlock(block)
err := store.InsertBlock(container, block)
require.Error(t, err)
blocks, err := store.GetAllBlocks()
blocks, err := store.GetAllBlocks(container)
require.NoError(t, err)
require.Len(t, blocks, initialCount+1)
})
}
func testGetSubTree2(t *testing.T, store store.Store) {
func testGetSubTree2(t *testing.T, store store.Store, container store.Container) {
userID := "user-id"
blocks, err := store.GetAllBlocks()
blocks, err := store.GetAllBlocks(container)
require.NoError(t, err)
initialCount := len(blocks)
@ -141,14 +145,14 @@ func testGetSubTree2(t *testing.T, store store.Store) {
},
}
InsertBlocks(t, store, blocksToInsert)
InsertBlocks(t, store, container, blocksToInsert)
blocks, err = store.GetAllBlocks()
blocks, err = store.GetAllBlocks(container)
require.NoError(t, err)
require.Len(t, blocks, initialCount+6)
t.Run("from root id", func(t *testing.T) {
blocks, err = store.GetSubTree2("parent")
blocks, err = store.GetSubTree2(container, "parent")
require.NoError(t, err)
require.Len(t, blocks, 3)
require.True(t, ContainsBlockWithID(blocks, "parent"))
@ -157,7 +161,7 @@ func testGetSubTree2(t *testing.T, store store.Store) {
})
t.Run("from child id", func(t *testing.T) {
blocks, err = store.GetSubTree2("child1")
blocks, err = store.GetSubTree2(container, "child1")
require.NoError(t, err)
require.Len(t, blocks, 2)
require.True(t, ContainsBlockWithID(blocks, "child1"))
@ -165,16 +169,16 @@ func testGetSubTree2(t *testing.T, store store.Store) {
})
t.Run("from not existing id", func(t *testing.T) {
blocks, err = store.GetSubTree2("not-exists")
blocks, err = store.GetSubTree2(container, "not-exists")
require.NoError(t, err)
require.Len(t, blocks, 0)
})
}
func testGetSubTree3(t *testing.T, store store.Store) {
func testGetSubTree3(t *testing.T, store store.Store, container store.Container) {
userID := "user-id"
blocks, err := store.GetAllBlocks()
blocks, err := store.GetAllBlocks(container)
require.NoError(t, err)
initialCount := len(blocks)
@ -216,14 +220,14 @@ func testGetSubTree3(t *testing.T, store store.Store) {
},
}
InsertBlocks(t, store, blocksToInsert)
InsertBlocks(t, store, container, blocksToInsert)
blocks, err = store.GetAllBlocks()
blocks, err = store.GetAllBlocks(container)
require.NoError(t, err)
require.Len(t, blocks, initialCount+6)
t.Run("from root id", func(t *testing.T) {
blocks, err = store.GetSubTree3("parent")
blocks, err = store.GetSubTree3(container, "parent")
require.NoError(t, err)
require.Len(t, blocks, 5)
require.True(t, ContainsBlockWithID(blocks, "parent"))
@ -234,7 +238,7 @@ func testGetSubTree3(t *testing.T, store store.Store) {
})
t.Run("from child id", func(t *testing.T) {
blocks, err = store.GetSubTree3("child1")
blocks, err = store.GetSubTree3(container, "child1")
require.NoError(t, err)
require.Len(t, blocks, 3)
require.True(t, ContainsBlockWithID(blocks, "child1"))
@ -243,16 +247,16 @@ func testGetSubTree3(t *testing.T, store store.Store) {
})
t.Run("from not existing id", func(t *testing.T) {
blocks, err = store.GetSubTree3("not-exists")
blocks, err = store.GetSubTree3(container, "not-exists")
require.NoError(t, err)
require.Len(t, blocks, 0)
})
}
func testGetRootID(t *testing.T, store store.Store) {
func testGetRootID(t *testing.T, store store.Store, container store.Container) {
userID := "user-id"
blocks, err := store.GetAllBlocks()
blocks, err := store.GetAllBlocks(container)
require.NoError(t, err)
initialCount := len(blocks)
@ -294,34 +298,34 @@ func testGetRootID(t *testing.T, store store.Store) {
},
}
InsertBlocks(t, store, blocksToInsert)
InsertBlocks(t, store, container, blocksToInsert)
blocks, err = store.GetAllBlocks()
blocks, err = store.GetAllBlocks(container)
require.NoError(t, err)
require.Len(t, blocks, initialCount+6)
t.Run("from root id", func(t *testing.T) {
rootID, err := store.GetRootID("parent")
rootID, err := store.GetRootID(container, "parent")
require.NoError(t, err)
require.Equal(t, "parent", rootID)
})
t.Run("from child id", func(t *testing.T) {
rootID, err := store.GetRootID("child1")
rootID, err := store.GetRootID(container, "child1")
require.NoError(t, err)
require.Equal(t, "parent", rootID)
})
t.Run("from not existing id", func(t *testing.T) {
_, err := store.GetRootID("not-exists")
_, err := store.GetRootID(container, "not-exists")
require.Error(t, err)
})
}
func testGetParentID(t *testing.T, store store.Store) {
func testGetParentID(t *testing.T, store store.Store, container store.Container) {
userID := "user-id"
blocks, err := store.GetAllBlocks()
blocks, err := store.GetAllBlocks(container)
require.NoError(t, err)
initialCount := len(blocks)
@ -363,34 +367,34 @@ func testGetParentID(t *testing.T, store store.Store) {
},
}
InsertBlocks(t, store, blocksToInsert)
InsertBlocks(t, store, container, blocksToInsert)
blocks, err = store.GetAllBlocks()
blocks, err = store.GetAllBlocks(container)
require.NoError(t, err)
require.Len(t, blocks, initialCount+6)
t.Run("from root id", func(t *testing.T) {
parentID, err := store.GetParentID("parent")
parentID, err := store.GetParentID(container, "parent")
require.NoError(t, err)
require.Equal(t, "", parentID)
})
t.Run("from child id", func(t *testing.T) {
parentID, err := store.GetParentID("grandchild1")
parentID, err := store.GetParentID(container, "grandchild1")
require.NoError(t, err)
require.Equal(t, "child1", parentID)
})
t.Run("from not existing id", func(t *testing.T) {
_, err := store.GetParentID("not-exists")
_, err := store.GetParentID(container, "not-exists")
require.Error(t, err)
})
}
func testDeleteBlock(t *testing.T, store store.Store) {
func testDeleteBlock(t *testing.T, store store.Store, container store.Container) {
userID := "user-id"
blocks, err := store.GetAllBlocks()
blocks, err := store.GetAllBlocks(container)
require.NoError(t, err)
initialCount := len(blocks)
@ -411,34 +415,34 @@ func testDeleteBlock(t *testing.T, store store.Store) {
ModifiedBy: userID,
},
}
InsertBlocks(t, store, blocksToInsert)
InsertBlocks(t, store, container, blocksToInsert)
blocks, err = store.GetAllBlocks()
blocks, err = store.GetAllBlocks(container)
require.NoError(t, err)
require.Len(t, blocks, initialCount+3)
t.Run("exiting id", func(t *testing.T) {
// Wait for not colliding the ID+insert_at key
time.Sleep(1 * time.Millisecond)
err := store.DeleteBlock("block1", userID)
err := store.DeleteBlock(container, "block1", userID)
require.NoError(t, err)
})
t.Run("exiting id multiple times", func(t *testing.T) {
// Wait for not colliding the ID+insert_at key
time.Sleep(1 * time.Millisecond)
err := store.DeleteBlock("block1", userID)
err := store.DeleteBlock(container, "block1", userID)
require.NoError(t, err)
// Wait for not colliding the ID+insert_at key
time.Sleep(1 * time.Millisecond)
err = store.DeleteBlock("block1", userID)
err = store.DeleteBlock(container, "block1", userID)
require.NoError(t, err)
})
t.Run("from not existing id", func(t *testing.T) {
// Wait for not colliding the ID+insert_at key
time.Sleep(1 * time.Millisecond)
err := store.DeleteBlock("not-exists", userID)
err := store.DeleteBlock(container, "not-exists", userID)
require.NoError(t, err)
})
}

View File

@ -8,16 +8,16 @@ import (
"github.com/stretchr/testify/require"
)
func InsertBlocks(t *testing.T, s store.Store, blocks []model.Block) {
func InsertBlocks(t *testing.T, s store.Store, container store.Container, blocks []model.Block) {
for _, block := range blocks {
err := s.InsertBlock(block)
err := s.InsertBlock(container, block)
require.NoError(t, err)
}
}
func DeleteBlocks(t *testing.T, s store.Store, blocks []model.Block, modifiedBy string) {
func DeleteBlocks(t *testing.T, s store.Store, container store.Container, blocks []model.Block, modifiedBy string) {
for _, block := range blocks {
err := s.DeleteBlock(block.ID, modifiedBy)
err := s.DeleteBlock(container, block.ID, modifiedBy)
require.NoError(t, err)
}
}

View File

@ -9,14 +9,18 @@ import (
)
func StoreTestSharingStore(t *testing.T, setup func(t *testing.T) (store.Store, func())) {
container := store.Container{
WorkspaceID: "",
}
t.Run("UpsertSharingAndGetSharing", func(t *testing.T) {
store, tearDown := setup(t)
defer tearDown()
testUpsertSharingAndGetSharing(t, store)
testUpsertSharingAndGetSharing(t, store, container)
})
}
func testUpsertSharingAndGetSharing(t *testing.T, store store.Store) {
func testUpsertSharingAndGetSharing(t *testing.T, store store.Store, container store.Container) {
t.Run("Insert first sharing and get it", func(t *testing.T) {
sharing := model.Sharing{
ID: "sharing-id",
@ -25,9 +29,9 @@ func testUpsertSharingAndGetSharing(t *testing.T, store store.Store) {
ModifiedBy: "user-id",
}
err := store.UpsertSharing(sharing)
err := store.UpsertSharing(container, sharing)
require.NoError(t, err)
newSharing, err := store.GetSharing("sharing-id")
newSharing, err := store.GetSharing(container, "sharing-id")
require.NoError(t, err)
newSharing.UpdateAt = 0
require.Equal(t, sharing, *newSharing)
@ -40,20 +44,20 @@ func testUpsertSharingAndGetSharing(t *testing.T, store store.Store) {
ModifiedBy: "user-id2",
}
newSharing, err := store.GetSharing("sharing-id")
newSharing, err := store.GetSharing(container, "sharing-id")
require.NoError(t, err)
newSharing.UpdateAt = 0
require.NotEqual(t, sharing, *newSharing)
err = store.UpsertSharing(sharing)
err = store.UpsertSharing(container, sharing)
require.NoError(t, err)
newSharing, err = store.GetSharing("sharing-id")
newSharing, err = store.GetSharing(container, "sharing-id")
require.NoError(t, err)
newSharing.UpdateAt = 0
require.Equal(t, sharing, *newSharing)
})
t.Run("Get not existing sharing", func(t *testing.T) {
_, err := store.GetSharing("not-existing")
_, err := store.GetSharing(container, "not-existing")
require.Error(t, err)
})
}

File diff suppressed because it is too large Load Diff

View File

@ -88,6 +88,11 @@ definitions:
description: The error message
type: string
x-go-name: Error
errorCode:
description: The error code
format: int64
type: integer
x-go-name: ErrorCode
type: object
x-go-package: github.com/mattermost/focalboard/server/model
FileUploadResponse:
@ -286,152 +291,6 @@ info:
title: Focalboard Server
version: 1.0.0
paths:
/api/v1/blocks:
get:
description: Returns blocks
operationId: getBlocks
parameters:
- description: ID of parent block, omit to specify all blocks
in: query
name: parent_id
type: string
- description: Type of blocks to return, omit to specify all types
in: query
name: type
type: string
produces:
- application/json
responses:
"200":
description: success
schema:
items:
$ref: '#/definitions/Block'
type: array
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
post:
description: Insert or update blocks
operationId: updateBlocks
parameters:
- description: array of blocks to insert or update
in: body
name: Body
required: true
schema:
items:
$ref: '#/definitions/Block'
type: array
produces:
- application/json
responses:
"200":
description: success
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
/api/v1/blocks/{blockID}:
delete:
description: Deletes a block
operationId: deleteBlock
parameters:
- description: ID of block to delete
in: path
name: blockID
required: true
type: string
produces:
- application/json
responses:
"200":
description: success
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
/api/v1/blocks/{blockID}/subtree:
get:
description: Returns the blocks of a subtree
operationId: getSubTree
parameters:
- description: The ID of the root block of the subtree
in: path
name: blockID
required: true
type: string
- description: The number of levels to return. 2 or 3. Defaults to 2.
in: query
maximum: 3
minimum: 2
name: l
type: integer
produces:
- application/json
responses:
"200":
description: success
schema:
items:
$ref: '#/definitions/Block'
type: array
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
/api/v1/blocks/export:
get:
description: Returns all blocks
operationId: exportBlocks
produces:
- application/json
responses:
"200":
description: success
schema:
items:
$ref: '#/definitions/Block'
type: array
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
/api/v1/blocks/import:
post:
description: Import blocks
operationId: importBlocks
parameters:
- description: array of blocks to import
in: body
name: Body
required: true
schema:
items:
$ref: '#/definitions/Block'
type: array
produces:
- application/json
responses:
"200":
description: success
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
/api/v1/files:
post:
consumes:
@ -504,55 +363,6 @@ paths:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
/api/v1/sharing/{rootID}:
get:
description: Returns sharing information for a root block
operationId: getSharing
parameters:
- description: ID of the root block
in: path
name: rootID
required: true
type: string
produces:
- application/json
responses:
"200":
description: success
schema:
$ref: '#/definitions/Sharing'
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
post:
description: Sets sharing information for a root block
operationId: postSharing
parameters:
- description: ID of the root block
in: path
name: rootID
required: true
type: string
- description: sharing information for a root block
in: body
name: Body
required: true
schema:
$ref: '#/definitions/Sharing'
produces:
- application/json
responses:
"200":
description: success
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
/api/v1/users/{userID}:
get:
description: Returns a user
@ -624,10 +434,16 @@ paths:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
/api/v1/workspace:
/api/v1/workspaces/{workspaceID}:
get:
description: Returns information of the root workspace
operationId: getWorkspace
parameters:
- description: Workspace ID
in: path
name: workspaceID
required: true
type: string
produces:
- application/json
responses:
@ -641,10 +457,252 @@ paths:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
/api/v1/workspace/regenerate_signup_token:
/api/v1/workspaces/{workspaceID}/blocks:
get:
description: Returns blocks
operationId: getBlocks
parameters:
- description: Workspace ID
in: path
name: workspaceID
required: true
type: string
- description: ID of parent block, omit to specify all blocks
in: query
name: parent_id
type: string
- description: Type of blocks to return, omit to specify all types
in: query
name: type
type: string
produces:
- application/json
responses:
"200":
description: success
schema:
items:
$ref: '#/definitions/Block'
type: array
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
post:
description: Insert or update blocks
operationId: updateBlocks
parameters:
- description: Workspace ID
in: path
name: workspaceID
required: true
type: string
- description: array of blocks to insert or update
in: body
name: Body
required: true
schema:
items:
$ref: '#/definitions/Block'
type: array
produces:
- application/json
responses:
"200":
description: success
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
/api/v1/workspaces/{workspaceID}/blocks/{blockID}:
delete:
description: Deletes a block
operationId: deleteBlock
parameters:
- description: Workspace ID
in: path
name: workspaceID
required: true
type: string
- description: ID of block to delete
in: path
name: blockID
required: true
type: string
produces:
- application/json
responses:
"200":
description: success
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
/api/v1/workspaces/{workspaceID}/blocks/{blockID}/subtree:
get:
description: Returns the blocks of a subtree
operationId: getSubTree
parameters:
- description: Workspace ID
in: path
name: workspaceID
required: true
type: string
- description: The ID of the root block of the subtree
in: path
name: blockID
required: true
type: string
- description: The number of levels to return. 2 or 3. Defaults to 2.
in: query
maximum: 3
minimum: 2
name: l
type: integer
produces:
- application/json
responses:
"200":
description: success
schema:
items:
$ref: '#/definitions/Block'
type: array
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
/api/v1/workspaces/{workspaceID}/blocks/export:
get:
description: Returns all blocks
operationId: exportBlocks
parameters:
- description: Workspace ID
in: path
name: workspaceID
required: true
type: string
produces:
- application/json
responses:
"200":
description: success
schema:
items:
$ref: '#/definitions/Block'
type: array
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
/api/v1/workspaces/{workspaceID}/blocks/import:
post:
description: Import blocks
operationId: importBlocks
parameters:
- description: Workspace ID
in: path
name: workspaceID
required: true
type: string
- description: array of blocks to import
in: body
name: Body
required: true
schema:
items:
$ref: '#/definitions/Block'
type: array
produces:
- application/json
responses:
"200":
description: success
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
/api/v1/workspaces/{workspaceID}/regenerate_signup_token:
post:
description: Regenerates the signup token for the root workspace
operationId: regenerateSignupToken
parameters:
- description: Workspace ID
in: path
name: workspaceID
required: true
type: string
produces:
- application/json
responses:
"200":
description: success
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
/api/v1/workspaces/{workspaceID}/sharing/{rootID}:
get:
description: Returns sharing information for a root block
operationId: getSharing
parameters:
- description: Workspace ID
in: path
name: workspaceID
required: true
type: string
- description: ID of the root block
in: path
name: rootID
required: true
type: string
produces:
- application/json
responses:
"200":
description: success
schema:
$ref: '#/definitions/Sharing'
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
post:
description: Sets sharing information for a root block
operationId: postSharing
parameters:
- description: Workspace ID
in: path
name: workspaceID
required: true
type: string
- description: ID of the root block
in: path
name: rootID
required: true
type: string
- description: sharing information for a root block
in: body
name: Body
required: true
schema:
$ref: '#/definitions/Sharing'
produces:
- application/json
responses:

View File

@ -11,6 +11,7 @@ import (
"github.com/gorilla/websocket"
"github.com/mattermost/focalboard/server/auth"
"github.com/mattermost/focalboard/server/model"
"github.com/mattermost/focalboard/server/services/store"
)
// IsValidSessionToken authenticates session tokens
@ -159,15 +160,31 @@ func (ws *Server) authenticateListener(wsSession *websocketSession, token, readT
log.Printf("authenticateListener: Authenticated")
}
func (ws *Server) getContainer(wsSession *websocketSession) (store.Container, error) {
// TODO
container := store.Container{
WorkspaceID: "",
}
return container, nil
}
func (ws *Server) checkAuthentication(wsSession *websocketSession, command *WebsocketCommand) bool {
if wsSession.isAuthenticated {
return true
}
container, err := ws.getContainer(wsSession)
if err != nil {
log.Printf("checkAuthentication: No container")
sendError(wsSession.client, "No container")
return false
}
if len(command.ReadToken) > 0 {
// Read token must be valid for all block IDs
for _, blockID := range command.BlockIDs {
isValid, _ := ws.auth.IsValidReadToken(blockID, command.ReadToken)
isValid, _ := ws.auth.IsValidReadToken(container, blockID, command.ReadToken)
if !isValid {
return false
}

View File

@ -11,9 +11,10 @@ import {
import {FlashMessages} from './components/flashMessages'
import {getCurrentLanguage, getMessages, storeLanguage} from './i18n'
import client from './octoClient'
import {default as client} from './octoClient'
import BoardPage from './pages/boardPage'
import ChangePasswordPage from './pages/changePasswordPage'
import ErrorPage from './pages/errorPage'
import LoginPage from './pages/loginPage'
import RegisterPage from './pages/registerPage'
import {IUser, UserContext} from './user'
@ -56,6 +57,9 @@ export default class App extends React.PureComponent<unknown, State> {
<div id='frame'>
<div id='main'>
<Switch>
<Route path='/error'>
<ErrorPage/>
</Route>
<Route path='/login'>
<LoginPage/>
</Route>
@ -67,17 +71,52 @@ export default class App extends React.PureComponent<unknown, State> {
</Route>
<Route path='/shared'>
<BoardPage
workspaceId='0'
readonly={true}
setLanguage={this.setAndStoreLanguage}
/>
</Route>
<Route path='/board'>
{this.state.initialLoad && !this.state.user && <Redirect to='login'/>}
<BoardPage setLanguage={this.setAndStoreLanguage}/>
{this.state.initialLoad && !this.state.user && <Redirect to='/login'/>}
<BoardPage
workspaceId='0'
setLanguage={this.setAndStoreLanguage}
/>
</Route>
<Route
path='/workspace/:workspaceId/shared'
render={({match}) => {
return (
<BoardPage
workspaceId={match.params.workspaceId}
readonly={true}
setLanguage={this.setAndStoreLanguage}
/>
)
}}
/>
<Route
path='/workspace/:workspaceId/'
render={({match}) => {
if (this.state.initialLoad && !this.state.user) {
const redirectUrl = `/workspace/${match.params.workspaceId}/`
const loginUrl = `/login?r=${encodeURIComponent(redirectUrl)}`
return <Redirect to={loginUrl}/>
}
return (
<BoardPage
workspaceId={match.params.workspaceId}
setLanguage={this.setAndStoreLanguage}
/>
)
}}
/>
<Route path='/'>
{this.state.initialLoad && !this.state.user && <Redirect to='login'/>}
<BoardPage setLanguage={this.setAndStoreLanguage}/>
{this.state.initialLoad && !this.state.user && <Redirect to='/login'/>}
<BoardPage
workspaceId='0'
setLanguage={this.setAndStoreLanguage}
/>
</Route>
</Switch>
</div>

View File

@ -14,12 +14,17 @@ class OctoClient {
get token(): string {
return localStorage.getItem('sessionId') || ''
}
set token(value: string) {
localStorage.setItem('sessionId', value)
}
get readToken(): string {
const queryString = new URLSearchParams(window.location.search)
const readToken = queryString.get('r') || ''
return readToken
}
workspaceId = '0'
constructor(serverUrl?: string) {
this.serverUrl = serverUrl || window.location.origin
Utils.log(`OctoClient serverUrl: ${this.serverUrl}`)
@ -92,6 +97,10 @@ class OctoClient {
}
}
private workspacePath() {
return `/api/v1/workspaces/${this.workspaceId}`
}
async getMe(): Promise<IUser | undefined> {
const path = '/api/v1/users/me'
const response = await fetch(this.serverUrl + path, {headers: this.headers()})
@ -113,7 +122,7 @@ class OctoClient {
}
async getSubtree(rootId?: string, levels = 2): Promise<IBlock[]> {
let path = `/api/v1/blocks/${encodeURIComponent(rootId || '')}/subtree?l=${levels}`
let path = this.workspacePath() + `/blocks/${encodeURIComponent(rootId || '')}/subtree?l=${levels}`
if (this.readToken) {
path += `&read_token=${this.readToken}`
}
@ -127,7 +136,7 @@ class OctoClient {
}
async exportFullArchive(): Promise<IBlock[]> {
const path = '/api/v1/blocks/export'
const path = this.workspacePath() + '/blocks/export'
const response = await fetch(this.serverUrl + path, {headers: this.headers()})
if (response.status !== 200) {
return []
@ -144,7 +153,7 @@ class OctoClient {
// Utils.log(`\t ${block.type}, ${block.id}`)
// })
const body = JSON.stringify(blocks)
return fetch(this.serverUrl + '/api/v1/blocks/import', {
return fetch(this.serverUrl + this.workspacePath() + '/blocks/import', {
method: 'POST',
headers: this.headers(),
body,
@ -154,15 +163,15 @@ class OctoClient {
async getBlocksWithParent(parentId: string, type?: string): Promise<IBlock[]> {
let path: string
if (type) {
path = `/api/v1/blocks?parent_id=${encodeURIComponent(parentId)}&type=${encodeURIComponent(type)}`
path = this.workspacePath() + `/blocks?parent_id=${encodeURIComponent(parentId)}&type=${encodeURIComponent(type)}`
} else {
path = `/api/v1/blocks?parent_id=${encodeURIComponent(parentId)}`
path = this.workspacePath() + `/blocks?parent_id=${encodeURIComponent(parentId)}`
}
return this.getBlocksWithPath(path)
}
async getBlocksWithType(type: string): Promise<IBlock[]> {
const path = `/api/v1/blocks?type=${encodeURIComponent(type)}`
const path = this.workspacePath() + `/blocks?type=${encodeURIComponent(type)}`
return this.getBlocksWithPath(path)
}
@ -218,7 +227,7 @@ class OctoClient {
async deleteBlock(blockId: string): Promise<Response> {
Utils.log(`deleteBlock: ${blockId}`)
return fetch(this.serverUrl + `/api/v1/blocks/${encodeURIComponent(blockId)}`, {
return fetch(this.serverUrl + this.workspacePath() + `/blocks/${encodeURIComponent(blockId)}`, {
method: 'DELETE',
headers: this.headers(),
})
@ -234,7 +243,7 @@ class OctoClient {
Utils.log(`\t ${block.type}, ${block.id}, ${block.title?.substr(0, 50) || ''}`)
})
const body = JSON.stringify(blocks)
return fetch(this.serverUrl + '/api/v1/blocks', {
return fetch(this.serverUrl + this.workspacePath() + '/blocks', {
method: 'POST',
headers: this.headers(),
body,
@ -244,7 +253,7 @@ class OctoClient {
// Sharing
async getSharing(rootId: string): Promise<ISharing | undefined> {
const path = `/api/v1/sharing/${rootId}`
const path = this.workspacePath() + `/sharing/${rootId}`
const response = await fetch(this.serverUrl + path, {headers: this.headers()})
if (response.status !== 200) {
return undefined
@ -254,7 +263,7 @@ class OctoClient {
}
async setSharing(sharing: ISharing): Promise<boolean> {
const path = `/api/v1/sharing/${sharing.id}`
const path = this.workspacePath() + `/sharing/${sharing.id}`
const body = JSON.stringify(sharing)
const response = await fetch(
this.serverUrl + path,
@ -274,7 +283,7 @@ class OctoClient {
// Workspace
async getWorkspace(): Promise<IWorkspace | undefined> {
const path = '/api/v1/workspace'
const path = this.workspacePath()
const response = await fetch(this.serverUrl + path, {headers: this.headers()})
if (response.status !== 200) {
return undefined
@ -284,7 +293,7 @@ class OctoClient {
}
async regenerateWorkspaceSignupToken(): Promise<boolean> {
const path = '/api/v1/workspace/regenerate_signup_token'
const path = this.workspacePath() + '/regenerate_signup_token'
const response = await fetch(this.serverUrl + path, {
method: 'POST',
headers: this.headers(),

View File

@ -7,6 +7,7 @@ import {IBlock} from '../blocks/block'
import {sendFlashMessage} from '../components/flashMessages'
import {WorkspaceComponent} from '../components/workspaceComponent'
import mutator from '../mutator'
import octoClient from '../octoClient'
import {OctoListener} from '../octoListener'
import {Utils} from '../utils'
import {BoardTree, MutableBoardTree} from '../viewModel/boardTree'
@ -15,6 +16,7 @@ import './boardPage.scss'
type Props = {
readonly?: boolean
workspaceId: string
setLanguage: (lang: string) => void
intl: IntlShape
}
@ -32,6 +34,7 @@ class BoardPage extends React.Component<Props, State> {
constructor(props: Props) {
super(props)
const queryString = new URLSearchParams(window.location.search)
let boardId = queryString.get('id') || ''
let viewId = queryString.get('v') || ''
@ -142,7 +145,10 @@ class BoardPage extends React.Component<Props, State> {
const {intl} = this.props
const {workspaceTree} = this.state
Utils.log(`BoardPage.render ${this.state.boardTree?.board?.title}`)
Utils.log(`BoardPage.render (workspace ${this.props.workspaceId}) ${this.state.boardTree?.board?.title}`)
// TODO: Make this less brittle. This only works because this is the root render function
octoClient.workspaceId = this.props.workspaceId
if (this.props.readonly && this.state.syncFailed) {
Utils.log('BoardPage.render: sync failed')
@ -196,6 +202,11 @@ class BoardPage extends React.Component<Props, State> {
private async sync(boardId: string = this.state.boardId, viewId: string | undefined = this.state.viewId) {
Utils.log(`sync start: ${boardId}`)
const workspace = await octoClient.getWorkspace()
if (!workspace) {
location.href = '/error?id=no_workspace'
}
const workspaceTree = await MutableWorkspaceTree.sync()
const boardIds = [...workspaceTree.boards.map((o) => o.id), ...workspaceTree.boardTemplates.map((o) => o.id)]
this.setState({workspaceTree})

View File

@ -0,0 +1,41 @@
.ErrorPage {
border: 1px solid #cccccc;
border-radius: 15px;
width: 450px;
height: 400px;
margin: 150px auto;
padding: 40px;
display: flex;
align-items: center;
justify-content: flex-start;
flex-direction: column;
box-shadow: rgba(var(--body-color), 0.1) 0px 0px 0px 1px, rgba(var(--body-color), 0.3) 0px 4px 8px;
@media screen and (max-width: 430px) {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
margin: auto;
padding-top: 10px;
}
.title {
font-size: 16px;
font-weight: 500;
}
> .Button {
margin-top: 10px;
margin-bottom: 20px;
min-height: 38px;
min-width: 250px;
}
.error {
color: #900000;
}
}

View File

@ -0,0 +1,24 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react'
import {
withRouter,
RouteComponentProps,
} from 'react-router-dom'
import './errorPage.scss'
type Props = RouteComponentProps
class ErrorPage extends React.PureComponent<Props> {
render(): React.ReactNode {
return (
<div className='ErrorPage'>
<div className='title'>{'Error'}</div>
</div>
)
}
}
export default withRouter(ErrorPage)