You've already forked focalboard
							
							
				mirror of
				https://github.com/mattermost/focalboard.git
				synced 2025-10-31 00:17:42 +02:00 
			
		
		
		
	Workspace backend support
This commit is contained in:
		| @@ -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) | ||||
|   | ||||
| @@ -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)) | ||||
| 	} | ||||
|   | ||||
| @@ -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{}{}, | ||||
| 	}) | ||||
|   | ||||
| @@ -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 | ||||
| 	} | ||||
|   | ||||
| @@ -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()) | ||||
| 	}) | ||||
|   | ||||
| @@ -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) | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
| 	} | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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= | ||||
|   | ||||
| @@ -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 | ||||
| ) | ||||
|   | ||||
| @@ -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= | ||||
|   | ||||
| @@ -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"` | ||||
| } | ||||
|   | ||||
| @@ -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"` | ||||
| } | ||||
|   | ||||
							
								
								
									
										5
									
								
								server/server/initHandlers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								server/server/initHandlers.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| package server | ||||
|  | ||||
| func (s *Server) initHandlers() { | ||||
| 	// Additional init handlers go here | ||||
| } | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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 | ||||
| } | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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, "/")...)...) | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
| 		} | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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; | ||||
| @@ -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); | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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; | ||||
| @@ -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); | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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", | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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) | ||||
| 	}) | ||||
| } | ||||
|   | ||||
| @@ -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) | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -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
											
										
									
								
							| @@ -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: | ||||
|   | ||||
| @@ -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 | ||||
| 			} | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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(), | ||||
|   | ||||
| @@ -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}) | ||||
|   | ||||
							
								
								
									
										41
									
								
								webapp/src/pages/errorPage.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								webapp/src/pages/errorPage.scss
									
									
									
									
									
										Normal 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; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										24
									
								
								webapp/src/pages/errorPage.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								webapp/src/pages/errorPage.tsx
									
									
									
									
									
										Normal 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) | ||||
		Reference in New Issue
	
	Block a user