mirror of
https://github.com/mattermost/focalboard.git
synced 2024-12-24 13:43:12 +02:00
Merge branch 'main' into MM-47238_boards-dev-server-url
This commit is contained in:
commit
8fe9b9b2c2
2
.github/ISSUE_TEMPLATE/doc_improvement.md
vendored
2
.github/ISSUE_TEMPLATE/doc_improvement.md
vendored
@ -2,7 +2,7 @@
|
||||
name: Documentation Request
|
||||
about: Request improvement to our documentation
|
||||
title: 'Doc: '
|
||||
labels: Documentation
|
||||
labels: Documentation, Triage
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
2
.github/ISSUE_TEMPLATE/enhancement.md
vendored
2
.github/ISSUE_TEMPLATE/enhancement.md
vendored
@ -2,7 +2,7 @@
|
||||
name: Enhancement/Feature Idea
|
||||
about: Suggest a new capability
|
||||
title: 'Feature Idea: '
|
||||
labels: Enhancement
|
||||
labels: Enhancement, Triage
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
@ -27,7 +27,7 @@ func normalizeAppErr(appErr *mm_model.AppError) error {
|
||||
// serviceAPIAdapter is an adapter that flattens the APIs provided by suite services so they can
|
||||
// be used as per the Plugin API.
|
||||
// Note: when supporting a plugin build is no longer needed this adapter may be removed as the Boards app
|
||||
// can be modified to use the services in modular fashion.
|
||||
// can be modified to use the services in modular fashion.
|
||||
type serviceAPIAdapter struct {
|
||||
api *boardsProduct
|
||||
ctx *request.Context
|
||||
@ -123,6 +123,10 @@ func (a *serviceAPIAdapter) CreateMember(teamID string, userID string) (*mm_mode
|
||||
// Permissions service.
|
||||
//
|
||||
|
||||
func (a *serviceAPIAdapter) HasPermissionTo(userID string, permission *mm_model.Permission) bool {
|
||||
return a.api.permissionsService.HasPermissionTo(userID, permission)
|
||||
}
|
||||
|
||||
func (a *serviceAPIAdapter) HasPermissionToTeam(userID, teamID string, permission *mm_model.Permission) bool {
|
||||
return a.api.permissionsService.HasPermissionToTeam(userID, teamID, permission)
|
||||
}
|
||||
@ -134,6 +138,7 @@ func (a *serviceAPIAdapter) HasPermissionToChannel(askingUserID string, channelI
|
||||
//
|
||||
// Bot service.
|
||||
//
|
||||
|
||||
func (a *serviceAPIAdapter) EnsureBot(bot *mm_model.Bot) (string, error) {
|
||||
return a.api.botService.EnsureBot(a.ctx, boardsProductID, bot)
|
||||
}
|
||||
@ -141,6 +146,7 @@ func (a *serviceAPIAdapter) EnsureBot(bot *mm_model.Bot) (string, error) {
|
||||
//
|
||||
// License service.
|
||||
//
|
||||
|
||||
func (a *serviceAPIAdapter) GetLicense() *mm_model.License {
|
||||
return a.api.licenseService.GetLicense()
|
||||
}
|
||||
@ -148,6 +154,7 @@ func (a *serviceAPIAdapter) GetLicense() *mm_model.License {
|
||||
//
|
||||
// FileInfoStore service.
|
||||
//
|
||||
|
||||
func (a *serviceAPIAdapter) GetFileInfo(fileID string) (*mm_model.FileInfo, error) {
|
||||
fi, appErr := a.api.fileInfoStoreService.GetFileInfo(fileID)
|
||||
return fi, normalizeAppErr(appErr)
|
||||
@ -156,6 +163,7 @@ func (a *serviceAPIAdapter) GetFileInfo(fileID string) (*mm_model.FileInfo, erro
|
||||
//
|
||||
// Cluster store.
|
||||
//
|
||||
|
||||
func (a *serviceAPIAdapter) PublishWebSocketEvent(event string, payload map[string]interface{}, broadcast *mm_model.WebsocketBroadcast) {
|
||||
a.api.clusterService.PublishWebSocketEvent(boardsProductID, event, payload, broadcast)
|
||||
}
|
||||
@ -167,6 +175,7 @@ func (a *serviceAPIAdapter) PublishPluginClusterEvent(ev mm_model.PluginClusterE
|
||||
//
|
||||
// Cloud service.
|
||||
//
|
||||
|
||||
func (a *serviceAPIAdapter) GetCloudLimits() (*mm_model.ProductLimits, error) {
|
||||
return a.api.cloudService.GetCloudLimits()
|
||||
}
|
||||
@ -174,6 +183,7 @@ func (a *serviceAPIAdapter) GetCloudLimits() (*mm_model.ProductLimits, error) {
|
||||
//
|
||||
// Config service.
|
||||
//
|
||||
|
||||
func (a *serviceAPIAdapter) GetConfig() *mm_model.Config {
|
||||
return a.api.configService.Config()
|
||||
}
|
||||
@ -181,6 +191,7 @@ func (a *serviceAPIAdapter) GetConfig() *mm_model.Config {
|
||||
//
|
||||
// Logger service.
|
||||
//
|
||||
|
||||
func (a *serviceAPIAdapter) GetLogger() mlog.LoggerIFace {
|
||||
return a.api.logger
|
||||
}
|
||||
@ -188,6 +199,7 @@ func (a *serviceAPIAdapter) GetLogger() mlog.LoggerIFace {
|
||||
//
|
||||
// KVStore service.
|
||||
//
|
||||
|
||||
func (a *serviceAPIAdapter) KVSetWithOptions(key string, value []byte, options mm_model.PluginKVSetOptions) (bool, error) {
|
||||
b, appErr := a.api.kvStoreService.SetPluginKeyWithOptions(boardsProductID, key, value, options)
|
||||
return b, normalizeAppErr(appErr)
|
||||
@ -196,6 +208,7 @@ func (a *serviceAPIAdapter) KVSetWithOptions(key string, value []byte, options m
|
||||
//
|
||||
// Store service.
|
||||
//
|
||||
|
||||
func (a *serviceAPIAdapter) GetMasterDB() (*sql.DB, error) {
|
||||
return a.api.storeService.GetMasterDB(), nil
|
||||
}
|
||||
@ -203,6 +216,7 @@ func (a *serviceAPIAdapter) GetMasterDB() (*sql.DB, error) {
|
||||
//
|
||||
// System service.
|
||||
//
|
||||
|
||||
func (a *serviceAPIAdapter) GetDiagnosticID() string {
|
||||
return a.api.systemService.GetDiagnosticId()
|
||||
}
|
||||
@ -210,6 +224,7 @@ func (a *serviceAPIAdapter) GetDiagnosticID() string {
|
||||
//
|
||||
// Router service.
|
||||
//
|
||||
|
||||
func (a *serviceAPIAdapter) RegisterRouter(sub *mux.Router) {
|
||||
a.api.routerService.RegisterRouter(boardsProductName, sub)
|
||||
}
|
||||
@ -217,6 +232,7 @@ func (a *serviceAPIAdapter) RegisterRouter(sub *mux.Router) {
|
||||
//
|
||||
// Preferences service.
|
||||
//
|
||||
|
||||
func (a *serviceAPIAdapter) GetPreferencesForUser(userID string) (mm_model.Preferences, error) {
|
||||
p, appErr := a.api.preferencesService.GetPreferencesForUser(userID)
|
||||
return p, normalizeAppErr(appErr)
|
||||
|
@ -125,6 +125,10 @@ func (a *pluginAPIAdapter) CreateMember(teamID string, userID string) (*mm_model
|
||||
// Permissions service.
|
||||
//
|
||||
|
||||
func (a *pluginAPIAdapter) HasPermissionTo(userID string, permission *mm_model.Permission) bool {
|
||||
return a.api.HasPermissionTo(userID, permission)
|
||||
}
|
||||
|
||||
func (a *pluginAPIAdapter) HasPermissionToTeam(userID, teamID string, permission *mm_model.Permission) bool {
|
||||
return a.api.HasPermissionToTeam(userID, teamID, permission)
|
||||
}
|
||||
@ -136,6 +140,7 @@ func (a *pluginAPIAdapter) HasPermissionToChannel(askingUserID string, channelID
|
||||
//
|
||||
// Bot service.
|
||||
//
|
||||
|
||||
func (a *pluginAPIAdapter) EnsureBot(bot *mm_model.Bot) (string, error) {
|
||||
return a.api.EnsureBotUser(bot)
|
||||
}
|
||||
@ -143,6 +148,7 @@ func (a *pluginAPIAdapter) EnsureBot(bot *mm_model.Bot) (string, error) {
|
||||
//
|
||||
// License service.
|
||||
//
|
||||
|
||||
func (a *pluginAPIAdapter) GetLicense() *mm_model.License {
|
||||
return a.api.GetLicense()
|
||||
}
|
||||
@ -150,6 +156,7 @@ func (a *pluginAPIAdapter) GetLicense() *mm_model.License {
|
||||
//
|
||||
// FileInfoStore service.
|
||||
//
|
||||
|
||||
func (a *pluginAPIAdapter) GetFileInfo(fileID string) (*mm_model.FileInfo, error) {
|
||||
fi, appErr := a.api.GetFileInfo(fileID)
|
||||
return fi, normalizeAppErr(appErr)
|
||||
@ -158,6 +165,7 @@ func (a *pluginAPIAdapter) GetFileInfo(fileID string) (*mm_model.FileInfo, error
|
||||
//
|
||||
// Cluster store.
|
||||
//
|
||||
|
||||
func (a *pluginAPIAdapter) PublishWebSocketEvent(event string, payload map[string]interface{}, broadcast *mm_model.WebsocketBroadcast) {
|
||||
a.api.PublishWebSocketEvent(event, payload, broadcast)
|
||||
}
|
||||
@ -169,6 +177,7 @@ func (a *pluginAPIAdapter) PublishPluginClusterEvent(ev mm_model.PluginClusterEv
|
||||
//
|
||||
// Cloud service.
|
||||
//
|
||||
|
||||
func (a *pluginAPIAdapter) GetCloudLimits() (*mm_model.ProductLimits, error) {
|
||||
return a.api.GetCloudLimits()
|
||||
}
|
||||
@ -176,6 +185,7 @@ func (a *pluginAPIAdapter) GetCloudLimits() (*mm_model.ProductLimits, error) {
|
||||
//
|
||||
// Config service.
|
||||
//
|
||||
|
||||
func (a *pluginAPIAdapter) GetConfig() *mm_model.Config {
|
||||
return a.api.GetUnsanitizedConfig()
|
||||
}
|
||||
@ -183,6 +193,7 @@ func (a *pluginAPIAdapter) GetConfig() *mm_model.Config {
|
||||
//
|
||||
// Logger service.
|
||||
//
|
||||
|
||||
func (a *pluginAPIAdapter) GetLogger() mlog.LoggerIFace {
|
||||
return a.logger
|
||||
}
|
||||
@ -190,6 +201,7 @@ func (a *pluginAPIAdapter) GetLogger() mlog.LoggerIFace {
|
||||
//
|
||||
// KVStore service.
|
||||
//
|
||||
|
||||
func (a *pluginAPIAdapter) KVSetWithOptions(key string, value []byte, options mm_model.PluginKVSetOptions) (bool, error) {
|
||||
b, appErr := a.api.KVSetWithOptions(key, value, options)
|
||||
return b, normalizeAppErr(appErr)
|
||||
@ -198,6 +210,7 @@ func (a *pluginAPIAdapter) KVSetWithOptions(key string, value []byte, options mm
|
||||
//
|
||||
// Store service.
|
||||
//
|
||||
|
||||
func (a *pluginAPIAdapter) GetMasterDB() (*sql.DB, error) {
|
||||
return a.storeService.GetMasterDB()
|
||||
}
|
||||
@ -205,6 +218,7 @@ func (a *pluginAPIAdapter) GetMasterDB() (*sql.DB, error) {
|
||||
//
|
||||
// System service.
|
||||
//
|
||||
|
||||
func (a *pluginAPIAdapter) GetDiagnosticID() string {
|
||||
return a.api.GetDiagnosticId()
|
||||
}
|
||||
@ -212,6 +226,7 @@ func (a *pluginAPIAdapter) GetDiagnosticID() string {
|
||||
//
|
||||
// Router service.
|
||||
//
|
||||
|
||||
func (a *pluginAPIAdapter) RegisterRouter(sub *mux.Router) {
|
||||
// NOOP for plugin
|
||||
}
|
||||
@ -219,6 +234,7 @@ func (a *pluginAPIAdapter) RegisterRouter(sub *mux.Router) {
|
||||
//
|
||||
// Preferences service.
|
||||
//
|
||||
|
||||
func (a *pluginAPIAdapter) GetPreferencesForUser(userID string) (mm_model.Preferences, error) {
|
||||
preferences, appErr := a.api.GetPreferencesForUser(userID)
|
||||
if appErr != nil {
|
||||
|
@ -91,11 +91,11 @@ func (a *appAPI) init(store store.Store, app appIface) {
|
||||
a.app = app
|
||||
}
|
||||
|
||||
func (a *appAPI) GetBlockHistory(blockID string, opts model.QueryBlockHistoryOptions) ([]model.Block, error) {
|
||||
func (a *appAPI) GetBlockHistory(blockID string, opts model.QueryBlockHistoryOptions) ([]*model.Block, error) {
|
||||
return a.store.GetBlockHistory(blockID, opts)
|
||||
}
|
||||
|
||||
func (a *appAPI) GetSubTree2(boardID, blockID string, opts model.QuerySubtreeOptions) ([]model.Block, error) {
|
||||
func (a *appAPI) GetSubTree2(boardID, blockID string, opts model.QuerySubtreeOptions) ([]*model.Block, error) {
|
||||
return a.store.GetSubTree2(boardID, blockID, opts)
|
||||
}
|
||||
|
||||
|
@ -3,12 +3,12 @@
|
||||
|
||||
import React from 'react'
|
||||
import {Provider as ReduxProvider} from 'react-redux'
|
||||
import {render} from '@testing-library/react'
|
||||
import {act, render} from '@testing-library/react'
|
||||
import {mocked} from 'jest-mock'
|
||||
import thunk from 'redux-thunk'
|
||||
|
||||
import octoClient from '../../../../webapp/src/octoClient'
|
||||
import {createBoard} from '../../../../webapp/src/blocks/board'
|
||||
import {BoardMember, createBoard} from '../../../../webapp/src/blocks/board'
|
||||
import {mockStateStore, wrapIntl} from '../../../../webapp/src/testUtils'
|
||||
|
||||
import RHSChannelBoards from './rhsChannelBoards'
|
||||
@ -26,6 +26,10 @@ describe('components/rhsChannelBoards', () => {
|
||||
board1.channelId = 'channel-id'
|
||||
board3.channelId = 'channel-id'
|
||||
|
||||
const boardMembership1 = {boardId: board1.id, userId: 'user-id'} as BoardMember
|
||||
const boardMembership2 = {boardId: board2.id, userId: 'user-id'} as BoardMember
|
||||
const boardMembership3 = {boardId: board3.id, userId: 'user-id'} as BoardMember
|
||||
|
||||
const team = {
|
||||
id: 'team-id',
|
||||
name: 'team',
|
||||
@ -52,9 +56,9 @@ describe('components/rhsChannelBoards', () => {
|
||||
[board3.id]: board3,
|
||||
},
|
||||
myBoardMemberships: {
|
||||
[board1.id]: {boardId: board1.id, userId: 'user-id'},
|
||||
[board2.id]: {boardId: board2.id, userId: 'user-id'},
|
||||
[board3.id]: {boardId: board3.id, userId: 'user-id'},
|
||||
[board1.id]: boardMembership1,
|
||||
[board2.id]: boardMembership2,
|
||||
[board3.id]: boardMembership3,
|
||||
},
|
||||
},
|
||||
channels: {
|
||||
@ -69,27 +73,40 @@ describe('components/rhsChannelBoards', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
mockedOctoClient.getBoards.mockResolvedValue([board1, board2, board3])
|
||||
mockedOctoClient.getMyBoardMemberships.mockResolvedValue([boardMembership1, boardMembership2, boardMembership3])
|
||||
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it('renders the RHS for channel boards', async () => {
|
||||
const store = mockStateStore([thunk], state)
|
||||
const {container} = render(wrapIntl(
|
||||
<ReduxProvider store={store}>
|
||||
<RHSChannelBoards/>
|
||||
</ReduxProvider>
|
||||
))
|
||||
let container: Element | DocumentFragment | null = null
|
||||
await act(async () => {
|
||||
const result = render(wrapIntl(
|
||||
<ReduxProvider store={store}>
|
||||
<RHSChannelBoards/>
|
||||
</ReduxProvider>
|
||||
))
|
||||
container = result.container
|
||||
})
|
||||
|
||||
expect(container).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('renders with empty list of boards', async () => {
|
||||
const localState = {...state, boards: {...state.boards, boards: {}}}
|
||||
const store = mockStateStore([thunk], localState)
|
||||
const {container} = render(wrapIntl(
|
||||
<ReduxProvider store={store}>
|
||||
<RHSChannelBoards/>
|
||||
</ReduxProvider>
|
||||
))
|
||||
|
||||
let container: Element | DocumentFragment | null = null
|
||||
await act(async () => {
|
||||
const result = render(wrapIntl(
|
||||
<ReduxProvider store={store}>
|
||||
<RHSChannelBoards/>
|
||||
</ReduxProvider>
|
||||
))
|
||||
container = result.container
|
||||
})
|
||||
|
||||
expect(container).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
@ -12,7 +12,7 @@ import {Board, BoardMember} from '../../../../webapp/src/blocks/board'
|
||||
import {getCurrentTeamId} from '../../../../webapp/src/store/teams'
|
||||
import {IUser} from '../../../../webapp/src/user'
|
||||
import {getMe, fetchMe} from '../../../../webapp/src/store/users'
|
||||
import {loadBoards} from '../../../../webapp/src/store/initialLoad'
|
||||
import {loadBoards, loadMyBoardsMemberships} from '../../../../webapp/src/store/initialLoad'
|
||||
import {getCurrentChannel} from '../../../../webapp/src/store/channels'
|
||||
import {
|
||||
getMySortedBoards,
|
||||
@ -41,10 +41,14 @@ const RHSChannelBoards = () => {
|
||||
const me = useAppSelector<IUser|null>(getMe)
|
||||
const dispatch = useAppDispatch()
|
||||
const intl = useIntl()
|
||||
const [dataLoaded, setDataLoaded] = React.useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(loadBoards())
|
||||
dispatch(fetchMe())
|
||||
Promise.all([
|
||||
dispatch(loadBoards()),
|
||||
dispatch(loadMyBoardsMemberships()),
|
||||
dispatch(fetchMe()),
|
||||
]).then(() => setDataLoaded(true))
|
||||
}, [])
|
||||
|
||||
useWebsockets(teamId || '', (wsClient: WSClient) => {
|
||||
@ -78,6 +82,10 @@ const RHSChannelBoards = () => {
|
||||
if (!currentChannel) {
|
||||
return null
|
||||
}
|
||||
if (!dataLoaded) {
|
||||
return null
|
||||
}
|
||||
|
||||
const channelBoards = boards.filter((b) => b.channelId === currentChannel.id)
|
||||
|
||||
let channelName = currentChannel.display_name
|
||||
|
@ -352,6 +352,30 @@ export default class Plugin {
|
||||
return data
|
||||
})
|
||||
}
|
||||
|
||||
// Site statistics handler
|
||||
if (registry.registerSiteStatisticsHandler) {
|
||||
registry.registerSiteStatisticsHandler(async () => {
|
||||
const siteStats = await octoClient.getSiteStatistics()
|
||||
if(siteStats){
|
||||
return {
|
||||
boards_count: {
|
||||
name: intl.formatMessage({id: 'SiteStats.total_boards', defaultMessage: 'Total Boards'}),
|
||||
id: 'total_boards',
|
||||
icon: 'icon-product-boards',
|
||||
value: siteStats.board_count,
|
||||
},
|
||||
cards_count: {
|
||||
name: intl.formatMessage({id: 'SiteStats.total_cards', defaultMessage: 'Total Cards'}),
|
||||
id: 'total_cards',
|
||||
icon: 'icon-products',
|
||||
value: siteStats.card_count,
|
||||
},
|
||||
}
|
||||
}
|
||||
return {}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
this.boardSelectorId = this.registry.registerRootComponent((props: {webSocketClient: MMWebSocketClient}) => (
|
||||
|
@ -18,6 +18,7 @@ export interface PluginRegistry {
|
||||
registerRightHandSidebarComponent(component: React.ElementType, title: React.Element)
|
||||
registerRootComponent(component: React.ElementType)
|
||||
registerInsightsHandler(handler: (timeRange: string, page: number, perPage: number, teamId: string, insightType: string) => void)
|
||||
registerSiteStatisticsHandler(handler: () => void)
|
||||
|
||||
// Add more if needed from https://developers.mattermost.com/extend/plugins/webapp/reference
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ const config = {
|
||||
type: 'asset/resource',
|
||||
generator: {
|
||||
filename: '[name][ext]',
|
||||
publicPath: '/static/',
|
||||
publicPath: TARGET_IS_PRODUCT ? undefined : '/static/',
|
||||
}
|
||||
},
|
||||
],
|
||||
@ -205,12 +205,6 @@ config.plugins.push(new webpack.DefinePlugin({
|
||||
if (NPM_TARGET === 'start:product') {
|
||||
const url = new URL(process.env.MM_BOARDS_DEV_SERVER_URL ?? 'http://localhost:9006');
|
||||
|
||||
for (const rule of config.module.rules) {
|
||||
if (rule.type === 'asset/resource' && rule.generator) {
|
||||
rule.generator.publicPath = url.toString() + 'static/';
|
||||
}
|
||||
}
|
||||
|
||||
config.devServer = {
|
||||
https: url.protocol === 'https:' && {
|
||||
minVersion: process.env.MM_SERVICESETTINGS_TLSMINVER,
|
||||
|
@ -95,6 +95,7 @@ func (a *API) RegisterRoutes(r *mux.Router) {
|
||||
a.registerTemplatesRoutes(apiv2)
|
||||
a.registerBoardsRoutes(apiv2)
|
||||
a.registerBlocksRoutes(apiv2)
|
||||
a.registerStatisticsRoutes(apiv2)
|
||||
|
||||
// V3 routes
|
||||
a.registerCardsRoutes(apiv2)
|
||||
|
@ -121,7 +121,7 @@ func (a *API) handleGetBlocks(w http.ResponseWriter, r *http.Request) {
|
||||
auditRec.AddMeta("all", all)
|
||||
auditRec.AddMeta("blockID", blockID)
|
||||
|
||||
var blocks []model.Block
|
||||
var blocks []*model.Block
|
||||
var block *model.Block
|
||||
switch {
|
||||
case all != "":
|
||||
@ -142,7 +142,7 @@ func (a *API) handleGetBlocks(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
blocks = append(blocks, *block)
|
||||
blocks = append(blocks, block)
|
||||
default:
|
||||
blocks, err = a.app.GetBlocks(boardID, parentID, blockType)
|
||||
if err != nil {
|
||||
@ -233,7 +233,7 @@ func (a *API) handlePostBlocks(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
var blocks []model.Block
|
||||
var blocks []*model.Block
|
||||
|
||||
err = json.Unmarshal(requestBody, &blocks)
|
||||
if err != nil {
|
||||
|
70
server/api/statistics.go
Normal file
70
server/api/statistics.go
Normal file
@ -0,0 +1,70 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/mattermost/focalboard/server/model"
|
||||
mmModel "github.com/mattermost/mattermost-server/v6/model"
|
||||
)
|
||||
|
||||
func (a *API) registerStatisticsRoutes(r *mux.Router) {
|
||||
// statistics
|
||||
r.HandleFunc("/statistics", a.sessionRequired(a.handleStatistics)).Methods("GET")
|
||||
}
|
||||
|
||||
func (a *API) handleStatistics(w http.ResponseWriter, r *http.Request) {
|
||||
// swagger:operation GET /statistics handleStatistics
|
||||
//
|
||||
// Fetches the statistic of the server.
|
||||
//
|
||||
// ---
|
||||
// produces:
|
||||
// - application/json
|
||||
// security:
|
||||
// - BearerAuth: []
|
||||
// responses:
|
||||
// '200':
|
||||
// description: success
|
||||
// schema:
|
||||
// "$ref": "#/definitions/BoardStatistics"
|
||||
// default:
|
||||
// description: internal error
|
||||
// schema:
|
||||
// "$ref": "#/definitions/ErrorResponse"
|
||||
if !a.MattermostAuth {
|
||||
a.errorResponse(w, r, model.NewErrNotImplemented("not permitted in standalone mode"))
|
||||
return
|
||||
}
|
||||
|
||||
// user must have right to access analytics
|
||||
userID := getUserID(r)
|
||||
if !a.permissions.HasPermissionTo(userID, mmModel.PermissionGetAnalytics) {
|
||||
a.errorResponse(w, r, model.NewErrPermission("access denied System Statistics"))
|
||||
return
|
||||
}
|
||||
|
||||
boardCount, err := a.app.GetBoardCount()
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, err)
|
||||
return
|
||||
}
|
||||
cardCount, err := a.app.GetUsedCardsCount()
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
stats := model.BoardsStatistics{
|
||||
Boards: int(boardCount),
|
||||
Cards: cardCount,
|
||||
}
|
||||
data, err := json.Marshal(stats)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
jsonBytesResponse(w, http.StatusOK, data)
|
||||
}
|
@ -14,9 +14,9 @@ import (
|
||||
|
||||
var ErrBlocksFromMultipleBoards = errors.New("the block set contain blocks from multiple boards")
|
||||
|
||||
func (a *App) GetBlocks(boardID, parentID string, blockType string) ([]model.Block, error) {
|
||||
func (a *App) GetBlocks(boardID, parentID string, blockType string) ([]*model.Block, error) {
|
||||
if boardID == "" {
|
||||
return []model.Block{}, nil
|
||||
return []*model.Block{}, nil
|
||||
}
|
||||
|
||||
if blockType != "" && parentID != "" {
|
||||
@ -30,7 +30,7 @@ func (a *App) GetBlocks(boardID, parentID string, blockType string) ([]model.Blo
|
||||
return a.store.GetBlocksWithParent(boardID, parentID)
|
||||
}
|
||||
|
||||
func (a *App) DuplicateBlock(boardID string, blockID string, userID string, asTemplate bool) ([]model.Block, error) {
|
||||
func (a *App) DuplicateBlock(boardID string, blockID string, userID string, asTemplate bool) ([]*model.Block, error) {
|
||||
board, err := a.GetBoard(boardID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -74,7 +74,7 @@ func (a *App) PatchBlockAndNotify(blockID string, blockPatch *model.BlockPatch,
|
||||
}
|
||||
|
||||
if a.IsCloudLimited() {
|
||||
containsLimitedBlocks, lErr := a.ContainsLimitedBlocks([]model.Block{*oldBlock})
|
||||
containsLimitedBlocks, lErr := a.ContainsLimitedBlocks([]*model.Block{oldBlock})
|
||||
if lErr != nil {
|
||||
return nil, lErr
|
||||
}
|
||||
@ -100,10 +100,10 @@ func (a *App) PatchBlockAndNotify(blockID string, blockPatch *model.BlockPatch,
|
||||
}
|
||||
a.blockChangeNotifier.Enqueue(func() error {
|
||||
// broadcast on websocket
|
||||
a.wsAdapter.BroadcastBlockChange(board.TeamID, *block)
|
||||
a.wsAdapter.BroadcastBlockChange(board.TeamID, block)
|
||||
|
||||
// broadcast on webhooks
|
||||
a.webhook.NotifyUpdate(*block)
|
||||
a.webhook.NotifyUpdate(block)
|
||||
|
||||
// send notifications
|
||||
if !disableNotify {
|
||||
@ -145,10 +145,10 @@ func (a *App) PatchBlocksAndNotify(teamID string, blockPatches *model.BlockPatch
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.wsAdapter.BroadcastBlockChange(teamID, *newBlock)
|
||||
a.webhook.NotifyUpdate(*newBlock)
|
||||
a.wsAdapter.BroadcastBlockChange(teamID, newBlock)
|
||||
a.webhook.NotifyUpdate(newBlock)
|
||||
if !disableNotify {
|
||||
a.notifyBlockChanged(notify.Update, newBlock, &oldBlocks[i], modifiedByID)
|
||||
a.notifyBlockChanged(notify.Update, newBlock, oldBlocks[i], modifiedByID)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -156,24 +156,24 @@ func (a *App) PatchBlocksAndNotify(teamID string, blockPatches *model.BlockPatch
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) InsertBlock(block model.Block, modifiedByID string) error {
|
||||
func (a *App) InsertBlock(block *model.Block, modifiedByID string) error {
|
||||
return a.InsertBlockAndNotify(block, modifiedByID, false)
|
||||
}
|
||||
|
||||
func (a *App) InsertBlockAndNotify(block model.Block, modifiedByID string, disableNotify bool) error {
|
||||
func (a *App) InsertBlockAndNotify(block *model.Block, modifiedByID string, disableNotify bool) error {
|
||||
board, bErr := a.store.GetBoard(block.BoardID)
|
||||
if bErr != nil {
|
||||
return bErr
|
||||
}
|
||||
|
||||
err := a.store.InsertBlock(&block, modifiedByID)
|
||||
err := a.store.InsertBlock(block, modifiedByID)
|
||||
if err == nil {
|
||||
a.blockChangeNotifier.Enqueue(func() error {
|
||||
a.wsAdapter.BroadcastBlockChange(board.TeamID, block)
|
||||
a.metrics.IncrementBlocksInserted(1)
|
||||
a.webhook.NotifyUpdate(block)
|
||||
if !disableNotify {
|
||||
a.notifyBlockChanged(notify.Add, &block, nil, modifiedByID)
|
||||
a.notifyBlockChanged(notify.Add, block, nil, modifiedByID)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@ -191,7 +191,7 @@ func (a *App) InsertBlockAndNotify(block model.Block, modifiedByID string, disab
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *App) isWithinViewsLimit(boardID string, block model.Block) (bool, error) {
|
||||
func (a *App) isWithinViewsLimit(boardID string, block *model.Block) (bool, error) {
|
||||
limits, err := a.GetBoardsCloudLimits()
|
||||
if err != nil {
|
||||
return false, err
|
||||
@ -213,13 +213,13 @@ func (a *App) isWithinViewsLimit(boardID string, block model.Block) (bool, error
|
||||
return len(views) < limits.Views, nil
|
||||
}
|
||||
|
||||
func (a *App) InsertBlocks(blocks []model.Block, modifiedByID string) ([]model.Block, error) {
|
||||
func (a *App) InsertBlocks(blocks []*model.Block, modifiedByID string) ([]*model.Block, error) {
|
||||
return a.InsertBlocksAndNotify(blocks, modifiedByID, false)
|
||||
}
|
||||
|
||||
func (a *App) InsertBlocksAndNotify(blocks []model.Block, modifiedByID string, disableNotify bool) ([]model.Block, error) {
|
||||
func (a *App) InsertBlocksAndNotify(blocks []*model.Block, modifiedByID string, disableNotify bool) ([]*model.Block, error) {
|
||||
if len(blocks) == 0 {
|
||||
return []model.Block{}, nil
|
||||
return []*model.Block{}, nil
|
||||
}
|
||||
|
||||
// all blocks must belong to the same board
|
||||
@ -235,7 +235,7 @@ func (a *App) InsertBlocksAndNotify(blocks []model.Block, modifiedByID string, d
|
||||
return nil, err
|
||||
}
|
||||
|
||||
needsNotify := make([]model.Block, 0, len(blocks))
|
||||
needsNotify := make([]*model.Block, 0, len(blocks))
|
||||
for i := range blocks {
|
||||
// this check is needed to whitelist inbuilt template
|
||||
// initialization. They do contain more than 5 views per board.
|
||||
@ -251,7 +251,7 @@ func (a *App) InsertBlocksAndNotify(blocks []model.Block, modifiedByID string, d
|
||||
}
|
||||
}
|
||||
|
||||
err := a.store.InsertBlock(&blocks[i], modifiedByID)
|
||||
err := a.store.InsertBlock(blocks[i], modifiedByID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -266,7 +266,7 @@ func (a *App) InsertBlocksAndNotify(blocks []model.Block, modifiedByID string, d
|
||||
block := b
|
||||
a.webhook.NotifyUpdate(block)
|
||||
if !disableNotify {
|
||||
a.notifyBlockChanged(notify.Add, &block, nil, modifiedByID)
|
||||
a.notifyBlockChanged(notify.Add, block, nil, modifiedByID)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -284,7 +284,7 @@ func (a *App) InsertBlocksAndNotify(blocks []model.Block, modifiedByID string, d
|
||||
return blocks, nil
|
||||
}
|
||||
|
||||
func (a *App) CopyCardFiles(sourceBoardID string, copiedBlocks []model.Block) error {
|
||||
func (a *App) CopyCardFiles(sourceBoardID string, copiedBlocks []*model.Block) error {
|
||||
// Images attached in cards have a path comprising the card's board ID.
|
||||
// When we create a template from this board, we need to copy the files
|
||||
// with the new board ID in path.
|
||||
@ -416,7 +416,7 @@ func (a *App) GetLastBlockHistoryEntry(blockID string) (*model.Block, error) {
|
||||
if len(blocks) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return &blocks[0], nil
|
||||
return blocks[0], nil
|
||||
}
|
||||
|
||||
func (a *App) UndeleteBlock(blockID string, modifiedBy string) (*model.Block, error) {
|
||||
@ -450,9 +450,9 @@ func (a *App) UndeleteBlock(blockID string, modifiedBy string) (*model.Block, er
|
||||
}
|
||||
|
||||
a.blockChangeNotifier.Enqueue(func() error {
|
||||
a.wsAdapter.BroadcastBlockChange(board.TeamID, *block)
|
||||
a.wsAdapter.BroadcastBlockChange(board.TeamID, block)
|
||||
a.metrics.IncrementBlocksInserted(1)
|
||||
a.webhook.NotifyUpdate(*block)
|
||||
a.webhook.NotifyUpdate(block)
|
||||
a.notifyBlockChanged(notify.Add, block, nil, modifiedBy)
|
||||
|
||||
return nil
|
||||
@ -474,7 +474,7 @@ func (a *App) GetBlockCountsByType() (map[string]int64, error) {
|
||||
return a.store.GetBlockCountsByType()
|
||||
}
|
||||
|
||||
func (a *App) GetBlocksForBoard(boardID string) ([]model.Block, error) {
|
||||
func (a *App) GetBlocksForBoard(boardID string) ([]*model.Block, error) {
|
||||
return a.store.GetBlocksForBoard(boardID)
|
||||
}
|
||||
|
||||
|
@ -28,10 +28,10 @@ func TestInsertBlock(t *testing.T) {
|
||||
|
||||
t.Run("success scenario", func(t *testing.T) {
|
||||
boardID := testBoardID
|
||||
block := model.Block{BoardID: boardID}
|
||||
block := &model.Block{BoardID: boardID}
|
||||
board := &model.Board{ID: boardID}
|
||||
th.Store.EXPECT().GetBoard(boardID).Return(board, nil)
|
||||
th.Store.EXPECT().InsertBlock(&block, "user-id-1").Return(nil)
|
||||
th.Store.EXPECT().InsertBlock(block, "user-id-1").Return(nil)
|
||||
th.Store.EXPECT().GetMembersForBoard(boardID).Return([]*model.BoardMember{}, nil)
|
||||
err := th.App.InsertBlock(block, "user-id-1")
|
||||
require.NoError(t, err)
|
||||
@ -39,10 +39,10 @@ func TestInsertBlock(t *testing.T) {
|
||||
|
||||
t.Run("error scenario", func(t *testing.T) {
|
||||
boardID := testBoardID
|
||||
block := model.Block{BoardID: boardID}
|
||||
block := &model.Block{BoardID: boardID}
|
||||
board := &model.Board{ID: boardID}
|
||||
th.Store.EXPECT().GetBoard(boardID).Return(board, nil)
|
||||
th.Store.EXPECT().InsertBlock(&block, "user-id-1").Return(blockError{"error"})
|
||||
th.Store.EXPECT().InsertBlock(block, "user-id-1").Return(blockError{"error"})
|
||||
err := th.App.InsertBlock(block, "user-id-1")
|
||||
require.Error(t, err, "error")
|
||||
})
|
||||
@ -60,10 +60,10 @@ func TestPatchBlocks(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
block1 := model.Block{ID: "block1"}
|
||||
th.Store.EXPECT().GetBlocksByIDs([]string{"block1"}).Return([]model.Block{block1}, nil)
|
||||
block1 := &model.Block{ID: "block1"}
|
||||
th.Store.EXPECT().GetBlocksByIDs([]string{"block1"}).Return([]*model.Block{block1}, nil)
|
||||
th.Store.EXPECT().PatchBlocks(gomock.Eq(&blockPatches), gomock.Eq("user-id-1")).Return(nil)
|
||||
th.Store.EXPECT().GetBlock("block1").Return(&block1, nil)
|
||||
th.Store.EXPECT().GetBlock("block1").Return(block1, nil)
|
||||
// this call comes from the WS server notification
|
||||
th.Store.EXPECT().GetMembersForBoard(gomock.Any()).Times(1)
|
||||
err := th.App.PatchBlocks("team-id", &blockPatches, "user-id-1")
|
||||
@ -91,7 +91,7 @@ func TestPatchBlocks(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
block1 := model.Block{
|
||||
block1 := &model.Block{
|
||||
ID: "block1",
|
||||
Type: model.TypeCard,
|
||||
ParentID: "board-id",
|
||||
@ -104,7 +104,7 @@ func TestPatchBlocks(t *testing.T) {
|
||||
Type: model.BoardTypeOpen,
|
||||
}
|
||||
|
||||
th.Store.EXPECT().GetBlocksByIDs([]string{"block1"}).Return([]model.Block{block1}, nil)
|
||||
th.Store.EXPECT().GetBlocksByIDs([]string{"block1"}).Return([]*model.Block{block1}, nil)
|
||||
th.Store.EXPECT().GetBoard("board-id").Return(board1, nil)
|
||||
th.Store.EXPECT().GetLicense().Return(fakeLicense)
|
||||
th.Store.EXPECT().GetCardLimitTimestamp().Return(int64(150), nil)
|
||||
@ -120,11 +120,11 @@ func TestDeleteBlock(t *testing.T) {
|
||||
t.Run("success scenario", func(t *testing.T) {
|
||||
boardID := testBoardID
|
||||
board := &model.Board{ID: boardID}
|
||||
block := model.Block{
|
||||
block := &model.Block{
|
||||
ID: "block-id",
|
||||
BoardID: board.ID,
|
||||
}
|
||||
th.Store.EXPECT().GetBlock(gomock.Eq("block-id")).Return(&block, nil)
|
||||
th.Store.EXPECT().GetBlock(gomock.Eq("block-id")).Return(block, nil)
|
||||
th.Store.EXPECT().DeleteBlock(gomock.Eq("block-id"), gomock.Eq("user-id-1")).Return(nil)
|
||||
th.Store.EXPECT().GetBoard(gomock.Eq(testBoardID)).Return(board, nil)
|
||||
th.Store.EXPECT().GetMembersForBoard(boardID).Return([]*model.BoardMember{}, nil)
|
||||
@ -135,11 +135,11 @@ func TestDeleteBlock(t *testing.T) {
|
||||
t.Run("error scenario", func(t *testing.T) {
|
||||
boardID := testBoardID
|
||||
board := &model.Board{ID: boardID}
|
||||
block := model.Block{
|
||||
block := &model.Block{
|
||||
ID: "block-id",
|
||||
BoardID: board.ID,
|
||||
}
|
||||
th.Store.EXPECT().GetBlock(gomock.Eq("block-id")).Return(&block, nil)
|
||||
th.Store.EXPECT().GetBlock(gomock.Eq("block-id")).Return(block, nil)
|
||||
th.Store.EXPECT().DeleteBlock(gomock.Eq("block-id"), gomock.Eq("user-id-1")).Return(blockError{"error"})
|
||||
th.Store.EXPECT().GetBoard(gomock.Eq(testBoardID)).Return(board, nil)
|
||||
err := th.App.DeleteBlock("block-id", "user-id-1")
|
||||
@ -154,16 +154,16 @@ func TestUndeleteBlock(t *testing.T) {
|
||||
t.Run("success scenario", func(t *testing.T) {
|
||||
boardID := testBoardID
|
||||
board := &model.Board{ID: boardID}
|
||||
block := model.Block{
|
||||
block := &model.Block{
|
||||
ID: "block-id",
|
||||
BoardID: board.ID,
|
||||
}
|
||||
th.Store.EXPECT().GetBlockHistory(
|
||||
gomock.Eq("block-id"),
|
||||
gomock.Eq(model.QueryBlockHistoryOptions{Limit: 1, Descending: true}),
|
||||
).Return([]model.Block{block}, nil)
|
||||
).Return([]*model.Block{block}, nil)
|
||||
th.Store.EXPECT().UndeleteBlock(gomock.Eq("block-id"), gomock.Eq("user-id-1")).Return(nil)
|
||||
th.Store.EXPECT().GetBlock(gomock.Eq("block-id")).Return(&block, nil)
|
||||
th.Store.EXPECT().GetBlock(gomock.Eq("block-id")).Return(block, nil)
|
||||
th.Store.EXPECT().GetBoard(boardID).Return(board, nil)
|
||||
th.Store.EXPECT().GetMembersForBoard(boardID).Return([]*model.BoardMember{}, nil)
|
||||
_, err := th.App.UndeleteBlock("block-id", "user-id-1")
|
||||
@ -171,13 +171,13 @@ func TestUndeleteBlock(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("error scenario", func(t *testing.T) {
|
||||
block := model.Block{
|
||||
block := &model.Block{
|
||||
ID: "block-id",
|
||||
}
|
||||
th.Store.EXPECT().GetBlockHistory(
|
||||
gomock.Eq("block-id"),
|
||||
gomock.Eq(model.QueryBlockHistoryOptions{Limit: 1, Descending: true}),
|
||||
).Return([]model.Block{block}, nil)
|
||||
).Return([]*model.Block{block}, nil)
|
||||
th.Store.EXPECT().UndeleteBlock(gomock.Eq("block-id"), gomock.Eq("user-id-1")).Return(blockError{"error"})
|
||||
_, err := th.App.UndeleteBlock("block-id", "user-id-1")
|
||||
require.Error(t, err, "error")
|
||||
@ -203,9 +203,9 @@ func TestIsWithinViewsLimit(t *testing.T) {
|
||||
th.Store.EXPECT().GetCloudLimits().Return(cloudLimit, nil)
|
||||
th.Store.EXPECT().GetUsedCardsCount().Return(1, nil)
|
||||
th.Store.EXPECT().GetCardLimitTimestamp().Return(int64(1), nil)
|
||||
th.Store.EXPECT().GetBlocksWithParentAndType("board_id", "parent_id", "view").Return([]model.Block{{}}, nil)
|
||||
th.Store.EXPECT().GetBlocksWithParentAndType("board_id", "parent_id", "view").Return([]*model.Block{{}}, nil)
|
||||
|
||||
withinLimits, err := th.App.isWithinViewsLimit("board_id", model.Block{ParentID: "parent_id"})
|
||||
withinLimits, err := th.App.isWithinViewsLimit("board_id", &model.Block{ParentID: "parent_id"})
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, withinLimits)
|
||||
})
|
||||
@ -221,9 +221,9 @@ func TestIsWithinViewsLimit(t *testing.T) {
|
||||
th.Store.EXPECT().GetCloudLimits().Return(cloudLimit, nil)
|
||||
th.Store.EXPECT().GetUsedCardsCount().Return(1, nil)
|
||||
th.Store.EXPECT().GetCardLimitTimestamp().Return(int64(1), nil)
|
||||
th.Store.EXPECT().GetBlocksWithParentAndType("board_id", "parent_id", "view").Return([]model.Block{{}}, nil)
|
||||
th.Store.EXPECT().GetBlocksWithParentAndType("board_id", "parent_id", "view").Return([]*model.Block{{}}, nil)
|
||||
|
||||
withinLimits, err := th.App.isWithinViewsLimit("board_id", model.Block{ParentID: "parent_id"})
|
||||
withinLimits, err := th.App.isWithinViewsLimit("board_id", &model.Block{ParentID: "parent_id"})
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, withinLimits)
|
||||
})
|
||||
@ -239,9 +239,9 @@ func TestIsWithinViewsLimit(t *testing.T) {
|
||||
th.Store.EXPECT().GetCloudLimits().Return(cloudLimit, nil)
|
||||
th.Store.EXPECT().GetUsedCardsCount().Return(1, nil)
|
||||
th.Store.EXPECT().GetCardLimitTimestamp().Return(int64(1), nil)
|
||||
th.Store.EXPECT().GetBlocksWithParentAndType("board_id", "parent_id", "view").Return([]model.Block{{}, {}, {}}, nil)
|
||||
th.Store.EXPECT().GetBlocksWithParentAndType("board_id", "parent_id", "view").Return([]*model.Block{{}, {}, {}}, nil)
|
||||
|
||||
withinLimits, err := th.App.isWithinViewsLimit("board_id", model.Block{ParentID: "parent_id"})
|
||||
withinLimits, err := th.App.isWithinViewsLimit("board_id", &model.Block{ParentID: "parent_id"})
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, withinLimits)
|
||||
})
|
||||
@ -257,9 +257,9 @@ func TestIsWithinViewsLimit(t *testing.T) {
|
||||
th.Store.EXPECT().GetCloudLimits().Return(cloudLimit, nil)
|
||||
th.Store.EXPECT().GetUsedCardsCount().Return(1, nil)
|
||||
th.Store.EXPECT().GetCardLimitTimestamp().Return(int64(1), nil)
|
||||
th.Store.EXPECT().GetBlocksWithParentAndType("board_id", "parent_id", "view").Return([]model.Block{}, nil)
|
||||
th.Store.EXPECT().GetBlocksWithParentAndType("board_id", "parent_id", "view").Return([]*model.Block{}, nil)
|
||||
|
||||
withinLimits, err := th.App.isWithinViewsLimit("board_id", model.Block{ParentID: "parent_id"})
|
||||
withinLimits, err := th.App.isWithinViewsLimit("board_id", &model.Block{ParentID: "parent_id"})
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, withinLimits)
|
||||
})
|
||||
@ -270,7 +270,7 @@ func TestIsWithinViewsLimit(t *testing.T) {
|
||||
}
|
||||
th.Store.EXPECT().GetLicense().Return(nonCloudLicense)
|
||||
|
||||
withinLimits, err := th.App.isWithinViewsLimit("board_id", model.Block{ParentID: "parent_id"})
|
||||
withinLimits, err := th.App.isWithinViewsLimit("board_id", &model.Block{ParentID: "parent_id"})
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, withinLimits)
|
||||
})
|
||||
@ -282,35 +282,35 @@ func TestInsertBlocks(t *testing.T) {
|
||||
|
||||
t.Run("success scenario", func(t *testing.T) {
|
||||
boardID := testBoardID
|
||||
block := model.Block{BoardID: boardID}
|
||||
block := &model.Block{BoardID: boardID}
|
||||
board := &model.Board{ID: boardID}
|
||||
th.Store.EXPECT().GetBoard(boardID).Return(board, nil)
|
||||
th.Store.EXPECT().InsertBlock(&block, "user-id-1").Return(nil)
|
||||
th.Store.EXPECT().InsertBlock(block, "user-id-1").Return(nil)
|
||||
th.Store.EXPECT().GetMembersForBoard(boardID).Return([]*model.BoardMember{}, nil)
|
||||
_, err := th.App.InsertBlocks([]model.Block{block}, "user-id-1")
|
||||
_, err := th.App.InsertBlocks([]*model.Block{block}, "user-id-1")
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("error scenario", func(t *testing.T) {
|
||||
boardID := testBoardID
|
||||
block := model.Block{BoardID: boardID}
|
||||
block := &model.Block{BoardID: boardID}
|
||||
board := &model.Board{ID: boardID}
|
||||
th.Store.EXPECT().GetBoard(boardID).Return(board, nil)
|
||||
th.Store.EXPECT().InsertBlock(&block, "user-id-1").Return(blockError{"error"})
|
||||
_, err := th.App.InsertBlocks([]model.Block{block}, "user-id-1")
|
||||
th.Store.EXPECT().InsertBlock(block, "user-id-1").Return(blockError{"error"})
|
||||
_, err := th.App.InsertBlocks([]*model.Block{block}, "user-id-1")
|
||||
require.Error(t, err, "error")
|
||||
})
|
||||
|
||||
t.Run("create view within limits", func(t *testing.T) {
|
||||
boardID := testBoardID
|
||||
block := model.Block{
|
||||
block := &model.Block{
|
||||
Type: model.TypeView,
|
||||
ParentID: "parent_id",
|
||||
BoardID: boardID,
|
||||
}
|
||||
board := &model.Board{ID: boardID}
|
||||
th.Store.EXPECT().GetBoard(boardID).Return(board, nil)
|
||||
th.Store.EXPECT().InsertBlock(&block, "user-id-1").Return(nil)
|
||||
th.Store.EXPECT().InsertBlock(block, "user-id-1").Return(nil)
|
||||
th.Store.EXPECT().GetMembersForBoard(boardID).Return([]*model.BoardMember{}, nil)
|
||||
|
||||
// setting up mocks for limits
|
||||
@ -327,15 +327,15 @@ func TestInsertBlocks(t *testing.T) {
|
||||
th.Store.EXPECT().GetCloudLimits().Return(cloudLimit, nil)
|
||||
th.Store.EXPECT().GetUsedCardsCount().Return(1, nil)
|
||||
th.Store.EXPECT().GetCardLimitTimestamp().Return(int64(1), nil)
|
||||
th.Store.EXPECT().GetBlocksWithParentAndType("test-board-id", "parent_id", "view").Return([]model.Block{{}}, nil)
|
||||
th.Store.EXPECT().GetBlocksWithParentAndType("test-board-id", "parent_id", "view").Return([]*model.Block{{}}, nil)
|
||||
|
||||
_, err := th.App.InsertBlocks([]model.Block{block}, "user-id-1")
|
||||
_, err := th.App.InsertBlocks([]*model.Block{block}, "user-id-1")
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("create view exceeding limits", func(t *testing.T) {
|
||||
boardID := testBoardID
|
||||
block := model.Block{
|
||||
block := &model.Block{
|
||||
Type: model.TypeView,
|
||||
ParentID: "parent_id",
|
||||
BoardID: boardID,
|
||||
@ -357,9 +357,9 @@ func TestInsertBlocks(t *testing.T) {
|
||||
th.Store.EXPECT().GetCloudLimits().Return(cloudLimit, nil)
|
||||
th.Store.EXPECT().GetUsedCardsCount().Return(1, nil)
|
||||
th.Store.EXPECT().GetCardLimitTimestamp().Return(int64(1), nil)
|
||||
th.Store.EXPECT().GetBlocksWithParentAndType("test-board-id", "parent_id", "view").Return([]model.Block{{}, {}}, nil)
|
||||
th.Store.EXPECT().GetBlocksWithParentAndType("test-board-id", "parent_id", "view").Return([]*model.Block{{}, {}}, nil)
|
||||
|
||||
_, err := th.App.InsertBlocks([]model.Block{block}, "user-id-1")
|
||||
_, err := th.App.InsertBlocks([]*model.Block{block}, "user-id-1")
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
@ -367,13 +367,13 @@ func TestInsertBlocks(t *testing.T) {
|
||||
t.Skipf("Will be fixed soon")
|
||||
|
||||
boardID := testBoardID
|
||||
view1 := model.Block{
|
||||
view1 := &model.Block{
|
||||
Type: model.TypeView,
|
||||
ParentID: "parent_id",
|
||||
BoardID: boardID,
|
||||
}
|
||||
|
||||
view2 := model.Block{
|
||||
view2 := &model.Block{
|
||||
Type: model.TypeView,
|
||||
ParentID: "parent_id",
|
||||
BoardID: boardID,
|
||||
@ -381,7 +381,7 @@ func TestInsertBlocks(t *testing.T) {
|
||||
|
||||
board := &model.Board{ID: boardID}
|
||||
th.Store.EXPECT().GetBoard(boardID).Return(board, nil)
|
||||
th.Store.EXPECT().InsertBlock(&view1, "user-id-1").Return(nil).Times(2)
|
||||
th.Store.EXPECT().InsertBlock(view1, "user-id-1").Return(nil).Times(2)
|
||||
th.Store.EXPECT().GetMembersForBoard(boardID).Return([]*model.BoardMember{}, nil).Times(2)
|
||||
|
||||
// setting up mocks for limits
|
||||
@ -398,9 +398,9 @@ func TestInsertBlocks(t *testing.T) {
|
||||
th.Store.EXPECT().GetCloudLimits().Return(cloudLimit, nil).Times(2)
|
||||
th.Store.EXPECT().GetUsedCardsCount().Return(1, nil).Times(2)
|
||||
th.Store.EXPECT().GetCardLimitTimestamp().Return(int64(1), nil).Times(2)
|
||||
th.Store.EXPECT().GetBlocksWithParentAndType("test-board-id", "parent_id", "view").Return([]model.Block{{}}, nil).Times(2)
|
||||
th.Store.EXPECT().GetBlocksWithParentAndType("test-board-id", "parent_id", "view").Return([]*model.Block{{}}, nil).Times(2)
|
||||
|
||||
_, err := th.App.InsertBlocks([]model.Block{view1, view2}, "user-id-1")
|
||||
_, err := th.App.InsertBlocks([]*model.Block{view1, view2}, "user-id-1")
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ func (a *App) getBoardDescendantModifiedInfo(boardID string, latest bool) (int64
|
||||
}
|
||||
if len(blocks) > 0 {
|
||||
// Compare the board history info with the descendant block info, if it exists
|
||||
block := &blocks[0]
|
||||
block := blocks[0]
|
||||
if latest && block.UpdateAt > timestamp {
|
||||
timestamp = block.UpdateAt
|
||||
modifiedBy = block.ModifiedBy
|
||||
@ -228,7 +228,7 @@ func (a *App) DuplicateBoard(boardID, userID, toTeam string, asTemplate bool) (*
|
||||
for _, block := range bab.Blocks {
|
||||
blk := block
|
||||
a.wsAdapter.BroadcastBlockChange(teamID, blk)
|
||||
a.notifyBlockChanged(notify.Add, &blk, nil, userID)
|
||||
a.notifyBlockChanged(notify.Add, blk, nil, userID)
|
||||
}
|
||||
for _, member := range members {
|
||||
a.wsAdapter.BroadcastMemberChange(teamID, member.BoardID, member)
|
||||
|
@ -35,7 +35,7 @@ func (a *App) CreateBoardsAndBlocks(bab *model.BoardsAndBlocks, userID string, a
|
||||
a.wsAdapter.BroadcastBlockChange(teamID, b)
|
||||
a.metrics.IncrementBlocksInserted(1)
|
||||
a.webhook.NotifyUpdate(b)
|
||||
a.notifyBlockChanged(notify.Add, &b, nil, userID)
|
||||
a.notifyBlockChanged(notify.Add, b, nil, userID)
|
||||
}
|
||||
|
||||
if addMember {
|
||||
@ -74,7 +74,7 @@ func (a *App) PatchBoardsAndBlocks(pbab *model.PatchBoardsAndBlocks, userID stri
|
||||
}
|
||||
}
|
||||
|
||||
oldBlocksMap := map[string]model.Block{}
|
||||
oldBlocksMap := map[string]*model.Block{}
|
||||
for _, block := range oldBlocks {
|
||||
oldBlocksMap[block.ID] = block
|
||||
}
|
||||
@ -98,7 +98,7 @@ func (a *App) PatchBoardsAndBlocks(pbab *model.PatchBoardsAndBlocks, userID stri
|
||||
a.metrics.IncrementBlocksPatched(1)
|
||||
a.wsAdapter.BroadcastBlockChange(teamID, b)
|
||||
a.webhook.NotifyUpdate(b)
|
||||
a.notifyBlockChanged(notify.Update, &b, &oldBlock, userID)
|
||||
a.notifyBlockChanged(notify.Update, b, oldBlock, userID)
|
||||
}
|
||||
|
||||
for _, board := range bab.Boards {
|
||||
|
@ -24,12 +24,12 @@ func (a *App) CreateCard(card *model.Card, boardID string, userID string, disabl
|
||||
|
||||
block := model.Card2Block(card)
|
||||
|
||||
newBlocks, err := a.InsertBlocksAndNotify([]model.Block{*block}, userID, disableNotify)
|
||||
newBlocks, err := a.InsertBlocksAndNotify([]*model.Block{block}, userID, disableNotify)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create card: %w", err)
|
||||
}
|
||||
|
||||
newCard, err := model.Block2Card(&newBlocks[0])
|
||||
newCard, err := model.Block2Card(newBlocks[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -53,7 +53,7 @@ func (a *App) GetCardsForBoard(boardID string, page int, perPage int) ([]*model.
|
||||
cards := make([]*model.Card, 0, len(blocks))
|
||||
for _, blk := range blocks {
|
||||
b := blk
|
||||
if card, err := model.Block2Card(&b); err != nil {
|
||||
if card, err := model.Block2Card(b); err != nil {
|
||||
return nil, fmt.Errorf("Block2Card fail: %w", err)
|
||||
} else {
|
||||
cards = append(cards, card)
|
||||
|
@ -69,9 +69,9 @@ func TestGetCards(t *testing.T) {
|
||||
const cardCount = 25
|
||||
|
||||
// make some cards
|
||||
blocks := make([]model.Block, 0, cardCount)
|
||||
blocks := make([]*model.Block, 0, cardCount)
|
||||
for i := 0; i < cardCount; i++ {
|
||||
card := model.Block{
|
||||
card := &model.Block{
|
||||
ID: utils.NewID(utils.IDTypeBlock),
|
||||
ParentID: board.ID,
|
||||
Schema: 1,
|
||||
|
@ -55,6 +55,10 @@ func (a *App) GetBoardsCloudLimits() (*model.BoardsCloudLimits, error) {
|
||||
return boardsCloudLimits, nil
|
||||
}
|
||||
|
||||
func (a *App) GetUsedCardsCount() (int, error) {
|
||||
return a.store.GetUsedCardsCount()
|
||||
}
|
||||
|
||||
// IsCloud returns true if the server is running as a plugin in a
|
||||
// cloud licensed server.
|
||||
func (a *App) IsCloud() bool {
|
||||
@ -123,7 +127,7 @@ func (a *App) UpdateCardLimitTimestamp() error {
|
||||
// getTemplateMapForBlocks gets all board ids for the blocks, and
|
||||
// builds a map with the board IDs as the key and their isTemplate
|
||||
// field as the value.
|
||||
func (a *App) getTemplateMapForBlocks(blocks []model.Block) (map[string]bool, error) {
|
||||
func (a *App) getTemplateMapForBlocks(blocks []*model.Block) (map[string]bool, error) {
|
||||
boardMap := map[string]*model.Board{}
|
||||
for _, block := range blocks {
|
||||
if _, ok := boardMap[block.BoardID]; !ok {
|
||||
@ -146,7 +150,7 @@ func (a *App) getTemplateMapForBlocks(blocks []model.Block) (map[string]bool, er
|
||||
// ApplyCloudLimits takes a set of blocks and, if the server is cloud
|
||||
// limited, limits those that are outside of the card limit and don't
|
||||
// belong to a template.
|
||||
func (a *App) ApplyCloudLimits(blocks []model.Block) ([]model.Block, error) {
|
||||
func (a *App) ApplyCloudLimits(blocks []*model.Block) ([]*model.Block, error) {
|
||||
// if there is no limit currently being applied, return
|
||||
if !a.IsCloudLimited() {
|
||||
return blocks, nil
|
||||
@ -162,7 +166,7 @@ func (a *App) ApplyCloudLimits(blocks []model.Block) ([]model.Block, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
limitedBlocks := make([]model.Block, len(blocks))
|
||||
limitedBlocks := make([]*model.Block, len(blocks))
|
||||
for i, block := range blocks {
|
||||
// if the block belongs to a template, it will never be
|
||||
// limited
|
||||
@ -183,7 +187,7 @@ func (a *App) ApplyCloudLimits(blocks []model.Block) ([]model.Block, error) {
|
||||
|
||||
// ContainsLimitedBlocks checks if a list of blocks contain any block
|
||||
// that references a limited card.
|
||||
func (a *App) ContainsLimitedBlocks(blocks []model.Block) (bool, error) {
|
||||
func (a *App) ContainsLimitedBlocks(blocks []*model.Block) (bool, error) {
|
||||
cardLimitTimestamp, err := a.store.GetCardLimitTimestamp()
|
||||
if err != nil {
|
||||
return false, err
|
||||
@ -193,7 +197,7 @@ func (a *App) ContainsLimitedBlocks(blocks []model.Block) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
cards := []model.Block{}
|
||||
cards := []*model.Block{}
|
||||
cardIDMap := map[string]bool{}
|
||||
for _, block := range blocks {
|
||||
switch block.Type {
|
||||
|
@ -231,7 +231,7 @@ func TestGetTemplateMapForBlocks(t *testing.T) {
|
||||
IsTemplate: false,
|
||||
}
|
||||
|
||||
blocks := []model.Block{
|
||||
blocks := []*model.Block{
|
||||
{
|
||||
ID: "card1",
|
||||
Type: model.TypeCard,
|
||||
@ -274,7 +274,7 @@ func TestGetTemplateMapForBlocks(t *testing.T) {
|
||||
th, tearDown := SetupTestHelper(t)
|
||||
defer tearDown()
|
||||
|
||||
blocks := []model.Block{
|
||||
blocks := []*model.Block{
|
||||
{
|
||||
ID: "card1",
|
||||
Type: model.TypeCard,
|
||||
@ -317,7 +317,7 @@ func TestApplyCloudLimits(t *testing.T) {
|
||||
IsTemplate: true,
|
||||
}
|
||||
|
||||
blocks := []model.Block{
|
||||
blocks := []*model.Block{
|
||||
{
|
||||
ID: "card1",
|
||||
Type: model.TypeCard,
|
||||
@ -360,14 +360,14 @@ func TestApplyCloudLimits(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("if the server is limited, it should limit the blocks that are beyond the card limit timestamp", func(t *testing.T) {
|
||||
findBlock := func(blocks []model.Block, id string) model.Block {
|
||||
findBlock := func(blocks []*model.Block, id string) *model.Block {
|
||||
for _, block := range blocks {
|
||||
if block.ID == id {
|
||||
return block
|
||||
}
|
||||
}
|
||||
require.FailNow(t, "block %s not found", id)
|
||||
return model.Block{} // this should never be reached
|
||||
return &model.Block{} // this should never be reached
|
||||
}
|
||||
|
||||
th, tearDown := SetupTestHelper(t)
|
||||
@ -404,7 +404,7 @@ func TestContainsLimitedBlocks(t *testing.T) {
|
||||
th, tearDown := SetupTestHelper(t)
|
||||
defer tearDown()
|
||||
|
||||
blocks := []model.Block{
|
||||
blocks := []*model.Block{
|
||||
{
|
||||
ID: "card1",
|
||||
Type: model.TypeCard,
|
||||
@ -425,7 +425,7 @@ func TestContainsLimitedBlocks(t *testing.T) {
|
||||
th, tearDown := SetupTestHelper(t)
|
||||
defer tearDown()
|
||||
|
||||
blocks := []model.Block{
|
||||
blocks := []*model.Block{
|
||||
{
|
||||
ID: "card1",
|
||||
Type: model.TypeCard,
|
||||
@ -454,7 +454,7 @@ func TestContainsLimitedBlocks(t *testing.T) {
|
||||
th, tearDown := SetupTestHelper(t)
|
||||
defer tearDown()
|
||||
|
||||
blocks := []model.Block{
|
||||
blocks := []*model.Block{
|
||||
{
|
||||
ID: "card1",
|
||||
Type: model.TypeCard,
|
||||
@ -484,7 +484,7 @@ func TestContainsLimitedBlocks(t *testing.T) {
|
||||
th, tearDown := SetupTestHelper(t)
|
||||
defer tearDown()
|
||||
|
||||
blocks := []model.Block{
|
||||
blocks := []*model.Block{
|
||||
{
|
||||
ID: "text1",
|
||||
Type: model.TypeText,
|
||||
@ -494,7 +494,7 @@ func TestContainsLimitedBlocks(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
card1 := model.Block{
|
||||
card1 := &model.Block{
|
||||
ID: "card1",
|
||||
Type: model.TypeCard,
|
||||
ParentID: "board1",
|
||||
@ -510,7 +510,7 @@ func TestContainsLimitedBlocks(t *testing.T) {
|
||||
th.App.SetCardLimit(500)
|
||||
cardLimitTimestamp := int64(150)
|
||||
th.Store.EXPECT().GetCardLimitTimestamp().Return(cardLimitTimestamp, nil)
|
||||
th.Store.EXPECT().GetBlocksByIDs([]string{"card1"}).Return([]model.Block{card1}, nil)
|
||||
th.Store.EXPECT().GetBlocksByIDs([]string{"card1"}).Return([]*model.Block{card1}, nil)
|
||||
th.Store.EXPECT().GetBoard("board1").Return(board1, nil)
|
||||
|
||||
containsLimitedBlocks, err := th.App.ContainsLimitedBlocks(blocks)
|
||||
@ -522,7 +522,7 @@ func TestContainsLimitedBlocks(t *testing.T) {
|
||||
th, tearDown := SetupTestHelper(t)
|
||||
defer tearDown()
|
||||
|
||||
blocks := []model.Block{
|
||||
blocks := []*model.Block{
|
||||
{
|
||||
ID: "text1",
|
||||
Type: model.TypeText,
|
||||
@ -532,7 +532,7 @@ func TestContainsLimitedBlocks(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
card1 := model.Block{
|
||||
card1 := &model.Block{
|
||||
ID: "card1",
|
||||
Type: model.TypeCard,
|
||||
ParentID: "board1",
|
||||
@ -548,7 +548,7 @@ func TestContainsLimitedBlocks(t *testing.T) {
|
||||
th.App.SetCardLimit(500)
|
||||
cardLimitTimestamp := int64(150)
|
||||
th.Store.EXPECT().GetCardLimitTimestamp().Return(cardLimitTimestamp, nil)
|
||||
th.Store.EXPECT().GetBlocksByIDs([]string{"card1"}).Return([]model.Block{card1}, nil)
|
||||
th.Store.EXPECT().GetBlocksByIDs([]string{"card1"}).Return([]*model.Block{card1}, nil)
|
||||
th.Store.EXPECT().GetBoard("board1").Return(board1, nil)
|
||||
|
||||
containsLimitedBlocks, err := th.App.ContainsLimitedBlocks(blocks)
|
||||
@ -560,7 +560,7 @@ func TestContainsLimitedBlocks(t *testing.T) {
|
||||
th, tearDown := SetupTestHelper(t)
|
||||
defer tearDown()
|
||||
|
||||
blocks := []model.Block{
|
||||
blocks := []*model.Block{
|
||||
// a content block that references a card that needs
|
||||
// fetching
|
||||
{
|
||||
@ -598,7 +598,7 @@ func TestContainsLimitedBlocks(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
card1 := model.Block{
|
||||
card1 := &model.Block{
|
||||
ID: "card1",
|
||||
Type: model.TypeCard,
|
||||
ParentID: "board1",
|
||||
@ -606,7 +606,7 @@ func TestContainsLimitedBlocks(t *testing.T) {
|
||||
UpdateAt: 200,
|
||||
}
|
||||
|
||||
card3 := model.Block{
|
||||
card3 := &model.Block{
|
||||
ID: "card3",
|
||||
Type: model.TypeCard,
|
||||
ParentID: "board3",
|
||||
@ -633,7 +633,7 @@ func TestContainsLimitedBlocks(t *testing.T) {
|
||||
th.App.SetCardLimit(500)
|
||||
cardLimitTimestamp := int64(150)
|
||||
th.Store.EXPECT().GetCardLimitTimestamp().Return(cardLimitTimestamp, nil)
|
||||
th.Store.EXPECT().GetBlocksByIDs(gomock.InAnyOrder([]string{"card1", "card3"})).Return([]model.Block{card1, card3}, nil)
|
||||
th.Store.EXPECT().GetBlocksByIDs(gomock.InAnyOrder([]string{"card1", "card3"})).Return([]*model.Block{card1, card3}, nil)
|
||||
th.Store.EXPECT().GetBoard("board1").Return(board1, nil)
|
||||
th.Store.EXPECT().GetBoard("board2").Return(board2, nil)
|
||||
th.Store.EXPECT().GetBoard("board3").Return(board3, nil)
|
||||
|
@ -110,7 +110,7 @@ func (a *App) writeArchiveBoard(zw *zip.Writer, board model.Board, opt model.Exp
|
||||
}
|
||||
|
||||
// writeArchiveBlockLine writes a single block to the archive.
|
||||
func (a *App) writeArchiveBlockLine(w io.Writer, block model.Block) error {
|
||||
func (a *App) writeArchiveBlockLine(w io.Writer, block *model.Block) error {
|
||||
b, err := json.Marshal(&block)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -199,7 +199,7 @@ func (a *App) getBoardsForArchive(boardIDs []string) ([]model.Board, error) {
|
||||
return boards, nil
|
||||
}
|
||||
|
||||
func extractImageFilename(imageBlock model.Block) (string, error) {
|
||||
func extractImageFilename(imageBlock *model.Block) (string, error) {
|
||||
f, ok := imageBlock.Fields["fileId"]
|
||||
if !ok {
|
||||
return "", model.ErrInvalidImageBlock
|
||||
|
@ -126,7 +126,7 @@ func (a *App) ImportBoardJSONL(r io.Reader, opt model.ImportArchiveOptions) (str
|
||||
// TODO: Stream this once `model.GenerateBlockIDs` can take a stream of blocks.
|
||||
// We don't want to load the whole file in memory, even though it's a single board.
|
||||
boardsAndBlocks := &model.BoardsAndBlocks{
|
||||
Blocks: make([]model.Block, 0, 10),
|
||||
Blocks: make([]*model.Block, 0, 10),
|
||||
Boards: make([]*model.Board, 0, 10),
|
||||
}
|
||||
lineReader := bufio.NewReader(r)
|
||||
@ -175,20 +175,20 @@ func (a *App) ImportBoardJSONL(r io.Reader, opt model.ImportArchiveOptions) (str
|
||||
boardID = board.ID
|
||||
case "board_block":
|
||||
// legacy archives encoded boards as blocks; we need to convert them to real boards.
|
||||
var block model.Block
|
||||
var block *model.Block
|
||||
if err2 := json.Unmarshal(archiveLine.Data, &block); err2 != nil {
|
||||
return "", fmt.Errorf("invalid board block in archive line %d: %w", lineNum, err2)
|
||||
}
|
||||
block.ModifiedBy = userID
|
||||
block.UpdateAt = now
|
||||
board, err := a.blockToBoard(&block, opt)
|
||||
board, err := a.blockToBoard(block, opt)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot convert archive line %d to block: %w", lineNum, err)
|
||||
}
|
||||
boardsAndBlocks.Boards = append(boardsAndBlocks.Boards, board)
|
||||
boardID = board.ID
|
||||
case "block":
|
||||
var block model.Block
|
||||
var block *model.Block
|
||||
if err2 := json.Unmarshal(archiveLine.Data, &block); err2 != nil {
|
||||
return "", fmt.Errorf("invalid block in archive line %d: %w", lineNum, err2)
|
||||
}
|
||||
@ -253,7 +253,7 @@ func (a *App) fixBoardsandBlocks(boardsAndBlocks *model.BoardsAndBlocks, opt mod
|
||||
|
||||
modInfoCache := make(map[string]interface{})
|
||||
modBoards := make([]*model.Board, 0, len(boardsAndBlocks.Boards))
|
||||
modBlocks := make([]model.Block, 0, len(boardsAndBlocks.Blocks))
|
||||
modBlocks := make([]*model.Block, 0, len(boardsAndBlocks.Blocks))
|
||||
|
||||
for _, board := range boardsAndBlocks.Boards {
|
||||
b := *board
|
||||
@ -268,7 +268,7 @@ func (a *App) fixBoardsandBlocks(boardsAndBlocks *model.BoardsAndBlocks, opt mod
|
||||
|
||||
for _, block := range boardsAndBlocks.Blocks {
|
||||
b := block
|
||||
if opt.BlockModifier != nil && !opt.BlockModifier(&b, modInfoCache) {
|
||||
if opt.BlockModifier != nil && !opt.BlockModifier(b, modInfoCache) {
|
||||
a.logger.Debug("skipping insert block per block modifier",
|
||||
mlog.String("blockID", block.ID),
|
||||
)
|
||||
|
@ -19,7 +19,7 @@ func TestApp_ImportArchive(t *testing.T) {
|
||||
Title: "Cross-Functional Project Plan",
|
||||
}
|
||||
|
||||
block := model.Block{
|
||||
block := &model.Block{
|
||||
ID: "2c1873e0-1484-407d-8b2c-3c3b5a2a9f9e",
|
||||
ParentID: board.ID,
|
||||
Type: model.TypeView,
|
||||
@ -28,7 +28,7 @@ func TestApp_ImportArchive(t *testing.T) {
|
||||
|
||||
babs := &model.BoardsAndBlocks{
|
||||
Boards: []*model.Board{board},
|
||||
Blocks: []model.Block{block},
|
||||
Blocks: []*model.Block{block},
|
||||
}
|
||||
|
||||
boardMember := &model.BoardMember{
|
||||
|
@ -21,7 +21,7 @@ func TestApp_initializeTemplates(t *testing.T) {
|
||||
TemplateVersion: defaultTemplateVersion,
|
||||
}
|
||||
|
||||
block := model.Block{
|
||||
block := &model.Block{
|
||||
ID: utils.NewID(utils.IDTypeBlock),
|
||||
ParentID: board.ID,
|
||||
BoardID: board.ID,
|
||||
@ -31,7 +31,7 @@ func TestApp_initializeTemplates(t *testing.T) {
|
||||
|
||||
boardsAndBlocks := &model.BoardsAndBlocks{
|
||||
Boards: []*model.Board{board},
|
||||
Blocks: []model.Block{block},
|
||||
Blocks: []*model.Block{block},
|
||||
}
|
||||
|
||||
boardMember := &model.BoardMember{
|
||||
|
@ -5,7 +5,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"strings"
|
||||
@ -56,7 +55,7 @@ func BuildErrorResponse(r *http.Response, err error) *Response {
|
||||
|
||||
func closeBody(r *http.Response) {
|
||||
if r.Body != nil {
|
||||
_, _ = io.Copy(ioutil.Discard, r.Body)
|
||||
_, _ = io.Copy(io.Discard, r.Body)
|
||||
_ = r.Body.Close()
|
||||
}
|
||||
}
|
||||
@ -244,7 +243,7 @@ func (c *Client) GetUserBoardsInsights(teamID string, userID string, timeRange s
|
||||
return boardInsightsList, BuildResponse(r)
|
||||
}
|
||||
|
||||
func (c *Client) GetBlocksForBoard(boardID string) ([]model.Block, *Response) {
|
||||
func (c *Client) GetBlocksForBoard(boardID string) ([]*model.Block, *Response) {
|
||||
r, err := c.DoAPIGet(c.GetBlocksRoute(boardID), "")
|
||||
if err != nil {
|
||||
return nil, BuildErrorResponse(r, err)
|
||||
@ -254,7 +253,7 @@ func (c *Client) GetBlocksForBoard(boardID string) ([]model.Block, *Response) {
|
||||
return model.BlocksFromJSON(r.Body), BuildResponse(r)
|
||||
}
|
||||
|
||||
func (c *Client) GetAllBlocksForBoard(boardID string) ([]model.Block, *Response) {
|
||||
func (c *Client) GetAllBlocksForBoard(boardID string) ([]*model.Block, *Response) {
|
||||
r, err := c.DoAPIGet(c.GetAllBlocksRoute(boardID), "")
|
||||
if err != nil {
|
||||
return nil, BuildErrorResponse(r, err)
|
||||
@ -321,7 +320,7 @@ func (c *Client) UndeleteBlock(boardID, blockID string) (bool, *Response) {
|
||||
return true, BuildResponse(r)
|
||||
}
|
||||
|
||||
func (c *Client) InsertBlocks(boardID string, blocks []model.Block, disableNotify bool) ([]model.Block, *Response) {
|
||||
func (c *Client) InsertBlocks(boardID string, blocks []*model.Block, disableNotify bool) ([]*model.Block, *Response) {
|
||||
var queryParams string
|
||||
if disableNotify {
|
||||
queryParams = "?" + disableNotifyQueryParam
|
||||
@ -903,3 +902,19 @@ func (c *Client) GetLimits() (*model.BoardsCloudLimits, *Response) {
|
||||
|
||||
return limits, BuildResponse(r)
|
||||
}
|
||||
|
||||
func (c *Client) GetStatistics() (*model.BoardsStatistics, *Response) {
|
||||
r, err := c.DoAPIGet("/statistics", "")
|
||||
if err != nil {
|
||||
return nil, BuildErrorResponse(r, err)
|
||||
}
|
||||
defer closeBody(r)
|
||||
|
||||
var stats *model.BoardsStatistics
|
||||
err = json.NewDecoder(r.Body).Decode(&stats)
|
||||
if err != nil {
|
||||
return nil, BuildErrorResponse(r, err)
|
||||
}
|
||||
|
||||
return stats, BuildResponse(r)
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ func TestGetBlocks(t *testing.T) {
|
||||
|
||||
initialID1 := utils.NewID(utils.IDTypeBlock)
|
||||
initialID2 := utils.NewID(utils.IDTypeBlock)
|
||||
newBlocks := []model.Block{
|
||||
newBlocks := []*model.Block{
|
||||
{
|
||||
ID: initialID1,
|
||||
BoardID: board.ID,
|
||||
@ -64,7 +64,7 @@ func TestPostBlock(t *testing.T) {
|
||||
|
||||
t.Run("Create a single block", func(t *testing.T) {
|
||||
initialID1 := utils.NewID(utils.IDTypeBlock)
|
||||
block := model.Block{
|
||||
block := &model.Block{
|
||||
ID: initialID1,
|
||||
BoardID: board.ID,
|
||||
CreateAt: 1,
|
||||
@ -73,7 +73,7 @@ func TestPostBlock(t *testing.T) {
|
||||
Title: "New title",
|
||||
}
|
||||
|
||||
newBlocks, resp := th.Client.InsertBlocks(board.ID, []model.Block{block}, false)
|
||||
newBlocks, resp := th.Client.InsertBlocks(board.ID, []*model.Block{block}, false)
|
||||
require.NoError(t, resp.Error)
|
||||
require.Len(t, newBlocks, 1)
|
||||
blockID1 = newBlocks[0].ID
|
||||
@ -92,7 +92,7 @@ func TestPostBlock(t *testing.T) {
|
||||
t.Run("Create a couple of blocks in the same call", func(t *testing.T) {
|
||||
initialID2 := utils.NewID(utils.IDTypeBlock)
|
||||
initialID3 := utils.NewID(utils.IDTypeBlock)
|
||||
newBlocks := []model.Block{
|
||||
newBlocks := []*model.Block{
|
||||
{
|
||||
ID: initialID2,
|
||||
BoardID: board.ID,
|
||||
@ -131,7 +131,7 @@ func TestPostBlock(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("Update a block should not be possible through the insert endpoint", func(t *testing.T) {
|
||||
block := model.Block{
|
||||
block := &model.Block{
|
||||
ID: blockID1,
|
||||
BoardID: board.ID,
|
||||
CreateAt: 1,
|
||||
@ -140,7 +140,7 @@ func TestPostBlock(t *testing.T) {
|
||||
Title: "Updated title",
|
||||
}
|
||||
|
||||
newBlocks, resp := th.Client.InsertBlocks(board.ID, []model.Block{block}, false)
|
||||
newBlocks, resp := th.Client.InsertBlocks(board.ID, []*model.Block{block}, false)
|
||||
require.NoError(t, resp.Error)
|
||||
require.Len(t, newBlocks, 1)
|
||||
blockID4 := newBlocks[0].ID
|
||||
@ -150,7 +150,7 @@ func TestPostBlock(t *testing.T) {
|
||||
require.NoError(t, resp.Error)
|
||||
require.Len(t, blocks, 4)
|
||||
|
||||
var block4 model.Block
|
||||
var block4 *model.Block
|
||||
for _, b := range blocks {
|
||||
if b.ID == blockID4 {
|
||||
block4 = b
|
||||
@ -170,7 +170,7 @@ func TestPatchBlock(t *testing.T) {
|
||||
board := th.CreateBoard("team-id", model.BoardTypeOpen)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
block := model.Block{
|
||||
block := &model.Block{
|
||||
ID: initialID,
|
||||
BoardID: board.ID,
|
||||
CreateAt: 1,
|
||||
@ -180,7 +180,7 @@ func TestPatchBlock(t *testing.T) {
|
||||
Fields: map[string]interface{}{"test": "test value", "test2": "test value 2"},
|
||||
}
|
||||
|
||||
newBlocks, resp := th.Client.InsertBlocks(board.ID, []model.Block{block}, false)
|
||||
newBlocks, resp := th.Client.InsertBlocks(board.ID, []*model.Block{block}, false)
|
||||
th.CheckOK(resp)
|
||||
require.Len(t, newBlocks, 1)
|
||||
blockID := newBlocks[0].ID
|
||||
@ -198,7 +198,7 @@ func TestPatchBlock(t *testing.T) {
|
||||
require.NoError(t, resp.Error)
|
||||
require.Len(t, blocks, 1)
|
||||
|
||||
var updatedBlock model.Block
|
||||
var updatedBlock *model.Block
|
||||
for _, b := range blocks {
|
||||
if b.ID == blockID {
|
||||
updatedBlock = b
|
||||
@ -223,7 +223,7 @@ func TestPatchBlock(t *testing.T) {
|
||||
require.NoError(t, resp.Error)
|
||||
require.Len(t, blocks, 1)
|
||||
|
||||
var updatedBlock model.Block
|
||||
var updatedBlock *model.Block
|
||||
for _, b := range blocks {
|
||||
if b.ID == blockID {
|
||||
updatedBlock = b
|
||||
@ -246,7 +246,7 @@ func TestPatchBlock(t *testing.T) {
|
||||
require.NoError(t, resp.Error)
|
||||
require.Len(t, blocks, 1)
|
||||
|
||||
var updatedBlock model.Block
|
||||
var updatedBlock *model.Block
|
||||
for _, b := range blocks {
|
||||
if b.ID == blockID {
|
||||
updatedBlock = b
|
||||
@ -269,7 +269,7 @@ func TestDeleteBlock(t *testing.T) {
|
||||
var blockID string
|
||||
t.Run("Create a block", func(t *testing.T) {
|
||||
initialID := utils.NewID(utils.IDTypeBlock)
|
||||
block := model.Block{
|
||||
block := &model.Block{
|
||||
ID: initialID,
|
||||
BoardID: board.ID,
|
||||
CreateAt: 1,
|
||||
@ -278,7 +278,7 @@ func TestDeleteBlock(t *testing.T) {
|
||||
Title: "New title",
|
||||
}
|
||||
|
||||
newBlocks, resp := th.Client.InsertBlocks(board.ID, []model.Block{block}, false)
|
||||
newBlocks, resp := th.Client.InsertBlocks(board.ID, []*model.Block{block}, false)
|
||||
require.NoError(t, resp.Error)
|
||||
require.Len(t, newBlocks, 1)
|
||||
require.NotZero(t, newBlocks[0].ID)
|
||||
@ -323,7 +323,7 @@ func TestUndeleteBlock(t *testing.T) {
|
||||
var blockID string
|
||||
t.Run("Create a block", func(t *testing.T) {
|
||||
initialID := utils.NewID(utils.IDTypeBoard)
|
||||
block := model.Block{
|
||||
block := &model.Block{
|
||||
ID: initialID,
|
||||
BoardID: board.ID,
|
||||
CreateAt: 1,
|
||||
@ -332,7 +332,7 @@ func TestUndeleteBlock(t *testing.T) {
|
||||
Title: "New title",
|
||||
}
|
||||
|
||||
newBlocks, resp := th.Client.InsertBlocks(board.ID, []model.Block{block}, false)
|
||||
newBlocks, resp := th.Client.InsertBlocks(board.ID, []*model.Block{block}, false)
|
||||
require.NoError(t, resp.Error)
|
||||
require.Len(t, newBlocks, 1)
|
||||
require.NotZero(t, newBlocks[0].ID)
|
||||
|
@ -424,7 +424,7 @@ func TestGetAllBlocksForBoard(t *testing.T) {
|
||||
childBlockID2 := utils.NewID(utils.IDTypeBlock)
|
||||
|
||||
t.Run("Create the block structure", func(t *testing.T) {
|
||||
newBlocks := []model.Block{
|
||||
newBlocks := []*model.Block{
|
||||
{
|
||||
ID: parentBlockID,
|
||||
BoardID: board.ID,
|
||||
@ -739,7 +739,7 @@ func TestGetBoardMetadata(t *testing.T) {
|
||||
require.Equal(t, rBoard.ModifiedBy, boardMetadata.LastModifiedBy)
|
||||
|
||||
// Insert card1
|
||||
card1 := model.Block{
|
||||
card1 := &model.Block{
|
||||
ID: "card1",
|
||||
BoardID: rBoard.ID,
|
||||
Title: "Card 1",
|
||||
@ -760,7 +760,7 @@ func TestGetBoardMetadata(t *testing.T) {
|
||||
require.Equal(t, rCard1.ModifiedBy, boardMetadata.LastModifiedBy)
|
||||
|
||||
// Insert card2
|
||||
card2 := model.Block{
|
||||
card2 := &model.Block{
|
||||
ID: "card2",
|
||||
BoardID: rBoard.ID,
|
||||
Title: "Card 2",
|
||||
@ -1946,7 +1946,7 @@ func TestDuplicateBoard(t *testing.T) {
|
||||
require.Equal(t, me.ID, board.CreatedBy)
|
||||
require.Equal(t, me.ID, board.ModifiedBy)
|
||||
|
||||
newBlocks := []model.Block{
|
||||
newBlocks := []*model.Block{
|
||||
{
|
||||
ID: utils.NewID(utils.IDTypeBlock),
|
||||
BoardID: board.ID,
|
||||
@ -2031,7 +2031,7 @@ func TestDuplicateBoard(t *testing.T) {
|
||||
th.CheckOK(resp)
|
||||
require.NoError(t, resp.Error)
|
||||
|
||||
newBlocks := []model.Block{
|
||||
newBlocks := []*model.Block{
|
||||
{
|
||||
ID: utils.NewID(utils.IDTypeBlock),
|
||||
BoardID: board.ID,
|
||||
|
@ -17,7 +17,7 @@ func TestCreateBoardsAndBlocks(t *testing.T) {
|
||||
|
||||
newBab := &model.BoardsAndBlocks{
|
||||
Boards: []*model.Board{},
|
||||
Blocks: []model.Block{},
|
||||
Blocks: []*model.Block{},
|
||||
}
|
||||
|
||||
bab, resp := th.Client.CreateBoardsAndBlocks(newBab)
|
||||
@ -32,7 +32,7 @@ func TestCreateBoardsAndBlocks(t *testing.T) {
|
||||
t.Run("no boards", func(t *testing.T) {
|
||||
newBab := &model.BoardsAndBlocks{
|
||||
Boards: []*model.Board{},
|
||||
Blocks: []model.Block{
|
||||
Blocks: []*model.Block{
|
||||
{ID: "block-id", BoardID: "board-id", Type: model.TypeCard},
|
||||
},
|
||||
}
|
||||
@ -47,7 +47,7 @@ func TestCreateBoardsAndBlocks(t *testing.T) {
|
||||
Boards: []*model.Board{
|
||||
{ID: "board-id", TeamID: teamID, Type: model.BoardTypePrivate},
|
||||
},
|
||||
Blocks: []model.Block{},
|
||||
Blocks: []*model.Block{},
|
||||
}
|
||||
|
||||
bab, resp := th.Client.CreateBoardsAndBlocks(newBab)
|
||||
@ -60,7 +60,7 @@ func TestCreateBoardsAndBlocks(t *testing.T) {
|
||||
Boards: []*model.Board{
|
||||
{ID: "board-id", TeamID: teamID, Type: model.BoardTypePrivate},
|
||||
},
|
||||
Blocks: []model.Block{
|
||||
Blocks: []*model.Block{
|
||||
{ID: "block-id", BoardID: "nonexistent-board-id", Type: model.TypeCard, CreateAt: 1, UpdateAt: 1},
|
||||
},
|
||||
}
|
||||
@ -76,7 +76,7 @@ func TestCreateBoardsAndBlocks(t *testing.T) {
|
||||
{ID: "board-id", TeamID: teamID, Type: model.BoardTypePrivate},
|
||||
{TeamID: teamID, Type: model.BoardTypePrivate},
|
||||
},
|
||||
Blocks: []model.Block{
|
||||
Blocks: []*model.Block{
|
||||
{ID: "block-id", BoardID: "board-id", Type: model.TypeCard, CreateAt: 1, UpdateAt: 1},
|
||||
},
|
||||
}
|
||||
@ -92,7 +92,7 @@ func TestCreateBoardsAndBlocks(t *testing.T) {
|
||||
{ID: "board-id-1", TeamID: "team-id-1", Type: model.BoardTypePrivate},
|
||||
{ID: "board-id-2", TeamID: "team-id-2", Type: model.BoardTypePrivate},
|
||||
},
|
||||
Blocks: []model.Block{
|
||||
Blocks: []*model.Block{
|
||||
{ID: "block-id", BoardID: "board-id-1", Type: model.TypeCard, CreateAt: 1, UpdateAt: 1},
|
||||
},
|
||||
}
|
||||
@ -108,7 +108,7 @@ func TestCreateBoardsAndBlocks(t *testing.T) {
|
||||
{ID: "board-id-1", Title: "public board", TeamID: teamID, Type: model.BoardTypeOpen},
|
||||
{ID: "board-id-2", Title: "private board", TeamID: teamID, Type: model.BoardTypePrivate},
|
||||
},
|
||||
Blocks: []model.Block{
|
||||
Blocks: []*model.Block{
|
||||
{ID: "block-id-1", Title: "block 1", BoardID: "board-id-1", Type: model.TypeCard, CreateAt: 1, UpdateAt: 1},
|
||||
{ID: "block-id-2", Title: "block 2", BoardID: "board-id-2", Type: model.TypeCard, CreateAt: 1, UpdateAt: 1},
|
||||
},
|
||||
@ -203,7 +203,7 @@ func TestPatchBoardsAndBlocks(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, board2)
|
||||
|
||||
newBlock1 := model.Block{
|
||||
newBlock1 := &model.Block{
|
||||
ID: "block-id-1",
|
||||
BoardID: board1.ID,
|
||||
Title: initialTitle,
|
||||
@ -213,7 +213,7 @@ func TestPatchBoardsAndBlocks(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, block1)
|
||||
|
||||
newBlock2 := model.Block{
|
||||
newBlock2 := &model.Block{
|
||||
ID: "block-id-2",
|
||||
BoardID: board2.ID,
|
||||
Title: initialTitle,
|
||||
@ -342,7 +342,7 @@ func TestPatchBoardsAndBlocks(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, board2)
|
||||
|
||||
newBlock1 := model.Block{
|
||||
newBlock1 := &model.Block{
|
||||
ID: "block-id-1",
|
||||
BoardID: board1.ID,
|
||||
Title: initialTitle,
|
||||
@ -352,7 +352,7 @@ func TestPatchBoardsAndBlocks(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, block1)
|
||||
|
||||
newBlock2 := model.Block{
|
||||
newBlock2 := &model.Block{
|
||||
ID: "block-id-2",
|
||||
BoardID: board2.ID,
|
||||
Title: initialTitle,
|
||||
@ -406,7 +406,7 @@ func TestPatchBoardsAndBlocks(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, board2)
|
||||
|
||||
newBlock1 := model.Block{
|
||||
newBlock1 := &model.Block{
|
||||
ID: "block-id-1",
|
||||
BoardID: board1.ID,
|
||||
Title: initialTitle,
|
||||
@ -416,7 +416,7 @@ func TestPatchBoardsAndBlocks(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, block1)
|
||||
|
||||
newBlock2 := model.Block{
|
||||
newBlock2 := &model.Block{
|
||||
ID: "block-id-2",
|
||||
BoardID: board2.ID,
|
||||
Title: initialTitle,
|
||||
@ -470,7 +470,7 @@ func TestPatchBoardsAndBlocks(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, board2)
|
||||
|
||||
newBlock1 := model.Block{
|
||||
newBlock1 := &model.Block{
|
||||
ID: "block-id-1",
|
||||
BoardID: board1.ID,
|
||||
Title: initialTitle,
|
||||
@ -480,7 +480,7 @@ func TestPatchBoardsAndBlocks(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, block1)
|
||||
|
||||
newBlock2 := model.Block{
|
||||
newBlock2 := &model.Block{
|
||||
ID: "block-id-2",
|
||||
BoardID: board2.ID,
|
||||
Title: initialTitle,
|
||||
@ -537,7 +537,7 @@ func TestPatchBoardsAndBlocks(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, board2)
|
||||
|
||||
newBlock1 := model.Block{
|
||||
newBlock1 := &model.Block{
|
||||
ID: "block-id-1",
|
||||
BoardID: board1.ID,
|
||||
Title: initialTitle,
|
||||
@ -547,7 +547,7 @@ func TestPatchBoardsAndBlocks(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, block1)
|
||||
|
||||
newBlock2 := model.Block{
|
||||
newBlock2 := &model.Block{
|
||||
ID: "block-id-2",
|
||||
BoardID: board2.ID,
|
||||
Title: initialTitle,
|
||||
@ -600,7 +600,7 @@ func TestPatchBoardsAndBlocks(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, board2)
|
||||
|
||||
newBlock1 := model.Block{
|
||||
newBlock1 := &model.Block{
|
||||
ID: "block-id-1",
|
||||
BoardID: board1.ID,
|
||||
Title: initialTitle,
|
||||
@ -610,7 +610,7 @@ func TestPatchBoardsAndBlocks(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, block1)
|
||||
|
||||
newBlock2 := model.Block{
|
||||
newBlock2 := &model.Block{
|
||||
ID: "block-id-2",
|
||||
BoardID: board2.ID,
|
||||
Title: initialTitle,
|
||||
@ -683,7 +683,7 @@ func TestDeleteBoardsAndBlocks(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, board)
|
||||
|
||||
newBlock := model.Block{
|
||||
newBlock := &model.Block{
|
||||
ID: "block-id-1",
|
||||
BoardID: board.ID,
|
||||
Title: "title",
|
||||
@ -762,7 +762,7 @@ func TestDeleteBoardsAndBlocks(t *testing.T) {
|
||||
{ID: "board-id-1", Title: "public board", TeamID: teamID, Type: model.BoardTypeOpen},
|
||||
{ID: "board-id-2", Title: "private board", TeamID: teamID, Type: model.BoardTypePrivate},
|
||||
},
|
||||
Blocks: []model.Block{
|
||||
Blocks: []*model.Block{
|
||||
{ID: "block-id-1", Title: "block 1", BoardID: "board-id-1", Type: model.TypeCard, CreateAt: 1, UpdateAt: 1},
|
||||
{ID: "block-id-2", Title: "block 2", BoardID: "board-id-2", Type: model.TypeCard, CreateAt: 1, UpdateAt: 1},
|
||||
},
|
||||
|
@ -88,10 +88,10 @@ func TestGetCards(t *testing.T) {
|
||||
cardNew, resp := th.Client.CreateCard(board.ID, card, true)
|
||||
th.CheckOK(resp)
|
||||
|
||||
blocks := make([]model.Block, 0, 3)
|
||||
blocks := make([]*model.Block, 0, 3)
|
||||
for j := 0; j < 3; j++ {
|
||||
now := model.GetMillis()
|
||||
block := model.Block{
|
||||
block := &model.Block{
|
||||
ID: utils.NewID(utils.IDTypeBlock),
|
||||
ParentID: cardNew.ID,
|
||||
CreatedBy: userID,
|
||||
|
@ -72,6 +72,10 @@ type TestHelper struct {
|
||||
|
||||
type FakePermissionPluginAPI struct{}
|
||||
|
||||
func (*FakePermissionPluginAPI) HasPermissionTo(userID string, permission *mmModel.Permission) bool {
|
||||
return userID == userAdmin
|
||||
}
|
||||
|
||||
func (*FakePermissionPluginAPI) HasPermissionToTeam(userID string, teamID string, permission *mmModel.Permission) bool {
|
||||
if userID == userNoTeamMember {
|
||||
return false
|
||||
|
@ -24,7 +24,7 @@ func TestExportBoard(t *testing.T) {
|
||||
UpdateAt: utils.GetMillis(),
|
||||
}
|
||||
|
||||
block := model.Block{
|
||||
block := &model.Block{
|
||||
ID: utils.NewID(utils.IDTypeCard),
|
||||
ParentID: board.ID,
|
||||
Type: model.TypeCard,
|
||||
@ -37,7 +37,7 @@ func TestExportBoard(t *testing.T) {
|
||||
|
||||
babs := &model.BoardsAndBlocks{
|
||||
Boards: []*model.Board{board},
|
||||
Blocks: []model.Block{block},
|
||||
Blocks: []*model.Block{block},
|
||||
}
|
||||
|
||||
babs, resp := th.Client.CreateBoardsAndBlocks(babs)
|
||||
|
@ -139,19 +139,19 @@ func setupData(t *testing.T, th *TestHelper) TestData {
|
||||
true,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
err = th.Server.App().InsertBlock(model.Block{ID: "block-1", Title: "Test", Type: "card", BoardID: customTemplate1.ID}, userAdminID)
|
||||
err = th.Server.App().InsertBlock(&model.Block{ID: "block-1", Title: "Test", Type: "card", BoardID: customTemplate1.ID}, userAdminID)
|
||||
require.NoError(t, err)
|
||||
customTemplate2, err := th.Server.App().CreateBoard(
|
||||
&model.Board{Title: "Custom template 2", TeamID: "test-team", IsTemplate: true, Type: model.BoardTypePrivate, MinimumRole: "viewer"},
|
||||
userAdminID,
|
||||
true)
|
||||
require.NoError(t, err)
|
||||
err = th.Server.App().InsertBlock(model.Block{ID: "block-2", Title: "Test", Type: "card", BoardID: customTemplate2.ID}, userAdminID)
|
||||
err = th.Server.App().InsertBlock(&model.Block{ID: "block-2", Title: "Test", Type: "card", BoardID: customTemplate2.ID}, userAdminID)
|
||||
require.NoError(t, err)
|
||||
|
||||
board1, err := th.Server.App().CreateBoard(&model.Board{Title: "Board 1", TeamID: "test-team", Type: model.BoardTypeOpen, MinimumRole: "viewer"}, userAdminID, true)
|
||||
require.NoError(t, err)
|
||||
err = th.Server.App().InsertBlock(model.Block{ID: "block-3", Title: "Test", Type: "card", BoardID: board1.ID}, userAdminID)
|
||||
err = th.Server.App().InsertBlock(&model.Block{ID: "block-3", Title: "Test", Type: "card", BoardID: board1.ID}, userAdminID)
|
||||
require.NoError(t, err)
|
||||
board2, err := th.Server.App().CreateBoard(&model.Board{Title: "Board 2", TeamID: "test-team", Type: model.BoardTypePrivate, MinimumRole: "viewer"}, userAdminID, true)
|
||||
require.NoError(t, err)
|
||||
@ -167,7 +167,7 @@ func setupData(t *testing.T, th *TestHelper) TestData {
|
||||
require.Equal(t, boardMember.UserID, userAdminID)
|
||||
require.Equal(t, boardMember.BoardID, board2.ID)
|
||||
|
||||
err = th.Server.App().InsertBlock(model.Block{ID: "block-4", Title: "Test", Type: "card", BoardID: board2.ID}, userAdminID)
|
||||
err = th.Server.App().InsertBlock(&model.Block{ID: "block-4", Title: "Test", Type: "card", BoardID: board2.ID}, userAdminID)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = th.Server.App().UpsertSharing(model.Sharing{ID: board2.ID, Enabled: true, Token: "valid", ModifiedBy: userAdminID, UpdateAt: model.GetMillis()})
|
||||
@ -1322,13 +1322,13 @@ func TestPermissionsPatchBoardBlock(t *testing.T) {
|
||||
|
||||
func TestPermissionsDeleteBoardBlock(t *testing.T) {
|
||||
extraSetup := func(t *testing.T, th *TestHelper, testData TestData) {
|
||||
err := th.Server.App().InsertBlock(model.Block{ID: "block-5", Title: "Test", Type: "card", BoardID: testData.publicTemplate.ID}, userAdmin)
|
||||
err := th.Server.App().InsertBlock(&model.Block{ID: "block-5", Title: "Test", Type: "card", BoardID: testData.publicTemplate.ID}, userAdmin)
|
||||
require.NoError(t, err)
|
||||
err = th.Server.App().InsertBlock(model.Block{ID: "block-6", Title: "Test", Type: "card", BoardID: testData.privateTemplate.ID}, userAdmin)
|
||||
err = th.Server.App().InsertBlock(&model.Block{ID: "block-6", Title: "Test", Type: "card", BoardID: testData.privateTemplate.ID}, userAdmin)
|
||||
require.NoError(t, err)
|
||||
err = th.Server.App().InsertBlock(model.Block{ID: "block-7", Title: "Test", Type: "card", BoardID: testData.publicBoard.ID}, userAdmin)
|
||||
err = th.Server.App().InsertBlock(&model.Block{ID: "block-7", Title: "Test", Type: "card", BoardID: testData.publicBoard.ID}, userAdmin)
|
||||
require.NoError(t, err)
|
||||
err = th.Server.App().InsertBlock(model.Block{ID: "block-8", Title: "Test", Type: "card", BoardID: testData.privateBoard.ID}, userAdmin)
|
||||
err = th.Server.App().InsertBlock(&model.Block{ID: "block-8", Title: "Test", Type: "card", BoardID: testData.privateBoard.ID}, userAdmin)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
@ -1393,13 +1393,13 @@ func TestPermissionsDeleteBoardBlock(t *testing.T) {
|
||||
|
||||
func TestPermissionsUndeleteBoardBlock(t *testing.T) {
|
||||
extraSetup := func(t *testing.T, th *TestHelper, testData TestData) {
|
||||
err := th.Server.App().InsertBlock(model.Block{ID: "block-5", Title: "Test", Type: "card", BoardID: testData.publicTemplate.ID}, userAdmin)
|
||||
err := th.Server.App().InsertBlock(&model.Block{ID: "block-5", Title: "Test", Type: "card", BoardID: testData.publicTemplate.ID}, userAdmin)
|
||||
require.NoError(t, err)
|
||||
err = th.Server.App().InsertBlock(model.Block{ID: "block-6", Title: "Test", Type: "card", BoardID: testData.privateTemplate.ID}, userAdmin)
|
||||
err = th.Server.App().InsertBlock(&model.Block{ID: "block-6", Title: "Test", Type: "card", BoardID: testData.privateTemplate.ID}, userAdmin)
|
||||
require.NoError(t, err)
|
||||
err = th.Server.App().InsertBlock(model.Block{ID: "block-7", Title: "Test", Type: "card", BoardID: testData.publicBoard.ID}, userAdmin)
|
||||
err = th.Server.App().InsertBlock(&model.Block{ID: "block-7", Title: "Test", Type: "card", BoardID: testData.publicBoard.ID}, userAdmin)
|
||||
require.NoError(t, err)
|
||||
err = th.Server.App().InsertBlock(model.Block{ID: "block-8", Title: "Test", Type: "card", BoardID: testData.privateBoard.ID}, userAdmin)
|
||||
err = th.Server.App().InsertBlock(&model.Block{ID: "block-8", Title: "Test", Type: "card", BoardID: testData.privateBoard.ID}, userAdmin)
|
||||
require.NoError(t, err)
|
||||
err = th.Server.App().DeleteBlock("block-1", userAdmin)
|
||||
require.NoError(t, err)
|
||||
@ -1548,13 +1548,13 @@ func TestPermissionsUndeleteBoard(t *testing.T) {
|
||||
|
||||
func TestPermissionsDuplicateBoardBlock(t *testing.T) {
|
||||
extraSetup := func(t *testing.T, th *TestHelper, testData TestData) {
|
||||
err := th.Server.App().InsertBlock(model.Block{ID: "block-5", Title: "Test", Type: "card", BoardID: testData.publicTemplate.ID}, userAdmin)
|
||||
err := th.Server.App().InsertBlock(&model.Block{ID: "block-5", Title: "Test", Type: "card", BoardID: testData.publicTemplate.ID}, userAdmin)
|
||||
require.NoError(t, err)
|
||||
err = th.Server.App().InsertBlock(model.Block{ID: "block-6", Title: "Test", Type: "card", BoardID: testData.privateTemplate.ID}, userAdmin)
|
||||
err = th.Server.App().InsertBlock(&model.Block{ID: "block-6", Title: "Test", Type: "card", BoardID: testData.privateTemplate.ID}, userAdmin)
|
||||
require.NoError(t, err)
|
||||
err = th.Server.App().InsertBlock(model.Block{ID: "block-7", Title: "Test", Type: "card", BoardID: testData.publicBoard.ID}, userAdmin)
|
||||
err = th.Server.App().InsertBlock(&model.Block{ID: "block-7", Title: "Test", Type: "card", BoardID: testData.publicBoard.ID}, userAdmin)
|
||||
require.NoError(t, err)
|
||||
err = th.Server.App().InsertBlock(model.Block{ID: "block-8", Title: "Test", Type: "card", BoardID: testData.privateBoard.ID}, userAdmin)
|
||||
err = th.Server.App().InsertBlock(&model.Block{ID: "block-8", Title: "Test", Type: "card", BoardID: testData.privateBoard.ID}, userAdmin)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
@ -2592,7 +2592,7 @@ func TestPermissionsUpdateUserConfig(t *testing.T) {
|
||||
func TestPermissionsCreateBoardsAndBlocks(t *testing.T) {
|
||||
bab := toJSON(t, model.BoardsAndBlocks{
|
||||
Boards: []*model.Board{{ID: "test", Title: "Test Board", TeamID: "test-team"}},
|
||||
Blocks: []model.Block{
|
||||
Blocks: []*model.Block{
|
||||
{ID: "test-block", BoardID: "test", Type: "card", CreateAt: model.GetMillis(), UpdateAt: model.GetMillis()},
|
||||
},
|
||||
})
|
||||
@ -3773,3 +3773,40 @@ func TestPermissionsChannel(t *testing.T) {
|
||||
runTestCases(t, ttCases, testData, clients)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPermissionsGetStatistics(t *testing.T) {
|
||||
t.Run("plugin", func(t *testing.T) {
|
||||
th := SetupTestHelperPluginMode(t)
|
||||
defer th.TearDown()
|
||||
clients := setupClients(th)
|
||||
testData := setupData(t, th)
|
||||
ttCases := []TestCase{
|
||||
{"/statistics", methodGet, "", userAnon, http.StatusUnauthorized, 0},
|
||||
{"/statistics", methodGet, "", userNoTeamMember, http.StatusForbidden, 0},
|
||||
{"/statistics", methodGet, "", userTeamMember, http.StatusForbidden, 0},
|
||||
{"/statistics", methodGet, "", userViewer, http.StatusForbidden, 0},
|
||||
{"/statistics", methodGet, "", userCommenter, http.StatusForbidden, 0},
|
||||
{"/statistics", methodGet, "", userEditor, http.StatusForbidden, 0},
|
||||
{"/statistics", methodGet, "", userAdmin, http.StatusOK, 1},
|
||||
{"/statistics", methodGet, "", userGuest, http.StatusForbidden, 0},
|
||||
}
|
||||
runTestCases(t, ttCases, testData, clients)
|
||||
})
|
||||
t.Run("local", func(t *testing.T) {
|
||||
th := SetupTestHelperLocalMode(t)
|
||||
defer th.TearDown()
|
||||
clients := setupLocalClients(th)
|
||||
testData := setupData(t, th)
|
||||
ttCases := []TestCase{
|
||||
{"/statistics", methodGet, "", userAnon, http.StatusUnauthorized, 0},
|
||||
{"/statistics", methodGet, "", userNoTeamMember, http.StatusNotImplemented, 0},
|
||||
{"/statistics", methodGet, "", userTeamMember, http.StatusNotImplemented, 0},
|
||||
{"/statistics", methodGet, "", userViewer, http.StatusNotImplemented, 0},
|
||||
{"/statistics", methodGet, "", userCommenter, http.StatusNotImplemented, 0},
|
||||
{"/statistics", methodGet, "", userEditor, http.StatusNotImplemented, 0},
|
||||
{"/statistics", methodGet, "", userAdmin, http.StatusNotImplemented, 1},
|
||||
{"/statistics", methodGet, "", userGuest, http.StatusForbidden, 0},
|
||||
}
|
||||
runTestCases(t, ttCases, testData, clients)
|
||||
})
|
||||
}
|
||||
|
55
server/integrationtests/statistics_test.go
Normal file
55
server/integrationtests/statistics_test.go
Normal file
@ -0,0 +1,55 @@
|
||||
package integrationtests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/focalboard/server/client"
|
||||
"github.com/mattermost/focalboard/server/model"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestStatisticsLocalMode(t *testing.T) {
|
||||
th := SetupTestHelper(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
t.Run("an unauthenticated client should not be able to get statistics", func(t *testing.T) {
|
||||
th.Logout(th.Client)
|
||||
|
||||
stats, resp := th.Client.GetStatistics()
|
||||
th.CheckUnauthorized(resp)
|
||||
require.Nil(t, stats)
|
||||
})
|
||||
|
||||
t.Run("Check authenticated user, not admin", func(t *testing.T) {
|
||||
th.Login1()
|
||||
|
||||
stats, resp := th.Client.GetStatistics()
|
||||
th.CheckNotImplemented(resp)
|
||||
require.Nil(t, stats)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStatisticsPluginMode(t *testing.T) {
|
||||
th := SetupTestHelperPluginMode(t)
|
||||
defer th.TearDown()
|
||||
|
||||
// Permissions are tested in permissions_test.go
|
||||
// This tests the functionality.
|
||||
t.Run("Check authenticated user, admin", func(t *testing.T) {
|
||||
th.Client = client.NewClient(th.Server.Config().ServerRoot, "")
|
||||
th.Client.HTTPHeader["Mattermost-User-Id"] = userAdmin
|
||||
|
||||
stats, resp := th.Client.GetStatistics()
|
||||
th.CheckOK(resp)
|
||||
require.NotNil(t, stats)
|
||||
|
||||
numberCards := 2
|
||||
th.CreateBoardAndCards("testTeam", model.BoardTypeOpen, numberCards)
|
||||
|
||||
stats, resp = th.Client.GetStatistics()
|
||||
th.CheckOK(resp)
|
||||
require.NotNil(t, stats)
|
||||
require.Equal(t, 1, stats.Boards)
|
||||
require.Equal(t, numberCards, stats.Cards)
|
||||
})
|
||||
}
|
@ -31,7 +31,7 @@ func createTestSubscriptions(client *client.Client, num int) ([]*model.Subscript
|
||||
}
|
||||
|
||||
for n := 0; n < num; n++ {
|
||||
newBlock := model.Block{
|
||||
newBlock := &model.Block{
|
||||
ID: utils.NewID(utils.IDTypeCard),
|
||||
BoardID: board.ID,
|
||||
CreateAt: 1,
|
||||
@ -39,7 +39,7 @@ func createTestSubscriptions(client *client.Client, num int) ([]*model.Subscript
|
||||
Type: model.TypeCard,
|
||||
}
|
||||
|
||||
newBlocks, resp := client.InsertBlocks(board.ID, []model.Block{newBlock}, false)
|
||||
newBlocks, resp := client.InsertBlocks(board.ID, []*model.Block{newBlock}, false)
|
||||
if resp.Error != nil {
|
||||
return nil, "", fmt.Errorf("cannot insert test card block: %w", resp.Error)
|
||||
}
|
||||
|
@ -118,14 +118,14 @@ type BoardModifier func(board *Board, cache map[string]interface{}) bool
|
||||
// Return true to import the block or false to skip import.
|
||||
type BlockModifier func(block *Block, cache map[string]interface{}) bool
|
||||
|
||||
func BlocksFromJSON(data io.Reader) []Block {
|
||||
var blocks []Block
|
||||
func BlocksFromJSON(data io.Reader) []*Block {
|
||||
var blocks []*Block
|
||||
_ = json.NewDecoder(data).Decode(&blocks)
|
||||
return blocks
|
||||
}
|
||||
|
||||
// LogClone implements the `mlog.LogCloner` interface to provide a subset of Block fields for logging.
|
||||
func (b Block) LogClone() interface{} {
|
||||
func (b *Block) LogClone() interface{} {
|
||||
return struct {
|
||||
ID string
|
||||
ParentID string
|
||||
@ -199,7 +199,7 @@ type QueryBoardHistoryOptions struct {
|
||||
Descending bool // if true then the records are sorted by insert_at in descending order
|
||||
}
|
||||
|
||||
func StampModificationMetadata(userID string, blocks []Block, auditRec *audit.Record) {
|
||||
func StampModificationMetadata(userID string, blocks []*Block, auditRec *audit.Record) {
|
||||
if userID == SingleUser {
|
||||
userID = ""
|
||||
}
|
||||
@ -215,15 +215,15 @@ func StampModificationMetadata(userID string, blocks []Block, auditRec *audit.Re
|
||||
}
|
||||
}
|
||||
|
||||
func (b Block) ShouldBeLimited(cardLimitTimestamp int64) bool {
|
||||
func (b *Block) ShouldBeLimited(cardLimitTimestamp int64) bool {
|
||||
return b.Type == TypeCard &&
|
||||
b.UpdateAt < cardLimitTimestamp
|
||||
}
|
||||
|
||||
// Returns a limited version of the block that doesn't contain the
|
||||
// contents of the block, only its IDs and type.
|
||||
func (b Block) GetLimited() Block {
|
||||
newBlock := Block{
|
||||
func (b *Block) GetLimited() *Block {
|
||||
newBlock := &Block{
|
||||
Title: b.Title,
|
||||
ID: b.ID,
|
||||
ParentID: b.ParentID,
|
||||
|
@ -15,7 +15,7 @@ import (
|
||||
func TestGenerateBlockIDs(t *testing.T) {
|
||||
t.Run("Should generate a new ID for a single block with no references", func(t *testing.T) {
|
||||
blockID := utils.NewID(utils.IDTypeBlock)
|
||||
blocks := []Block{{ID: blockID}}
|
||||
blocks := []*Block{{ID: blockID}}
|
||||
|
||||
blocks = GenerateBlockIDs(blocks, &mlog.Logger{})
|
||||
|
||||
@ -28,7 +28,7 @@ func TestGenerateBlockIDs(t *testing.T) {
|
||||
blockID := utils.NewID(utils.IDTypeBlock)
|
||||
boardID := utils.NewID(utils.IDTypeBlock)
|
||||
parentID := utils.NewID(utils.IDTypeBlock)
|
||||
blocks := []Block{{ID: blockID, BoardID: boardID, ParentID: parentID}}
|
||||
blocks := []*Block{{ID: blockID, BoardID: boardID, ParentID: parentID}}
|
||||
|
||||
blocks = GenerateBlockIDs(blocks, &mlog.Logger{})
|
||||
|
||||
@ -41,14 +41,14 @@ func TestGenerateBlockIDs(t *testing.T) {
|
||||
blockID1 := utils.NewID(utils.IDTypeBlock)
|
||||
boardID1 := utils.NewID(utils.IDTypeBlock)
|
||||
parentID1 := utils.NewID(utils.IDTypeBlock)
|
||||
block1 := Block{ID: blockID1, BoardID: boardID1, ParentID: parentID1}
|
||||
block1 := &Block{ID: blockID1, BoardID: boardID1, ParentID: parentID1}
|
||||
|
||||
blockID2 := utils.NewID(utils.IDTypeBlock)
|
||||
boardID2 := blockID1
|
||||
parentID2 := utils.NewID(utils.IDTypeBlock)
|
||||
block2 := Block{ID: blockID2, BoardID: boardID2, ParentID: parentID2}
|
||||
block2 := &Block{ID: blockID2, BoardID: boardID2, ParentID: parentID2}
|
||||
|
||||
blocks := []Block{block1, block2}
|
||||
blocks := []*Block{block1, block2}
|
||||
|
||||
blocks = GenerateBlockIDs(blocks, &mlog.Logger{})
|
||||
|
||||
@ -69,14 +69,14 @@ func TestGenerateBlockIDs(t *testing.T) {
|
||||
blockID1 := utils.NewID(utils.IDTypeBlock)
|
||||
boardID1 := ""
|
||||
parentID1 := utils.NewID(utils.IDTypeBlock)
|
||||
block1 := Block{ID: blockID1, BoardID: boardID1, ParentID: parentID1}
|
||||
block1 := &Block{ID: blockID1, BoardID: boardID1, ParentID: parentID1}
|
||||
|
||||
blockID2 := utils.NewID(utils.IDTypeBlock)
|
||||
boardID2 := utils.NewID(utils.IDTypeBlock)
|
||||
parentID2 := ""
|
||||
block2 := Block{ID: blockID2, BoardID: boardID2, ParentID: parentID2}
|
||||
block2 := &Block{ID: blockID2, BoardID: boardID2, ParentID: parentID2}
|
||||
|
||||
blocks := []Block{block1, block2}
|
||||
blocks := []*Block{block1, block2}
|
||||
|
||||
blocks = GenerateBlockIDs(blocks, &mlog.Logger{})
|
||||
|
||||
@ -94,28 +94,28 @@ func TestGenerateBlockIDs(t *testing.T) {
|
||||
blockID1 := utils.NewID(utils.IDTypeBlock)
|
||||
boardID1 := utils.NewID(utils.IDTypeBlock)
|
||||
parentID1 := utils.NewID(utils.IDTypeBlock)
|
||||
block1 := Block{ID: blockID1, BoardID: boardID1, ParentID: parentID1}
|
||||
block1 := &Block{ID: blockID1, BoardID: boardID1, ParentID: parentID1}
|
||||
|
||||
// linked to 1
|
||||
blockID2 := utils.NewID(utils.IDTypeBlock)
|
||||
boardID2 := blockID1
|
||||
parentID2 := utils.NewID(utils.IDTypeBlock)
|
||||
block2 := Block{ID: blockID2, BoardID: boardID2, ParentID: parentID2}
|
||||
block2 := &Block{ID: blockID2, BoardID: boardID2, ParentID: parentID2}
|
||||
|
||||
// linked to 2
|
||||
blockID3 := utils.NewID(utils.IDTypeBlock)
|
||||
boardID3 := blockID2
|
||||
parentID3 := utils.NewID(utils.IDTypeBlock)
|
||||
block3 := Block{ID: blockID3, BoardID: boardID3, ParentID: parentID3}
|
||||
block3 := &Block{ID: blockID3, BoardID: boardID3, ParentID: parentID3}
|
||||
|
||||
// linked to 1
|
||||
blockID4 := utils.NewID(utils.IDTypeBlock)
|
||||
boardID4 := blockID1
|
||||
parentID4 := utils.NewID(utils.IDTypeBlock)
|
||||
block4 := Block{ID: blockID4, BoardID: boardID4, ParentID: parentID4}
|
||||
block4 := &Block{ID: blockID4, BoardID: boardID4, ParentID: parentID4}
|
||||
|
||||
// blocks are shuffled
|
||||
blocks := []Block{block4, block2, block1, block3}
|
||||
blocks := []*Block{block4, block2, block1, block3}
|
||||
|
||||
blocks = GenerateBlockIDs(blocks, &mlog.Logger{})
|
||||
|
||||
@ -147,7 +147,7 @@ func TestGenerateBlockIDs(t *testing.T) {
|
||||
blockID1 := utils.NewID(utils.IDTypeBlock)
|
||||
boardID1 := utils.NewID(utils.IDTypeBlock)
|
||||
parentID1 := utils.NewID(utils.IDTypeBlock)
|
||||
block1 := Block{
|
||||
block1 := &Block{
|
||||
ID: blockID1,
|
||||
BoardID: boardID1,
|
||||
ParentID: parentID1,
|
||||
@ -156,7 +156,7 @@ func TestGenerateBlockIDs(t *testing.T) {
|
||||
blockID2 := utils.NewID(utils.IDTypeBlock)
|
||||
boardID2 := utils.NewID(utils.IDTypeBlock)
|
||||
parentID2 := utils.NewID(utils.IDTypeBlock)
|
||||
block2 := Block{
|
||||
block2 := &Block{
|
||||
ID: blockID2,
|
||||
BoardID: boardID2,
|
||||
ParentID: parentID2,
|
||||
@ -167,7 +167,7 @@ func TestGenerateBlockIDs(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
blocks := []Block{block1, block2}
|
||||
blocks := []*Block{block1, block2}
|
||||
|
||||
blocks = GenerateBlockIDs(blocks, &mlog.Logger{})
|
||||
|
||||
@ -191,21 +191,21 @@ func TestGenerateBlockIDs(t *testing.T) {
|
||||
blockID1 := utils.NewID(utils.IDTypeBlock)
|
||||
boardID1 := utils.NewID(utils.IDTypeBlock)
|
||||
parentID1 := utils.NewID(utils.IDTypeBlock)
|
||||
block1 := Block{
|
||||
block1 := &Block{
|
||||
ID: blockID1,
|
||||
BoardID: boardID1,
|
||||
ParentID: parentID1,
|
||||
}
|
||||
|
||||
blockID2 := utils.NewID(utils.IDTypeBlock)
|
||||
block2 := Block{
|
||||
block2 := &Block{
|
||||
ID: blockID2,
|
||||
BoardID: boardID1,
|
||||
ParentID: parentID1,
|
||||
}
|
||||
|
||||
blockID3 := utils.NewID(utils.IDTypeBlock)
|
||||
block3 := Block{
|
||||
block3 := &Block{
|
||||
ID: blockID3,
|
||||
BoardID: boardID1,
|
||||
ParentID: parentID1,
|
||||
@ -215,7 +215,7 @@ func TestGenerateBlockIDs(t *testing.T) {
|
||||
boardID2 := utils.NewID(utils.IDTypeBlock)
|
||||
parentID2 := utils.NewID(utils.IDTypeBlock)
|
||||
|
||||
block4 := Block{
|
||||
block4 := &Block{
|
||||
ID: blockID4,
|
||||
BoardID: boardID2,
|
||||
ParentID: parentID2,
|
||||
@ -230,7 +230,7 @@ func TestGenerateBlockIDs(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
blocks := []Block{block1, block2, block3, block4}
|
||||
blocks := []*Block{block1, block2, block3, block4}
|
||||
|
||||
blocks = GenerateBlockIDs(blocks, &mlog.Logger{})
|
||||
|
||||
@ -258,7 +258,7 @@ func TestGenerateBlockIDs(t *testing.T) {
|
||||
blockID1 := utils.NewID(utils.IDTypeBlock)
|
||||
boardID1 := utils.NewID(utils.IDTypeBlock)
|
||||
parentID1 := utils.NewID(utils.IDTypeBlock)
|
||||
block1 := Block{
|
||||
block1 := &Block{
|
||||
ID: blockID1,
|
||||
BoardID: boardID1,
|
||||
ParentID: parentID1,
|
||||
@ -267,7 +267,7 @@ func TestGenerateBlockIDs(t *testing.T) {
|
||||
blockID2 := utils.NewID(utils.IDTypeBlock)
|
||||
boardID2 := utils.NewID(utils.IDTypeBlock)
|
||||
parentID2 := utils.NewID(utils.IDTypeBlock)
|
||||
block2 := Block{
|
||||
block2 := &Block{
|
||||
ID: blockID2,
|
||||
BoardID: boardID2,
|
||||
ParentID: parentID2,
|
||||
@ -276,7 +276,7 @@ func TestGenerateBlockIDs(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
blocks := []Block{block1, block2}
|
||||
blocks := []*Block{block1, block2}
|
||||
|
||||
blocks = GenerateBlockIDs(blocks, &mlog.Logger{})
|
||||
|
||||
@ -297,8 +297,8 @@ func TestGenerateBlockIDs(t *testing.T) {
|
||||
|
||||
func TestStampModificationMetadata(t *testing.T) {
|
||||
t.Run("base case", func(t *testing.T) {
|
||||
block := Block{}
|
||||
blocks := []Block{block}
|
||||
block := &Block{}
|
||||
blocks := []*Block{block}
|
||||
assert.Empty(t, block.ModifiedBy)
|
||||
assert.Empty(t, block.UpdateAt)
|
||||
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
// keeping consistent any references that other blocks would made to
|
||||
// the original IDs, so a tree of blocks can get new IDs and maintain
|
||||
// its shape.
|
||||
func GenerateBlockIDs(blocks []Block, logger mlog.LoggerIFace) []Block {
|
||||
func GenerateBlockIDs(blocks []*Block, logger mlog.LoggerIFace) []*Block {
|
||||
blockIDs := map[string]BlockType{}
|
||||
referenceIDs := map[string]bool{}
|
||||
for _, block := range blocks {
|
||||
@ -93,7 +93,7 @@ func GenerateBlockIDs(blocks []Block, logger mlog.LoggerIFace) []Block {
|
||||
return utils.NewID(BlockType2IDType(blockIDs[id]))
|
||||
}
|
||||
|
||||
newBlocks := make([]Block, len(blocks))
|
||||
newBlocks := make([]*Block, len(blocks))
|
||||
for i, block := range blocks {
|
||||
block.ID = getExistingOrNewID(block.ID)
|
||||
block.BoardID = getExistingOrOldID(block.BoardID)
|
||||
@ -101,11 +101,11 @@ func GenerateBlockIDs(blocks []Block, logger mlog.LoggerIFace) []Block {
|
||||
|
||||
blockMod := block
|
||||
if _, ok := blockMod.Fields["contentOrder"]; ok {
|
||||
fixFieldIDs(&blockMod, "contentOrder", getExistingOrOldID, logger)
|
||||
fixFieldIDs(blockMod, "contentOrder", getExistingOrOldID, logger)
|
||||
}
|
||||
|
||||
if _, ok := blockMod.Fields["cardOrder"]; ok {
|
||||
fixFieldIDs(&blockMod, "cardOrder", getExistingOrOldID, logger)
|
||||
fixFieldIDs(blockMod, "cardOrder", getExistingOrOldID, logger)
|
||||
}
|
||||
|
||||
if _, ok := blockMod.Fields["defaultTemplateId"]; ok {
|
||||
|
15
server/model/board_statistics.go
Normal file
15
server/model/board_statistics.go
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
package model
|
||||
|
||||
// BoardsStatistics is the representation of the statistics for the Boards server
|
||||
// swagger:model
|
||||
type BoardsStatistics struct {
|
||||
// The maximum number of cards on the server
|
||||
// required: true
|
||||
Boards int `json:"board_count"`
|
||||
|
||||
// The maximum number of cards on the server
|
||||
// required: true
|
||||
Cards int `json:"card_count"`
|
||||
}
|
@ -35,7 +35,7 @@ type BoardsAndBlocks struct {
|
||||
|
||||
// The blocks
|
||||
// required: false
|
||||
Blocks []Block `json:"blocks"`
|
||||
Blocks []*Block `json:"blocks"`
|
||||
}
|
||||
|
||||
func (bab *BoardsAndBlocks) IsValid() error {
|
||||
@ -139,13 +139,13 @@ func GenerateBoardsAndBlocksIDs(bab *BoardsAndBlocks, logger mlog.LoggerIFace) (
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blocksByBoard := map[string][]Block{}
|
||||
blocksByBoard := map[string][]*Block{}
|
||||
for _, block := range bab.Blocks {
|
||||
blocksByBoard[block.BoardID] = append(blocksByBoard[block.BoardID], block)
|
||||
}
|
||||
|
||||
boards := []*Board{}
|
||||
blocks := []Block{}
|
||||
blocks := []*Block{}
|
||||
for _, board := range bab.Boards {
|
||||
newID := utils.NewID(utils.IDTypeBoard)
|
||||
for _, block := range blocksByBoard[board.ID] {
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
func TestIsValidBoardsAndBlocks(t *testing.T) {
|
||||
t.Run("no boards", func(t *testing.T) {
|
||||
bab := &BoardsAndBlocks{
|
||||
Blocks: []Block{
|
||||
Blocks: []*Block{
|
||||
{ID: "block-id-1", BoardID: "board-id-1", Type: TypeCard},
|
||||
{ID: "block-id-2", BoardID: "board-id-2", Type: TypeCard},
|
||||
},
|
||||
@ -37,7 +37,7 @@ func TestIsValidBoardsAndBlocks(t *testing.T) {
|
||||
{ID: "board-id-1", Type: BoardTypeOpen},
|
||||
{ID: "board-id-2", Type: BoardTypePrivate},
|
||||
},
|
||||
Blocks: []Block{
|
||||
Blocks: []*Block{
|
||||
{ID: "block-id-1", BoardID: "board-id-1", Type: TypeCard},
|
||||
{ID: "block-id-3", BoardID: "board-id-3", Type: TypeCard},
|
||||
{ID: "block-id-2", BoardID: "board-id-2", Type: TypeCard},
|
||||
@ -53,7 +53,7 @@ func TestIsValidBoardsAndBlocks(t *testing.T) {
|
||||
{ID: "board-id-1", Type: BoardTypeOpen},
|
||||
{ID: "board-id-2", Type: BoardTypePrivate},
|
||||
},
|
||||
Blocks: []Block{
|
||||
Blocks: []*Block{
|
||||
{ID: "block-id-1", BoardID: "board-id-1", Type: TypeCard},
|
||||
{ID: "block-id-3", BoardID: "board-id-2", Type: TypeCard},
|
||||
{ID: "block-id-2", BoardID: "board-id-2", Type: TypeCard},
|
||||
@ -68,13 +68,13 @@ func TestGenerateBoardsAndBlocksIDs(t *testing.T) {
|
||||
logger, err := mlog.NewLogger()
|
||||
require.NoError(t, err)
|
||||
|
||||
getBlockByType := func(blocks []Block, blockType BlockType) Block {
|
||||
getBlockByType := func(blocks []*Block, blockType BlockType) *Block {
|
||||
for _, b := range blocks {
|
||||
if b.Type == blockType {
|
||||
return b
|
||||
}
|
||||
}
|
||||
return Block{}
|
||||
return &Block{}
|
||||
}
|
||||
|
||||
getBoardByTitle := func(boards []*Board, title string) *Board {
|
||||
@ -88,7 +88,7 @@ func TestGenerateBoardsAndBlocksIDs(t *testing.T) {
|
||||
|
||||
t.Run("invalid boards and blocks", func(t *testing.T) {
|
||||
bab := &BoardsAndBlocks{
|
||||
Blocks: []Block{
|
||||
Blocks: []*Block{
|
||||
{ID: "block-id-1", BoardID: "board-id-1", Type: TypeCard},
|
||||
{ID: "block-id-2", BoardID: "board-id-2", Type: TypeCard},
|
||||
},
|
||||
@ -106,7 +106,7 @@ func TestGenerateBoardsAndBlocksIDs(t *testing.T) {
|
||||
{ID: "board-id-2", Type: BoardTypePrivate, Title: "board2"},
|
||||
{ID: "board-id-3", Type: BoardTypeOpen, Title: "board3"},
|
||||
},
|
||||
Blocks: []Block{
|
||||
Blocks: []*Block{
|
||||
{ID: "block-id-1", BoardID: "board-id-1", Type: TypeCard},
|
||||
{ID: "block-id-2", BoardID: "board-id-2", Type: TypeView},
|
||||
{ID: "block-id-3", BoardID: "board-id-2", Type: TypeText},
|
||||
|
@ -347,6 +347,20 @@ func (mr *MockServicesAPIMockRecorder) GetUsersFromProfiles(arg0 interface{}) *g
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUsersFromProfiles", reflect.TypeOf((*MockServicesAPI)(nil).GetUsersFromProfiles), arg0)
|
||||
}
|
||||
|
||||
// HasPermissionTo mocks base method.
|
||||
func (m *MockServicesAPI) HasPermissionTo(arg0 string, arg1 *model.Permission) bool {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "HasPermissionTo", arg0, arg1)
|
||||
ret0, _ := ret[0].(bool)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// HasPermissionTo indicates an expected call of HasPermissionTo.
|
||||
func (mr *MockServicesAPIMockRecorder) HasPermissionTo(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasPermissionTo", reflect.TypeOf((*MockServicesAPI)(nil).HasPermissionTo), arg0, arg1)
|
||||
}
|
||||
|
||||
// HasPermissionToChannel mocks base method.
|
||||
func (m *MockServicesAPI) HasPermissionToChannel(arg0, arg1 string, arg2 *model.Permission) bool {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -49,6 +49,7 @@ type ServicesAPI interface {
|
||||
CreateMember(teamID string, userID string) (*mm_model.TeamMember, error)
|
||||
|
||||
// Permissions service
|
||||
HasPermissionTo(userID string, permission *mm_model.Permission) bool
|
||||
HasPermissionToTeam(userID, teamID string, permission *mm_model.Permission) bool
|
||||
HasPermissionToChannel(askingUserID string, channelID string, permission *mm_model.Permission) bool
|
||||
|
||||
|
@ -10,8 +10,8 @@ import (
|
||||
)
|
||||
|
||||
type AppAPI interface {
|
||||
GetBlockHistory(blockID string, opts model.QueryBlockHistoryOptions) ([]model.Block, error)
|
||||
GetSubTree2(boardID, blockID string, opts model.QuerySubtreeOptions) ([]model.Block, error)
|
||||
GetBlockHistory(blockID string, opts model.QueryBlockHistoryOptions) ([]*model.Block, error)
|
||||
GetSubTree2(boardID, blockID string, opts model.QuerySubtreeOptions) ([]*model.Block, error)
|
||||
GetBoardAndCardByID(blockID string) (board *model.Board, card *model.Block, err error)
|
||||
|
||||
GetUserByID(userID string) (*model.User, error)
|
||||
|
@ -68,7 +68,7 @@ func (dg *diffGenerator) generateDiffs() ([]*Diff, error) {
|
||||
if len(blocks) == 0 {
|
||||
return nil, fmt.Errorf("block not found for notification: %w", err)
|
||||
}
|
||||
block := &blocks[0]
|
||||
block := blocks[0]
|
||||
|
||||
if dg.board == nil || dg.card == nil {
|
||||
return nil, fmt.Errorf("cannot generate diff for block %s; must have a valid board and card: %w", dg.hint.BlockID, err)
|
||||
@ -166,7 +166,7 @@ func (dg *diffGenerator) generateDiffsForCard(card *model.Block, schema model.Pr
|
||||
continue
|
||||
}
|
||||
|
||||
blockDiff, err := dg.generateDiffForBlock(&blocks[i], schema)
|
||||
blockDiff, err := dg.generateDiffForBlock(blocks[i], schema)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not generate diff for block %s: %w", blocks[i].ID, err)
|
||||
}
|
||||
@ -225,7 +225,7 @@ func (dg *diffGenerator) generateDiffForBlock(newBlock *model.Block, schema mode
|
||||
|
||||
var oldBlock *model.Block
|
||||
if len(history) != 0 {
|
||||
oldBlock = &history[0]
|
||||
oldBlock = history[0]
|
||||
|
||||
dg.logger.Debug("generateDiffForBlock - old block",
|
||||
mlog.String("block_id", oldBlock.ID),
|
||||
|
@ -23,6 +23,10 @@ func New(store permissions.Store, logger mlog.LoggerIFace) *Service {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) HasPermissionTo(userID string, permission *mmModel.Permission) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *Service) HasPermissionToTeam(userID, teamID string, permission *mmModel.Permission) bool {
|
||||
if userID == "" || teamID == "" || permission == nil {
|
||||
return false
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
)
|
||||
|
||||
type APIInterface interface {
|
||||
HasPermissionTo(userID string, permission *mmModel.Permission) bool
|
||||
HasPermissionToTeam(userID string, teamID string, permission *mmModel.Permission) bool
|
||||
HasPermissionToChannel(userID string, channelID string, permission *mmModel.Permission) bool
|
||||
}
|
||||
@ -24,11 +25,19 @@ type Service struct {
|
||||
|
||||
func New(store permissions.Store, api APIInterface, logger mlog.LoggerIFace) *Service {
|
||||
return &Service{
|
||||
store: store,
|
||||
api: api,
|
||||
store: store,
|
||||
api: api,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) HasPermissionTo(userID string, permission *mmModel.Permission) bool {
|
||||
if userID == "" || permission == nil {
|
||||
return false
|
||||
}
|
||||
return s.api.HasPermissionTo(userID, permission)
|
||||
}
|
||||
|
||||
func (s *Service) HasPermissionToTeam(userID, teamID string, permission *mmModel.Permission) bool {
|
||||
if userID == "" || teamID == "" || permission == nil {
|
||||
return false
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
)
|
||||
|
||||
type PermissionsService interface {
|
||||
HasPermissionTo(userID string, permission *mmModel.Permission) bool
|
||||
HasPermissionToTeam(userID, teamID string, permission *mmModel.Permission) bool
|
||||
HasPermissionToChannel(userID, channelID string, permission *mmModel.Permission) bool
|
||||
HasPermissionToBoard(userID, boardID string, permission *mmModel.Permission) bool
|
||||
|
@ -800,15 +800,15 @@ func (s *MattermostAuthLayer) implicitBoardMembershipsFromRows(rows *sql.Rows) (
|
||||
}
|
||||
|
||||
func (s *MattermostAuthLayer) GetMemberForBoard(boardID, userID string) (*model.BoardMember, error) {
|
||||
bm, err := s.Store.GetMemberForBoard(boardID, userID)
|
||||
bm, originalErr := s.Store.GetMemberForBoard(boardID, userID)
|
||||
// Explicit membership not found
|
||||
if model.IsErrNotFound(err) {
|
||||
if model.IsErrNotFound(originalErr) {
|
||||
if userID == model.SystemUserID {
|
||||
return nil, model.NewErrNotFound(userID)
|
||||
}
|
||||
var user *model.User
|
||||
// No synthetic memberships for guests
|
||||
user, err = s.GetUserByID(userID)
|
||||
user, err := s.GetUserByID(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -867,8 +867,8 @@ func (s *MattermostAuthLayer) GetMemberForBoard(boardID, userID string) (*model.
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if originalErr != nil {
|
||||
return nil, originalErr
|
||||
}
|
||||
return bm, nil
|
||||
}
|
||||
|
@ -295,10 +295,10 @@ func (mr *MockStoreMockRecorder) DeleteSubscription(arg0, arg1 interface{}) *gom
|
||||
}
|
||||
|
||||
// DuplicateBlock mocks base method.
|
||||
func (m *MockStore) DuplicateBlock(arg0, arg1, arg2 string, arg3 bool) ([]model.Block, error) {
|
||||
func (m *MockStore) DuplicateBlock(arg0, arg1, arg2 string, arg3 bool) ([]*model.Block, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DuplicateBlock", arg0, arg1, arg2, arg3)
|
||||
ret0, _ := ret[0].([]model.Block)
|
||||
ret0, _ := ret[0].([]*model.Block)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@ -386,10 +386,10 @@ func (mr *MockStoreMockRecorder) GetBlockCountsByType() *gomock.Call {
|
||||
}
|
||||
|
||||
// GetBlockHistory mocks base method.
|
||||
func (m *MockStore) GetBlockHistory(arg0 string, arg1 model.QueryBlockHistoryOptions) ([]model.Block, error) {
|
||||
func (m *MockStore) GetBlockHistory(arg0 string, arg1 model.QueryBlockHistoryOptions) ([]*model.Block, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetBlockHistory", arg0, arg1)
|
||||
ret0, _ := ret[0].([]model.Block)
|
||||
ret0, _ := ret[0].([]*model.Block)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@ -401,10 +401,10 @@ func (mr *MockStoreMockRecorder) GetBlockHistory(arg0, arg1 interface{}) *gomock
|
||||
}
|
||||
|
||||
// GetBlockHistoryDescendants mocks base method.
|
||||
func (m *MockStore) GetBlockHistoryDescendants(arg0 string, arg1 model.QueryBlockHistoryOptions) ([]model.Block, error) {
|
||||
func (m *MockStore) GetBlockHistoryDescendants(arg0 string, arg1 model.QueryBlockHistoryOptions) ([]*model.Block, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetBlockHistoryDescendants", arg0, arg1)
|
||||
ret0, _ := ret[0].([]model.Block)
|
||||
ret0, _ := ret[0].([]*model.Block)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@ -416,10 +416,10 @@ func (mr *MockStoreMockRecorder) GetBlockHistoryDescendants(arg0, arg1 interface
|
||||
}
|
||||
|
||||
// GetBlocks mocks base method.
|
||||
func (m *MockStore) GetBlocks(arg0 model.QueryBlocksOptions) ([]model.Block, error) {
|
||||
func (m *MockStore) GetBlocks(arg0 model.QueryBlocksOptions) ([]*model.Block, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetBlocks", arg0)
|
||||
ret0, _ := ret[0].([]model.Block)
|
||||
ret0, _ := ret[0].([]*model.Block)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@ -431,10 +431,10 @@ func (mr *MockStoreMockRecorder) GetBlocks(arg0 interface{}) *gomock.Call {
|
||||
}
|
||||
|
||||
// GetBlocksByIDs mocks base method.
|
||||
func (m *MockStore) GetBlocksByIDs(arg0 []string) ([]model.Block, error) {
|
||||
func (m *MockStore) GetBlocksByIDs(arg0 []string) ([]*model.Block, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetBlocksByIDs", arg0)
|
||||
ret0, _ := ret[0].([]model.Block)
|
||||
ret0, _ := ret[0].([]*model.Block)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@ -446,10 +446,10 @@ func (mr *MockStoreMockRecorder) GetBlocksByIDs(arg0 interface{}) *gomock.Call {
|
||||
}
|
||||
|
||||
// GetBlocksForBoard mocks base method.
|
||||
func (m *MockStore) GetBlocksForBoard(arg0 string) ([]model.Block, error) {
|
||||
func (m *MockStore) GetBlocksForBoard(arg0 string) ([]*model.Block, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetBlocksForBoard", arg0)
|
||||
ret0, _ := ret[0].([]model.Block)
|
||||
ret0, _ := ret[0].([]*model.Block)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@ -461,10 +461,10 @@ func (mr *MockStoreMockRecorder) GetBlocksForBoard(arg0 interface{}) *gomock.Cal
|
||||
}
|
||||
|
||||
// GetBlocksWithParent mocks base method.
|
||||
func (m *MockStore) GetBlocksWithParent(arg0, arg1 string) ([]model.Block, error) {
|
||||
func (m *MockStore) GetBlocksWithParent(arg0, arg1 string) ([]*model.Block, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetBlocksWithParent", arg0, arg1)
|
||||
ret0, _ := ret[0].([]model.Block)
|
||||
ret0, _ := ret[0].([]*model.Block)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@ -476,10 +476,10 @@ func (mr *MockStoreMockRecorder) GetBlocksWithParent(arg0, arg1 interface{}) *go
|
||||
}
|
||||
|
||||
// GetBlocksWithParentAndType mocks base method.
|
||||
func (m *MockStore) GetBlocksWithParentAndType(arg0, arg1, arg2 string) ([]model.Block, error) {
|
||||
func (m *MockStore) GetBlocksWithParentAndType(arg0, arg1, arg2 string) ([]*model.Block, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetBlocksWithParentAndType", arg0, arg1, arg2)
|
||||
ret0, _ := ret[0].([]model.Block)
|
||||
ret0, _ := ret[0].([]*model.Block)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@ -491,10 +491,10 @@ func (mr *MockStoreMockRecorder) GetBlocksWithParentAndType(arg0, arg1, arg2 int
|
||||
}
|
||||
|
||||
// GetBlocksWithType mocks base method.
|
||||
func (m *MockStore) GetBlocksWithType(arg0, arg1 string) ([]model.Block, error) {
|
||||
func (m *MockStore) GetBlocksWithType(arg0, arg1 string) ([]*model.Block, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetBlocksWithType", arg0, arg1)
|
||||
ret0, _ := ret[0].([]model.Block)
|
||||
ret0, _ := ret[0].([]*model.Block)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@ -837,10 +837,10 @@ func (mr *MockStoreMockRecorder) GetSharing(arg0 interface{}) *gomock.Call {
|
||||
}
|
||||
|
||||
// GetSubTree2 mocks base method.
|
||||
func (m *MockStore) GetSubTree2(arg0, arg1 string, arg2 model.QuerySubtreeOptions) ([]model.Block, error) {
|
||||
func (m *MockStore) GetSubTree2(arg0, arg1 string, arg2 model.QuerySubtreeOptions) ([]*model.Block, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetSubTree2", arg0, arg1, arg2)
|
||||
ret0, _ := ret[0].([]model.Block)
|
||||
ret0, _ := ret[0].([]*model.Block)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@ -1181,7 +1181,7 @@ func (mr *MockStoreMockRecorder) InsertBlock(arg0, arg1 interface{}) *gomock.Cal
|
||||
}
|
||||
|
||||
// InsertBlocks mocks base method.
|
||||
func (m *MockStore) InsertBlocks(arg0 []model.Block, arg1 string) error {
|
||||
func (m *MockStore) InsertBlocks(arg0 []*model.Block, arg1 string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "InsertBlocks", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
|
@ -55,7 +55,7 @@ func (s *SQLStore) blockFields() []string {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SQLStore) getBlocks(db sq.BaseRunner, opts model.QueryBlocksOptions) ([]model.Block, error) {
|
||||
func (s *SQLStore) getBlocks(db sq.BaseRunner, opts model.QueryBlocksOptions) ([]*model.Block, error) {
|
||||
query := s.getQueryBuilder(db).
|
||||
Select(s.blockFields()...).
|
||||
From(s.tablePrefix + "blocks")
|
||||
@ -91,7 +91,7 @@ func (s *SQLStore) getBlocks(db sq.BaseRunner, opts model.QueryBlocksOptions) ([
|
||||
return s.blocksFromRows(rows)
|
||||
}
|
||||
|
||||
func (s *SQLStore) getBlocksWithParentAndType(db sq.BaseRunner, boardID, parentID string, blockType string) ([]model.Block, error) {
|
||||
func (s *SQLStore) getBlocksWithParentAndType(db sq.BaseRunner, boardID, parentID string, blockType string) ([]*model.Block, error) {
|
||||
opts := model.QueryBlocksOptions{
|
||||
BoardID: boardID,
|
||||
ParentID: parentID,
|
||||
@ -100,7 +100,7 @@ func (s *SQLStore) getBlocksWithParentAndType(db sq.BaseRunner, boardID, parentI
|
||||
return s.getBlocks(db, opts)
|
||||
}
|
||||
|
||||
func (s *SQLStore) getBlocksWithParent(db sq.BaseRunner, boardID, parentID string) ([]model.Block, error) {
|
||||
func (s *SQLStore) getBlocksWithParent(db sq.BaseRunner, boardID, parentID string) ([]*model.Block, error) {
|
||||
opts := model.QueryBlocksOptions{
|
||||
BoardID: boardID,
|
||||
ParentID: parentID,
|
||||
@ -108,7 +108,7 @@ func (s *SQLStore) getBlocksWithParent(db sq.BaseRunner, boardID, parentID strin
|
||||
return s.getBlocks(db, opts)
|
||||
}
|
||||
|
||||
func (s *SQLStore) getBlocksByIDs(db sq.BaseRunner, ids []string) ([]model.Block, error) {
|
||||
func (s *SQLStore) getBlocksByIDs(db sq.BaseRunner, ids []string) ([]*model.Block, error) {
|
||||
query := s.getQueryBuilder(db).
|
||||
Select(s.blockFields()...).
|
||||
From(s.tablePrefix + "blocks").
|
||||
@ -134,7 +134,7 @@ func (s *SQLStore) getBlocksByIDs(db sq.BaseRunner, ids []string) ([]model.Block
|
||||
return blocks, nil
|
||||
}
|
||||
|
||||
func (s *SQLStore) getBlocksWithType(db sq.BaseRunner, boardID, blockType string) ([]model.Block, error) {
|
||||
func (s *SQLStore) getBlocksWithType(db sq.BaseRunner, boardID, blockType string) ([]*model.Block, error) {
|
||||
opts := model.QueryBlocksOptions{
|
||||
BoardID: boardID,
|
||||
BlockType: model.BlockType(blockType),
|
||||
@ -143,7 +143,7 @@ func (s *SQLStore) getBlocksWithType(db sq.BaseRunner, boardID, blockType string
|
||||
}
|
||||
|
||||
// getSubTree2 returns blocks within 2 levels of the given blockID.
|
||||
func (s *SQLStore) getSubTree2(db sq.BaseRunner, boardID string, blockID string, opts model.QuerySubtreeOptions) ([]model.Block, error) {
|
||||
func (s *SQLStore) getSubTree2(db sq.BaseRunner, boardID string, blockID string, opts model.QuerySubtreeOptions) ([]*model.Block, error) {
|
||||
query := s.getQueryBuilder(db).
|
||||
Select(s.blockFields()...).
|
||||
From(s.tablePrefix + "blocks").
|
||||
@ -174,15 +174,15 @@ func (s *SQLStore) getSubTree2(db sq.BaseRunner, boardID string, blockID string,
|
||||
return s.blocksFromRows(rows)
|
||||
}
|
||||
|
||||
func (s *SQLStore) getBlocksForBoard(db sq.BaseRunner, boardID string) ([]model.Block, error) {
|
||||
func (s *SQLStore) getBlocksForBoard(db sq.BaseRunner, boardID string) ([]*model.Block, error) {
|
||||
opts := model.QueryBlocksOptions{
|
||||
BoardID: boardID,
|
||||
}
|
||||
return s.getBlocks(db, opts)
|
||||
}
|
||||
|
||||
func (s *SQLStore) blocksFromRows(rows *sql.Rows) ([]model.Block, error) {
|
||||
results := []model.Block{}
|
||||
func (s *SQLStore) blocksFromRows(rows *sql.Rows) ([]*model.Block, error) {
|
||||
results := []*model.Block{}
|
||||
|
||||
for rows.Next() {
|
||||
var block model.Block
|
||||
@ -223,7 +223,7 @@ func (s *SQLStore) blocksFromRows(rows *sql.Rows) ([]model.Block, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
results = append(results, block)
|
||||
results = append(results, &block)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
@ -336,14 +336,14 @@ func (s *SQLStore) patchBlocks(db sq.BaseRunner, blockPatches *model.BlockPatchB
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SQLStore) insertBlocks(db sq.BaseRunner, blocks []model.Block, userID string) error {
|
||||
func (s *SQLStore) insertBlocks(db sq.BaseRunner, blocks []*model.Block, userID string) error {
|
||||
for _, block := range blocks {
|
||||
if block.BoardID == "" {
|
||||
return BoardIDNilError{}
|
||||
}
|
||||
}
|
||||
for i := range blocks {
|
||||
err := s.insertBlock(db, &blocks[i], userID)
|
||||
err := s.insertBlock(db, blocks[i], userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -521,7 +521,8 @@ func (s *SQLStore) getBoardCount(db sq.BaseRunner) (int64, error) {
|
||||
query := s.getQueryBuilder(db).
|
||||
Select("COUNT(*) AS count").
|
||||
From(s.tablePrefix + "boards").
|
||||
Where(sq.Eq{"delete_at": 0})
|
||||
Where(sq.Eq{"delete_at": 0}).
|
||||
Where(sq.Eq{"is_template": false})
|
||||
|
||||
row := query.QueryRow()
|
||||
|
||||
@ -556,10 +557,10 @@ func (s *SQLStore) getBlock(db sq.BaseRunner, blockID string) (*model.Block, err
|
||||
return nil, model.NewErrNotFound("block ID=" + blockID)
|
||||
}
|
||||
|
||||
return &blocks[0], nil
|
||||
return blocks[0], nil
|
||||
}
|
||||
|
||||
func (s *SQLStore) getBlockHistory(db sq.BaseRunner, blockID string, opts model.QueryBlockHistoryOptions) ([]model.Block, error) {
|
||||
func (s *SQLStore) getBlockHistory(db sq.BaseRunner, blockID string, opts model.QueryBlockHistoryOptions) ([]*model.Block, error) {
|
||||
var order string
|
||||
if opts.Descending {
|
||||
order = descClause
|
||||
@ -593,7 +594,7 @@ func (s *SQLStore) getBlockHistory(db sq.BaseRunner, blockID string, opts model.
|
||||
return s.blocksFromRows(rows)
|
||||
}
|
||||
|
||||
func (s *SQLStore) getBlockHistoryDescendants(db sq.BaseRunner, boardID string, opts model.QueryBlockHistoryOptions) ([]model.Block, error) {
|
||||
func (s *SQLStore) getBlockHistoryDescendants(db sq.BaseRunner, boardID string, opts model.QueryBlockHistoryOptions) ([]*model.Block, error) {
|
||||
var order string
|
||||
if opts.Descending {
|
||||
order = descClause
|
||||
@ -645,7 +646,7 @@ func (s *SQLStore) getBoardAndCardByID(db sq.BaseRunner, blockID string) (board
|
||||
return nil, nil, model.NewErrNotFound("block history BlockID=" + blockID)
|
||||
}
|
||||
|
||||
return s.getBoardAndCard(db, &blocks[0])
|
||||
return s.getBoardAndCard(db, blocks[0])
|
||||
}
|
||||
|
||||
// getBoardAndCard returns the first parent of type `card` and and the `board` for the specified block.
|
||||
@ -677,7 +678,7 @@ func (s *SQLStore) getBoardAndCard(db sq.BaseRunner, block *model.Block) (board
|
||||
if len(blocks) == 0 {
|
||||
return board, card, nil
|
||||
}
|
||||
iter = &blocks[0]
|
||||
iter = blocks[0]
|
||||
}
|
||||
board, err = s.getBoard(db, block.BoardID)
|
||||
if err != nil {
|
||||
@ -754,7 +755,7 @@ func (s *SQLStore) replaceBlockID(db sq.BaseRunner, currentID, newID, workspaceI
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SQLStore) duplicateBlock(db sq.BaseRunner, boardID string, blockID string, userID string, asTemplate bool) ([]model.Block, error) {
|
||||
func (s *SQLStore) duplicateBlock(db sq.BaseRunner, boardID string, blockID string, userID string, asTemplate bool) ([]*model.Block, error) {
|
||||
blocks, err := s.getSubTree2(db, boardID, blockID, model.QuerySubtreeOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -764,8 +765,8 @@ func (s *SQLStore) duplicateBlock(db sq.BaseRunner, boardID string, blockID stri
|
||||
return nil, model.NewErrNotFound(message)
|
||||
}
|
||||
|
||||
var rootBlock model.Block
|
||||
allBlocks := []model.Block{}
|
||||
var rootBlock *model.Block
|
||||
allBlocks := []*model.Block{}
|
||||
for _, block := range blocks {
|
||||
if block.Type == model.TypeComment {
|
||||
continue
|
||||
@ -780,7 +781,7 @@ func (s *SQLStore) duplicateBlock(db sq.BaseRunner, boardID string, blockID stri
|
||||
allBlocks = append(allBlocks, block)
|
||||
}
|
||||
}
|
||||
allBlocks = append([]model.Block{rootBlock}, allBlocks...)
|
||||
allBlocks = append([]*model.Block{rootBlock}, allBlocks...)
|
||||
|
||||
allBlocks = model.GenerateBlockIDs(allBlocks, nil)
|
||||
if err := s.insertBlocks(db, allBlocks, userID); err != nil {
|
||||
|
@ -43,7 +43,7 @@ func (s *SQLStore) createBoardsAndBlocksWithAdmin(db sq.BaseRunner, bab *model.B
|
||||
|
||||
func (s *SQLStore) createBoardsAndBlocks(db sq.BaseRunner, bab *model.BoardsAndBlocks, userID string) (*model.BoardsAndBlocks, error) {
|
||||
boards := []*model.Board{}
|
||||
blocks := []model.Block{}
|
||||
blocks := []*model.Block{}
|
||||
|
||||
for _, board := range bab.Boards {
|
||||
newBoard, err := s.insertBoard(db, board, userID)
|
||||
@ -56,7 +56,7 @@ func (s *SQLStore) createBoardsAndBlocks(db sq.BaseRunner, bab *model.BoardsAndB
|
||||
|
||||
for _, block := range bab.Blocks {
|
||||
b := block
|
||||
err := s.insertBlock(db, &b, userID)
|
||||
err := s.insertBlock(db, b, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -90,7 +90,7 @@ func (s *SQLStore) patchBoardsAndBlocks(db sq.BaseRunner, pbab *model.PatchBoard
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bab.Blocks = append(bab.Blocks, *block)
|
||||
bab.Blocks = append(bab.Blocks, block)
|
||||
}
|
||||
|
||||
return bab, nil
|
||||
@ -130,7 +130,7 @@ func (s *SQLStore) deleteBoardsAndBlocks(db sq.BaseRunner, dbab *model.DeleteBoa
|
||||
func (s *SQLStore) duplicateBoard(db sq.BaseRunner, boardID string, userID string, toTeam string, asTemplate bool) (*model.BoardsAndBlocks, []*model.BoardMember, error) {
|
||||
bab := &model.BoardsAndBlocks{
|
||||
Boards: []*model.Board{},
|
||||
Blocks: []model.Block{},
|
||||
Blocks: []*model.Block{},
|
||||
}
|
||||
|
||||
board, err := s.getBoard(db, boardID)
|
||||
@ -162,7 +162,7 @@ func (s *SQLStore) duplicateBoard(db sq.BaseRunner, boardID string, userID strin
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
newBlocks := []model.Block{}
|
||||
newBlocks := []*model.Block{}
|
||||
for _, b := range blocks {
|
||||
if b.Type != model.TypeComment {
|
||||
newBlocks = append(newBlocks, b)
|
||||
|
@ -28,7 +28,7 @@ const (
|
||||
DeletedMembershipBoardsMigrationKey = "DeletedMembershipBoardsMigrationComplete"
|
||||
)
|
||||
|
||||
func (s *SQLStore) getBlocksWithSameID(db sq.BaseRunner) ([]model.Block, error) {
|
||||
func (s *SQLStore) getBlocksWithSameID(db sq.BaseRunner) ([]*model.Block, error) {
|
||||
subquery, _, _ := s.getQueryBuilder(db).
|
||||
Select("id").
|
||||
From(s.tablePrefix + "blocks").
|
||||
@ -93,7 +93,7 @@ func (s *SQLStore) RunUniqueIDsMigration() error {
|
||||
return fmt.Errorf("cannot get blocks with same ID: %w", err)
|
||||
}
|
||||
|
||||
blocksByID := map[string][]model.Block{}
|
||||
blocksByID := map[string][]*model.Block{}
|
||||
for _, block := range blocks {
|
||||
blocksByID[block.ID] = append(blocksByID[block.ID], block)
|
||||
}
|
||||
@ -655,11 +655,11 @@ func (s *SQLStore) RunFixCollationsAndCharsetsMigration() error {
|
||||
}
|
||||
|
||||
// get collation and charSet setting that Channels is using.
|
||||
// when unit testing, no channels tables exist so just set to a default.
|
||||
// when personal server or unit testing, no channels tables exist so just set to a default.
|
||||
var collation string
|
||||
var charSet string
|
||||
var err error
|
||||
if os.Getenv("FOCALBOARD_UNIT_TESTING") == "1" {
|
||||
if !s.isPlugin || os.Getenv("FOCALBOARD_UNIT_TESTING") == "1" {
|
||||
collation = "utf8mb4_general_ci"
|
||||
charSet = "utf8mb4"
|
||||
} else {
|
||||
|
@ -10,7 +10,6 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
//nolint:gosec
|
||||
func TestGetBlocksWithSameID(t *testing.T) {
|
||||
t.Skip("we need to setup a test with the database migrated up to version 14 and then run these tests")
|
||||
|
||||
@ -22,51 +21,50 @@ func TestGetBlocksWithSameID(t *testing.T) {
|
||||
container2 := "2"
|
||||
container3 := "3"
|
||||
|
||||
block1 := model.Block{ID: "block-id-1", BoardID: "board-id-1"}
|
||||
block2 := model.Block{ID: "block-id-2", BoardID: "board-id-2"}
|
||||
block3 := model.Block{ID: "block-id-3", BoardID: "board-id-3"}
|
||||
block1 := &model.Block{ID: "block-id-1", BoardID: "board-id-1"}
|
||||
block2 := &model.Block{ID: "block-id-2", BoardID: "board-id-2"}
|
||||
block3 := &model.Block{ID: "block-id-3", BoardID: "board-id-3"}
|
||||
|
||||
block4 := model.Block{ID: "block-id-1", BoardID: "board-id-1"}
|
||||
block5 := model.Block{ID: "block-id-2", BoardID: "board-id-2"}
|
||||
block4 := &model.Block{ID: "block-id-1", BoardID: "board-id-1"}
|
||||
block5 := &model.Block{ID: "block-id-2", BoardID: "board-id-2"}
|
||||
|
||||
block6 := model.Block{ID: "block-id-1", BoardID: "board-id-1"}
|
||||
block7 := model.Block{ID: "block-id-7", BoardID: "board-id-7"}
|
||||
block8 := model.Block{ID: "block-id-8", BoardID: "board-id-8"}
|
||||
block6 := &model.Block{ID: "block-id-1", BoardID: "board-id-1"}
|
||||
block7 := &model.Block{ID: "block-id-7", BoardID: "board-id-7"}
|
||||
block8 := &model.Block{ID: "block-id-8", BoardID: "board-id-8"}
|
||||
|
||||
for _, block := range []model.Block{block1, block2, block3} {
|
||||
err := sqlStore.insertLegacyBlock(sqlStore.db, container1, &block, "user-id")
|
||||
for _, block := range []*model.Block{block1, block2, block3} {
|
||||
err := sqlStore.insertLegacyBlock(sqlStore.db, container1, block, "user-id")
|
||||
require.NoError(t, err)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
for _, block := range []model.Block{block4, block5} {
|
||||
err := sqlStore.insertLegacyBlock(sqlStore.db, container2, &block, "user-id")
|
||||
for _, block := range []*model.Block{block4, block5} {
|
||||
err := sqlStore.insertLegacyBlock(sqlStore.db, container2, block, "user-id")
|
||||
require.NoError(t, err)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
for _, block := range []model.Block{block6, block7, block8} {
|
||||
err := sqlStore.insertLegacyBlock(sqlStore.db, container3, &block, "user-id")
|
||||
for _, block := range []*model.Block{block6, block7, block8} {
|
||||
err := sqlStore.insertLegacyBlock(sqlStore.db, container3, block, "user-id")
|
||||
require.NoError(t, err)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
blocksWithDuplicatedID := []model.Block{block1, block2, block4, block5, block6}
|
||||
blocksWithDuplicatedID := []*model.Block{block1, block2, block4, block5, block6}
|
||||
|
||||
blocks, err := sqlStore.getBlocksWithSameID(sqlStore.db)
|
||||
require.NoError(t, err)
|
||||
|
||||
// we process the found blocks to remove extra information and be
|
||||
// able to compare both expected and found sets
|
||||
foundBlocks := []model.Block{}
|
||||
foundBlocks := []*model.Block{}
|
||||
for _, foundBlock := range blocks {
|
||||
foundBlocks = append(foundBlocks, model.Block{ID: foundBlock.ID, BoardID: foundBlock.BoardID})
|
||||
foundBlocks = append(foundBlocks, &model.Block{ID: foundBlock.ID, BoardID: foundBlock.BoardID})
|
||||
}
|
||||
|
||||
require.ElementsMatch(t, blocksWithDuplicatedID, foundBlocks)
|
||||
}
|
||||
|
||||
//nolint:gosec
|
||||
func TestReplaceBlockID(t *testing.T) {
|
||||
t.Skip("we need to setup a test with the database migrated up to version 14 and then run these tests")
|
||||
|
||||
@ -78,33 +76,33 @@ func TestReplaceBlockID(t *testing.T) {
|
||||
container2 := "2"
|
||||
|
||||
// blocks from team1
|
||||
block1 := model.Block{ID: "block-id-1", BoardID: "board-id-1"}
|
||||
block2 := model.Block{ID: "block-id-2", BoardID: "board-id-2", ParentID: "block-id-1"}
|
||||
block3 := model.Block{ID: "block-id-3", BoardID: "block-id-1"}
|
||||
block4 := model.Block{ID: "block-id-4", BoardID: "block-id-2"}
|
||||
block5 := model.Block{ID: "block-id-5", BoardID: "block-id-1", ParentID: "block-id-1"}
|
||||
block8 := model.Block{
|
||||
block1 := &model.Block{ID: "block-id-1", BoardID: "board-id-1"}
|
||||
block2 := &model.Block{ID: "block-id-2", BoardID: "board-id-2", ParentID: "block-id-1"}
|
||||
block3 := &model.Block{ID: "block-id-3", BoardID: "block-id-1"}
|
||||
block4 := &model.Block{ID: "block-id-4", BoardID: "block-id-2"}
|
||||
block5 := &model.Block{ID: "block-id-5", BoardID: "block-id-1", ParentID: "block-id-1"}
|
||||
block8 := &model.Block{
|
||||
ID: "block-id-8", BoardID: "board-id-2", Type: model.TypeCard,
|
||||
Fields: map[string]interface{}{"contentOrder": []string{"block-id-1", "block-id-2"}},
|
||||
}
|
||||
|
||||
// blocks from team2. They're identical to blocks 1 and 2,
|
||||
// but they shouldn't change
|
||||
block6 := model.Block{ID: "block-id-1", BoardID: "board-id-1"}
|
||||
block7 := model.Block{ID: "block-id-2", BoardID: "board-id-2", ParentID: "block-id-1"}
|
||||
block9 := model.Block{
|
||||
block6 := &model.Block{ID: "block-id-1", BoardID: "board-id-1"}
|
||||
block7 := &model.Block{ID: "block-id-2", BoardID: "board-id-2", ParentID: "block-id-1"}
|
||||
block9 := &model.Block{
|
||||
ID: "block-id-8", BoardID: "board-id-2", Type: model.TypeCard,
|
||||
Fields: map[string]interface{}{"contentOrder": []string{"block-id-1", "block-id-2"}},
|
||||
}
|
||||
|
||||
for _, block := range []model.Block{block1, block2, block3, block4, block5, block8} {
|
||||
err := sqlStore.insertLegacyBlock(sqlStore.db, container1, &block, "user-id")
|
||||
for _, block := range []*model.Block{block1, block2, block3, block4, block5, block8} {
|
||||
err := sqlStore.insertLegacyBlock(sqlStore.db, container1, block, "user-id")
|
||||
require.NoError(t, err)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
for _, block := range []model.Block{block6, block7, block9} {
|
||||
err := sqlStore.insertLegacyBlock(sqlStore.db, container2, &block, "user-id")
|
||||
for _, block := range []*model.Block{block6, block7, block9} {
|
||||
err := sqlStore.insertLegacyBlock(sqlStore.db, container2, block, "user-id")
|
||||
require.NoError(t, err)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
@ -145,7 +143,6 @@ func TestReplaceBlockID(t *testing.T) {
|
||||
require.Equal(t, newBlock9.Fields["contentOrder"].([]interface{})[1], "block-id-2")
|
||||
}
|
||||
|
||||
//nolint:gosec
|
||||
func TestRunUniqueIDsMigration(t *testing.T) {
|
||||
t.Skip("we need to setup a test with the database migrated up to version 14 and then run these tests")
|
||||
|
||||
@ -164,33 +161,33 @@ func TestRunUniqueIDsMigration(t *testing.T) {
|
||||
|
||||
// blocks from workspace1. They shouldn't change, as the first
|
||||
// duplicated ID is preserved
|
||||
block1 := model.Block{ID: "block-id-1", BoardID: "board-id-1"}
|
||||
block2 := model.Block{ID: "block-id-2", BoardID: "board-id-2", ParentID: "block-id-1"}
|
||||
block3 := model.Block{ID: "block-id-3", BoardID: "block-id-1"}
|
||||
block1 := &model.Block{ID: "block-id-1", BoardID: "board-id-1"}
|
||||
block2 := &model.Block{ID: "block-id-2", BoardID: "board-id-2", ParentID: "block-id-1"}
|
||||
block3 := &model.Block{ID: "block-id-3", BoardID: "block-id-1"}
|
||||
|
||||
// blocks from workspace2. They're identical to blocks 1, 2 and 3,
|
||||
// and they should change
|
||||
block4 := model.Block{ID: "block-id-1", BoardID: "board-id-1"}
|
||||
block5 := model.Block{ID: "block-id-2", BoardID: "board-id-2", ParentID: "block-id-1"}
|
||||
block6 := model.Block{ID: "block-id-6", BoardID: "block-id-1", ParentID: "block-id-2"}
|
||||
block4 := &model.Block{ID: "block-id-1", BoardID: "board-id-1"}
|
||||
block5 := &model.Block{ID: "block-id-2", BoardID: "board-id-2", ParentID: "block-id-1"}
|
||||
block6 := &model.Block{ID: "block-id-6", BoardID: "block-id-1", ParentID: "block-id-2"}
|
||||
|
||||
// block from workspace3. It should change as well
|
||||
block7 := model.Block{ID: "block-id-2", BoardID: "board-id-2"}
|
||||
block7 := &model.Block{ID: "block-id-2", BoardID: "board-id-2"}
|
||||
|
||||
for _, block := range []model.Block{block1, block2, block3} {
|
||||
err := sqlStore.insertLegacyBlock(sqlStore.db, container1, &block, "user-id-2")
|
||||
for _, block := range []*model.Block{block1, block2, block3} {
|
||||
err := sqlStore.insertLegacyBlock(sqlStore.db, container1, block, "user-id-2")
|
||||
require.NoError(t, err)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
for _, block := range []model.Block{block4, block5, block6} {
|
||||
err := sqlStore.insertLegacyBlock(sqlStore.db, container2, &block, "user-id-2")
|
||||
for _, block := range []*model.Block{block4, block5, block6} {
|
||||
err := sqlStore.insertLegacyBlock(sqlStore.db, container2, block, "user-id-2")
|
||||
require.NoError(t, err)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
for _, block := range []model.Block{block7} {
|
||||
err := sqlStore.insertLegacyBlock(sqlStore.db, container3, &block, "user-id-2")
|
||||
for _, block := range []*model.Block{block7} {
|
||||
err := sqlStore.insertLegacyBlock(sqlStore.db, container3, block, "user-id-2")
|
||||
require.NoError(t, err)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
@ -60,8 +60,8 @@ func legacyBoardFields(prefix string) []string {
|
||||
// the old block model. This method is kept to enable the unique IDs
|
||||
// data migration.
|
||||
//nolint:unused
|
||||
func (s *SQLStore) legacyBlocksFromRows(rows *sql.Rows) ([]model.Block, error) {
|
||||
results := []model.Block{}
|
||||
func (s *SQLStore) legacyBlocksFromRows(rows *sql.Rows) ([]*model.Block, error) {
|
||||
results := []*model.Block{}
|
||||
|
||||
for rows.Next() {
|
||||
var block model.Block
|
||||
@ -103,7 +103,7 @@ func (s *SQLStore) legacyBlocksFromRows(rows *sql.Rows) ([]model.Block, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
results = append(results, block)
|
||||
results = append(results, &block)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
@ -150,7 +150,7 @@ func (s *SQLStore) getLegacyBlock(db sq.BaseRunner, workspaceID string, blockID
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &blocks[0], nil
|
||||
return blocks[0], nil
|
||||
}
|
||||
|
||||
// insertLegacyBlock is the old insertBlock version that still uses
|
||||
|
@ -221,7 +221,7 @@ func (s *SQLStore) DeleteSubscription(blockID string, subscriberID string) error
|
||||
|
||||
}
|
||||
|
||||
func (s *SQLStore) DuplicateBlock(boardID string, blockID string, userID string, asTemplate bool) ([]model.Block, error) {
|
||||
func (s *SQLStore) DuplicateBlock(boardID string, blockID string, userID string, asTemplate bool) ([]*model.Block, error) {
|
||||
if s.dbType == model.SqliteDBType {
|
||||
return s.duplicateBlock(s.db, boardID, blockID, userID, asTemplate)
|
||||
}
|
||||
@ -289,42 +289,42 @@ func (s *SQLStore) GetBlockCountsByType() (map[string]int64, error) {
|
||||
|
||||
}
|
||||
|
||||
func (s *SQLStore) GetBlockHistory(blockID string, opts model.QueryBlockHistoryOptions) ([]model.Block, error) {
|
||||
func (s *SQLStore) GetBlockHistory(blockID string, opts model.QueryBlockHistoryOptions) ([]*model.Block, error) {
|
||||
return s.getBlockHistory(s.db, blockID, opts)
|
||||
|
||||
}
|
||||
|
||||
func (s *SQLStore) GetBlockHistoryDescendants(boardID string, opts model.QueryBlockHistoryOptions) ([]model.Block, error) {
|
||||
func (s *SQLStore) GetBlockHistoryDescendants(boardID string, opts model.QueryBlockHistoryOptions) ([]*model.Block, error) {
|
||||
return s.getBlockHistoryDescendants(s.db, boardID, opts)
|
||||
|
||||
}
|
||||
|
||||
func (s *SQLStore) GetBlocks(opts model.QueryBlocksOptions) ([]model.Block, error) {
|
||||
func (s *SQLStore) GetBlocks(opts model.QueryBlocksOptions) ([]*model.Block, error) {
|
||||
return s.getBlocks(s.db, opts)
|
||||
|
||||
}
|
||||
|
||||
func (s *SQLStore) GetBlocksByIDs(ids []string) ([]model.Block, error) {
|
||||
func (s *SQLStore) GetBlocksByIDs(ids []string) ([]*model.Block, error) {
|
||||
return s.getBlocksByIDs(s.db, ids)
|
||||
|
||||
}
|
||||
|
||||
func (s *SQLStore) GetBlocksForBoard(boardID string) ([]model.Block, error) {
|
||||
func (s *SQLStore) GetBlocksForBoard(boardID string) ([]*model.Block, error) {
|
||||
return s.getBlocksForBoard(s.db, boardID)
|
||||
|
||||
}
|
||||
|
||||
func (s *SQLStore) GetBlocksWithParent(boardID string, parentID string) ([]model.Block, error) {
|
||||
func (s *SQLStore) GetBlocksWithParent(boardID string, parentID string) ([]*model.Block, error) {
|
||||
return s.getBlocksWithParent(s.db, boardID, parentID)
|
||||
|
||||
}
|
||||
|
||||
func (s *SQLStore) GetBlocksWithParentAndType(boardID string, parentID string, blockType string) ([]model.Block, error) {
|
||||
func (s *SQLStore) GetBlocksWithParentAndType(boardID string, parentID string, blockType string) ([]*model.Block, error) {
|
||||
return s.getBlocksWithParentAndType(s.db, boardID, parentID, blockType)
|
||||
|
||||
}
|
||||
|
||||
func (s *SQLStore) GetBlocksWithType(boardID string, blockType string) ([]model.Block, error) {
|
||||
func (s *SQLStore) GetBlocksWithType(boardID string, blockType string) ([]*model.Block, error) {
|
||||
return s.getBlocksWithType(s.db, boardID, blockType)
|
||||
|
||||
}
|
||||
@ -439,7 +439,7 @@ func (s *SQLStore) GetSharing(rootID string) (*model.Sharing, error) {
|
||||
|
||||
}
|
||||
|
||||
func (s *SQLStore) GetSubTree2(boardID string, blockID string, opts model.QuerySubtreeOptions) ([]model.Block, error) {
|
||||
func (s *SQLStore) GetSubTree2(boardID string, blockID string, opts model.QuerySubtreeOptions) ([]*model.Block, error) {
|
||||
return s.getSubTree2(s.db, boardID, blockID, opts)
|
||||
|
||||
}
|
||||
@ -573,7 +573,7 @@ func (s *SQLStore) InsertBlock(block *model.Block, userID string) error {
|
||||
|
||||
}
|
||||
|
||||
func (s *SQLStore) InsertBlocks(blocks []model.Block, userID string) error {
|
||||
func (s *SQLStore) InsertBlocks(blocks []*model.Block, userID string) error {
|
||||
if s.dbType == model.SqliteDBType {
|
||||
return s.insertBlocks(s.db, blocks, userID)
|
||||
}
|
||||
|
@ -14,19 +14,19 @@ const CardLimitTimestampSystemKey = "card_limit_timestamp"
|
||||
|
||||
// Store represents the abstraction of the data storage.
|
||||
type Store interface {
|
||||
GetBlocks(opts model.QueryBlocksOptions) ([]model.Block, error)
|
||||
GetBlocksWithParentAndType(boardID, parentID string, blockType string) ([]model.Block, error)
|
||||
GetBlocksWithParent(boardID, parentID string) ([]model.Block, error)
|
||||
GetBlocksByIDs(ids []string) ([]model.Block, error)
|
||||
GetBlocksWithType(boardID, blockType string) ([]model.Block, error)
|
||||
GetSubTree2(boardID, blockID string, opts model.QuerySubtreeOptions) ([]model.Block, error)
|
||||
GetBlocksForBoard(boardID string) ([]model.Block, error)
|
||||
GetBlocks(opts model.QueryBlocksOptions) ([]*model.Block, error)
|
||||
GetBlocksWithParentAndType(boardID, parentID string, blockType string) ([]*model.Block, error)
|
||||
GetBlocksWithParent(boardID, parentID string) ([]*model.Block, error)
|
||||
GetBlocksByIDs(ids []string) ([]*model.Block, error)
|
||||
GetBlocksWithType(boardID, blockType string) ([]*model.Block, error)
|
||||
GetSubTree2(boardID, blockID string, opts model.QuerySubtreeOptions) ([]*model.Block, error)
|
||||
GetBlocksForBoard(boardID string) ([]*model.Block, error)
|
||||
// @withTransaction
|
||||
InsertBlock(block *model.Block, userID string) error
|
||||
// @withTransaction
|
||||
DeleteBlock(blockID string, modifiedBy string) error
|
||||
// @withTransaction
|
||||
InsertBlocks(blocks []model.Block, userID string) error
|
||||
InsertBlocks(blocks []*model.Block, userID string) error
|
||||
// @withTransaction
|
||||
UndeleteBlock(blockID string, modifiedBy string) error
|
||||
// @withTransaction
|
||||
@ -36,15 +36,15 @@ type Store interface {
|
||||
GetBlock(blockID string) (*model.Block, error)
|
||||
// @withTransaction
|
||||
PatchBlock(blockID string, blockPatch *model.BlockPatch, userID string) error
|
||||
GetBlockHistory(blockID string, opts model.QueryBlockHistoryOptions) ([]model.Block, error)
|
||||
GetBlockHistoryDescendants(boardID string, opts model.QueryBlockHistoryOptions) ([]model.Block, error)
|
||||
GetBlockHistory(blockID string, opts model.QueryBlockHistoryOptions) ([]*model.Block, error)
|
||||
GetBlockHistoryDescendants(boardID string, opts model.QueryBlockHistoryOptions) ([]*model.Block, error)
|
||||
GetBoardHistory(boardID string, opts model.QueryBoardHistoryOptions) ([]*model.Board, error)
|
||||
GetBoardAndCardByID(blockID string) (board *model.Board, card *model.Block, err error)
|
||||
GetBoardAndCard(block *model.Block) (board *model.Board, card *model.Block, err error)
|
||||
// @withTransaction
|
||||
DuplicateBoard(boardID string, userID string, toTeam string, asTemplate bool) (*model.BoardsAndBlocks, []*model.BoardMember, error)
|
||||
// @withTransaction
|
||||
DuplicateBlock(boardID string, blockID string, userID string, asTemplate bool) ([]model.Block, error)
|
||||
DuplicateBlock(boardID string, blockID string, userID string, asTemplate bool) ([]*model.Block, error)
|
||||
// @withTransaction
|
||||
PatchBlocks(blockPatches *model.BlockPatchBatch, userID string) error
|
||||
|
||||
|
@ -85,13 +85,13 @@ func testInsertBlock(t *testing.T, store store.Store) {
|
||||
initialCount := len(blocks)
|
||||
|
||||
t.Run("valid block", func(t *testing.T) {
|
||||
block := model.Block{
|
||||
block := &model.Block{
|
||||
ID: "id-test",
|
||||
BoardID: boardID,
|
||||
ModifiedBy: userID,
|
||||
}
|
||||
|
||||
err := store.InsertBlock(&block, "user-id-1")
|
||||
err := store.InsertBlock(block, "user-id-1")
|
||||
require.NoError(t, err)
|
||||
|
||||
blocks, err := store.GetBlocksForBoard(boardID)
|
||||
@ -100,13 +100,13 @@ func testInsertBlock(t *testing.T, store store.Store) {
|
||||
})
|
||||
|
||||
t.Run("invalid rootid", func(t *testing.T) {
|
||||
block := model.Block{
|
||||
block := &model.Block{
|
||||
ID: "id-test",
|
||||
BoardID: "",
|
||||
ModifiedBy: userID,
|
||||
}
|
||||
|
||||
err := store.InsertBlock(&block, "user-id-1")
|
||||
err := store.InsertBlock(block, "user-id-1")
|
||||
require.Error(t, err)
|
||||
|
||||
blocks, err := store.GetBlocksForBoard(boardID)
|
||||
@ -115,14 +115,14 @@ func testInsertBlock(t *testing.T, store store.Store) {
|
||||
})
|
||||
|
||||
t.Run("invalid fields data", func(t *testing.T) {
|
||||
block := model.Block{
|
||||
block := &model.Block{
|
||||
ID: "id-test",
|
||||
BoardID: "id-test",
|
||||
ModifiedBy: userID,
|
||||
Fields: map[string]interface{}{"no-serialiable-value": t.Run},
|
||||
}
|
||||
|
||||
err := store.InsertBlock(&block, "user-id-1")
|
||||
err := store.InsertBlock(block, "user-id-1")
|
||||
require.Error(t, err)
|
||||
|
||||
blocks, err := store.GetBlocksForBoard(boardID)
|
||||
@ -131,24 +131,24 @@ func testInsertBlock(t *testing.T, store store.Store) {
|
||||
})
|
||||
|
||||
t.Run("insert new block", func(t *testing.T) {
|
||||
block := model.Block{
|
||||
block := &model.Block{
|
||||
BoardID: testBoardID,
|
||||
}
|
||||
|
||||
err := store.InsertBlock(&block, "user-id-2")
|
||||
err := store.InsertBlock(block, "user-id-2")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "user-id-2", block.CreatedBy)
|
||||
})
|
||||
|
||||
t.Run("update existing block", func(t *testing.T) {
|
||||
block := model.Block{
|
||||
block := &model.Block{
|
||||
ID: "id-2",
|
||||
BoardID: "board-id-1",
|
||||
Title: "Old Title",
|
||||
}
|
||||
|
||||
// inserting
|
||||
err := store.InsertBlock(&block, "user-id-2")
|
||||
err := store.InsertBlock(block, "user-id-2")
|
||||
require.NoError(t, err)
|
||||
|
||||
// created by populated from user id for new blocks
|
||||
@ -159,13 +159,13 @@ func testInsertBlock(t *testing.T, store store.Store) {
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
|
||||
// updating
|
||||
newBlock := model.Block{
|
||||
newBlock := &model.Block{
|
||||
ID: "id-2",
|
||||
BoardID: "board-id-1",
|
||||
CreatedBy: "user-id-3",
|
||||
Title: "New Title",
|
||||
}
|
||||
err = store.InsertBlock(&newBlock, "user-id-4")
|
||||
err = store.InsertBlock(newBlock, "user-id-4")
|
||||
require.NoError(t, err)
|
||||
// created by is not altered for existing blocks
|
||||
require.Equal(t, "user-id-3", newBlock.CreatedBy)
|
||||
@ -179,7 +179,7 @@ func testInsertBlock(t *testing.T, store store.Store) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
t.Run("data tamper attempt", func(t *testing.T) {
|
||||
block := model.Block{
|
||||
block := &model.Block{
|
||||
ID: "id-10",
|
||||
BoardID: "board-id-1",
|
||||
Title: "Old Title",
|
||||
@ -190,7 +190,7 @@ func testInsertBlock(t *testing.T, store store.Store) {
|
||||
}
|
||||
|
||||
// inserting
|
||||
err := store.InsertBlock(&block, "user-id-1")
|
||||
err := store.InsertBlock(block, "user-id-1")
|
||||
require.NoError(t, err)
|
||||
expectedTime := time.Now()
|
||||
|
||||
@ -213,19 +213,19 @@ func testInsertBlocks(t *testing.T, store store.Store) {
|
||||
initialCount := len(blocks)
|
||||
|
||||
t.Run("invalid block", func(t *testing.T) {
|
||||
validBlock := model.Block{
|
||||
validBlock := &model.Block{
|
||||
ID: "id-test",
|
||||
BoardID: "id-test",
|
||||
ModifiedBy: userID,
|
||||
}
|
||||
|
||||
invalidBlock := model.Block{
|
||||
invalidBlock := &model.Block{
|
||||
ID: "id-test",
|
||||
BoardID: "",
|
||||
ModifiedBy: userID,
|
||||
}
|
||||
|
||||
newBlocks := []model.Block{validBlock, invalidBlock}
|
||||
newBlocks := []*model.Block{validBlock, invalidBlock}
|
||||
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
err := store.InsertBlocks(newBlocks, "user-id-1")
|
||||
@ -242,7 +242,7 @@ func testPatchBlock(t *testing.T, store store.Store) {
|
||||
userID := testUserID
|
||||
boardID := "board-id-1"
|
||||
|
||||
block := model.Block{
|
||||
block := &model.Block{
|
||||
ID: "id-test",
|
||||
BoardID: boardID,
|
||||
Title: "oldTitle",
|
||||
@ -250,7 +250,7 @@ func testPatchBlock(t *testing.T, store store.Store) {
|
||||
Fields: map[string]interface{}{"test": "test value", "test2": "test value 2"},
|
||||
}
|
||||
|
||||
err := store.InsertBlock(&block, "user-id-1")
|
||||
err := store.InsertBlock(block, "user-id-1")
|
||||
require.NoError(t, err)
|
||||
|
||||
blocks, errBlocks := store.GetBlocksForBoard(boardID)
|
||||
@ -269,11 +269,11 @@ func testPatchBlock(t *testing.T, store store.Store) {
|
||||
})
|
||||
|
||||
t.Run("invalid fields data", func(t *testing.T) {
|
||||
blockPatch := model.BlockPatch{
|
||||
blockPatch := &model.BlockPatch{
|
||||
UpdatedFields: map[string]interface{}{"no-serialiable-value": t.Run},
|
||||
}
|
||||
|
||||
err := store.PatchBlock("id-test", &blockPatch, "user-id-1")
|
||||
err := store.PatchBlock("id-test", blockPatch, "user-id-1")
|
||||
require.Error(t, err)
|
||||
|
||||
blocks, err := store.GetBlocksForBoard(boardID)
|
||||
@ -303,7 +303,7 @@ func testPatchBlock(t *testing.T, store store.Store) {
|
||||
})
|
||||
|
||||
t.Run("update block custom fields", func(t *testing.T) {
|
||||
blockPatch := model.BlockPatch{
|
||||
blockPatch := &model.BlockPatch{
|
||||
UpdatedFields: map[string]interface{}{"test": "new test value", "test3": "new value"},
|
||||
}
|
||||
|
||||
@ -311,7 +311,7 @@ func testPatchBlock(t *testing.T, store store.Store) {
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
|
||||
// inserting
|
||||
err := store.PatchBlock("id-test", &blockPatch, "user-id-2")
|
||||
err := store.PatchBlock("id-test", blockPatch, "user-id-2")
|
||||
require.NoError(t, err)
|
||||
|
||||
retrievedBlock, err := store.GetBlock("id-test")
|
||||
@ -325,7 +325,7 @@ func testPatchBlock(t *testing.T, store store.Store) {
|
||||
})
|
||||
|
||||
t.Run("remove block custom fields", func(t *testing.T) {
|
||||
blockPatch := model.BlockPatch{
|
||||
blockPatch := &model.BlockPatch{
|
||||
DeletedFields: []string{"test", "test3", "test100"},
|
||||
}
|
||||
|
||||
@ -333,7 +333,7 @@ func testPatchBlock(t *testing.T, store store.Store) {
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
|
||||
// inserting
|
||||
err := store.PatchBlock("id-test", &blockPatch, "user-id-2")
|
||||
err := store.PatchBlock("id-test", blockPatch, "user-id-2")
|
||||
require.NoError(t, err)
|
||||
|
||||
retrievedBlock, err := store.GetBlock("id-test")
|
||||
@ -348,19 +348,19 @@ func testPatchBlock(t *testing.T, store store.Store) {
|
||||
}
|
||||
|
||||
func testPatchBlocks(t *testing.T, store store.Store) {
|
||||
block := model.Block{
|
||||
block := &model.Block{
|
||||
ID: "id-test",
|
||||
BoardID: "id-test",
|
||||
Title: "oldTitle",
|
||||
}
|
||||
|
||||
block2 := model.Block{
|
||||
block2 := &model.Block{
|
||||
ID: "id-test2",
|
||||
BoardID: "id-test2",
|
||||
Title: "oldTitle2",
|
||||
}
|
||||
|
||||
insertBlocks := []model.Block{block, block2}
|
||||
insertBlocks := []*model.Block{block, block2}
|
||||
err := store.InsertBlocks(insertBlocks, "user-id-1")
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -419,7 +419,7 @@ func testPatchBlocks(t *testing.T, store store.Store) {
|
||||
}
|
||||
|
||||
var (
|
||||
subtreeSampleBlocks = []model.Block{
|
||||
subtreeSampleBlocks = []*model.Block{
|
||||
{
|
||||
ID: "parent",
|
||||
BoardID: testBoardID,
|
||||
@ -504,7 +504,7 @@ func testDeleteBlock(t *testing.T, store store.Store) {
|
||||
require.NoError(t, err)
|
||||
initialCount := len(blocks)
|
||||
|
||||
blocksToInsert := []model.Block{
|
||||
blocksToInsert := []*model.Block{
|
||||
{
|
||||
ID: "block1",
|
||||
BoardID: boardID,
|
||||
@ -562,7 +562,7 @@ func testUndeleteBlock(t *testing.T, store store.Store) {
|
||||
require.NoError(t, err)
|
||||
initialCount := len(blocks)
|
||||
|
||||
blocksToInsert := []model.Block{
|
||||
blocksToInsert := []*model.Block{
|
||||
{
|
||||
ID: "block1",
|
||||
BoardID: boardID,
|
||||
@ -654,7 +654,7 @@ func testGetBlocks(t *testing.T, store store.Store) {
|
||||
blocks, err := store.GetBlocksForBoard(boardID)
|
||||
require.NoError(t, err)
|
||||
|
||||
blocksToInsert := []model.Block{
|
||||
blocksToInsert := []*model.Block{
|
||||
{
|
||||
ID: "block1",
|
||||
BoardID: boardID,
|
||||
@ -785,13 +785,13 @@ func testGetBlocks(t *testing.T, store store.Store) {
|
||||
|
||||
func testGetBlock(t *testing.T, store store.Store) {
|
||||
t.Run("get a block", func(t *testing.T) {
|
||||
block := model.Block{
|
||||
block := &model.Block{
|
||||
ID: "block-id-10",
|
||||
BoardID: "board-id-1",
|
||||
ModifiedBy: "user-id-1",
|
||||
}
|
||||
|
||||
err := store.InsertBlock(&block, "user-id-1")
|
||||
err := store.InsertBlock(block, "user-id-1")
|
||||
require.NoError(t, err)
|
||||
|
||||
fetchedBlock, err := store.GetBlock("block-id-10")
|
||||
@ -816,14 +816,14 @@ func testGetBlock(t *testing.T, store store.Store) {
|
||||
func testDuplicateBlock(t *testing.T, store store.Store) {
|
||||
blocksToInsert := subtreeSampleBlocks
|
||||
blocksToInsert = append(blocksToInsert,
|
||||
model.Block{
|
||||
&model.Block{
|
||||
ID: "grandchild1a",
|
||||
BoardID: testBoardID,
|
||||
ParentID: "child1",
|
||||
ModifiedBy: testUserID,
|
||||
Type: model.TypeComment,
|
||||
},
|
||||
model.Block{
|
||||
&model.Block{
|
||||
ID: "grandchild2a",
|
||||
BoardID: testBoardID,
|
||||
ParentID: "child2",
|
||||
@ -874,7 +874,7 @@ func testGetBlockMetadata(t *testing.T, store store.Store) {
|
||||
blocks, err := store.GetBlocksForBoard(boardID)
|
||||
require.NoError(t, err)
|
||||
|
||||
blocksToInsert := []model.Block{
|
||||
blocksToInsert := []*model.Block{
|
||||
{
|
||||
ID: "block1",
|
||||
BoardID: boardID,
|
||||
@ -914,7 +914,7 @@ func testGetBlockMetadata(t *testing.T, store store.Store) {
|
||||
|
||||
for _, v := range blocksToInsert {
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
subBlocks := []model.Block{v}
|
||||
subBlocks := []*model.Block{v}
|
||||
InsertBlocks(t, store, subBlocks, testUserID)
|
||||
}
|
||||
defer DeleteBlocks(t, store, blocksToInsert, "test")
|
||||
|
@ -31,7 +31,7 @@ func getBoardsInsightsTest(t *testing.T, store store.Store) {
|
||||
{ID: "board-id-2", TeamID: teamID, Type: model.BoardTypePrivate},
|
||||
{ID: "board-id-3", TeamID: teamID, Type: model.BoardTypeOpen},
|
||||
},
|
||||
Blocks: []model.Block{
|
||||
Blocks: []*model.Block{
|
||||
{ID: "block-id-1", BoardID: "board-id-1", Type: model.TypeCard},
|
||||
{ID: "block-id-2", BoardID: "board-id-2", Type: model.TypeCard},
|
||||
{ID: "block-id-3", BoardID: "board-id-1", Type: model.TypeCard},
|
||||
@ -52,7 +52,7 @@ func getBoardsInsightsTest(t *testing.T, store store.Store) {
|
||||
require.NotNil(t, bab)
|
||||
|
||||
newBab = &model.BoardsAndBlocks{
|
||||
Blocks: []model.Block{
|
||||
Blocks: []*model.Block{
|
||||
{ID: "block-id-13", BoardID: "board-id-1", Type: model.TypeCard},
|
||||
{ID: "block-id-14", BoardID: "board-id-1", Type: model.TypeCard},
|
||||
},
|
||||
|
@ -51,7 +51,7 @@ func testCreateBoardsAndBlocks(t *testing.T, store store.Store) {
|
||||
{ID: "board-id-2", TeamID: teamID, Type: model.BoardTypePrivate},
|
||||
{ID: "board-id-3", TeamID: teamID, Type: model.BoardTypeOpen},
|
||||
},
|
||||
Blocks: []model.Block{
|
||||
Blocks: []*model.Block{
|
||||
{ID: "block-id-1", BoardID: "board-id-1", Type: model.TypeCard},
|
||||
{ID: "block-id-2", BoardID: "board-id-2", Type: model.TypeCard},
|
||||
},
|
||||
@ -84,7 +84,7 @@ func testCreateBoardsAndBlocks(t *testing.T, store store.Store) {
|
||||
{ID: "board-id-5", TeamID: teamID, Type: model.BoardTypePrivate},
|
||||
{ID: "board-id-6", TeamID: teamID, Type: model.BoardTypeOpen},
|
||||
},
|
||||
Blocks: []model.Block{
|
||||
Blocks: []*model.Block{
|
||||
{ID: "block-id-3", BoardID: "board-id-4", Type: model.TypeCard},
|
||||
{ID: "block-id-4", BoardID: "board-id-5", Type: model.TypeCard},
|
||||
},
|
||||
@ -126,7 +126,7 @@ func testCreateBoardsAndBlocks(t *testing.T, store store.Store) {
|
||||
{ID: "board-id-8", TeamID: teamID, Type: model.BoardTypePrivate},
|
||||
{ID: "board-id-9", TeamID: teamID, Type: model.BoardTypeOpen},
|
||||
},
|
||||
Blocks: []model.Block{
|
||||
Blocks: []*model.Block{
|
||||
{ID: "block-id-5", BoardID: "board-id-7", Type: model.TypeCard},
|
||||
{ID: "block-id-6", BoardID: "", Type: model.TypeCard},
|
||||
},
|
||||
@ -164,12 +164,12 @@ func testPatchBoardsAndBlocks(t *testing.T, store store.Store) {
|
||||
_, err := store.InsertBoard(board, userID)
|
||||
require.NoError(t, err)
|
||||
|
||||
block := model.Block{
|
||||
block := &model.Block{
|
||||
ID: "block-id-1",
|
||||
BoardID: "board-id-1",
|
||||
Title: initialTitle,
|
||||
}
|
||||
require.NoError(t, store.InsertBlock(&block, userID))
|
||||
require.NoError(t, store.InsertBlock(block, userID))
|
||||
|
||||
// apply the patches
|
||||
pbab := &model.PatchBoardsAndBlocks{
|
||||
@ -207,7 +207,7 @@ func testPatchBoardsAndBlocks(t *testing.T, store store.Store) {
|
||||
{ID: "board-id-2", TeamID: teamID, Type: model.BoardTypePrivate},
|
||||
{ID: "board-id-3", Title: "initial title", TeamID: teamID, Type: model.BoardTypeOpen},
|
||||
},
|
||||
Blocks: []model.Block{
|
||||
Blocks: []*model.Block{
|
||||
{ID: "block-id-1", Title: "initial title", BoardID: "board-id-1", Type: model.TypeCard},
|
||||
{ID: "block-id-2", Schema: 1, BoardID: "board-id-2", Type: model.TypeCard},
|
||||
},
|
||||
@ -586,7 +586,7 @@ func testDuplicateBoard(t *testing.T, store store.Store) {
|
||||
{ID: "board-id-2", TeamID: teamID, Type: model.BoardTypePrivate},
|
||||
{ID: "board-id-3", TeamID: teamID, Type: model.BoardTypeOpen},
|
||||
},
|
||||
Blocks: []model.Block{
|
||||
Blocks: []*model.Block{
|
||||
{ID: "block-id-1", BoardID: "board-id-1", Type: model.TypeCard},
|
||||
{ID: "block-id-1a", BoardID: "board-id-1", Type: model.TypeComment},
|
||||
{ID: "block-id-2", BoardID: "board-id-2", Type: model.TypeCard},
|
||||
|
@ -61,24 +61,24 @@ func testGetUsedCardsCount(t *testing.T, store storeservice.Store) {
|
||||
|
||||
// board 1 has three cards
|
||||
for _, cardID := range []string{"card1", "card2", "card3"} {
|
||||
card := model.Block{
|
||||
card := &model.Block{
|
||||
ID: cardID,
|
||||
ParentID: "board1",
|
||||
BoardID: "board1",
|
||||
Type: model.TypeCard,
|
||||
}
|
||||
require.NoError(t, store.InsertBlock(&card, userID))
|
||||
require.NoError(t, store.InsertBlock(card, userID))
|
||||
}
|
||||
|
||||
// board 2 has two cards
|
||||
for _, cardID := range []string{"card4", "card5"} {
|
||||
card := model.Block{
|
||||
card := &model.Block{
|
||||
ID: cardID,
|
||||
ParentID: "board2",
|
||||
BoardID: "board2",
|
||||
Type: model.TypeCard,
|
||||
}
|
||||
require.NoError(t, store.InsertBlock(&card, userID))
|
||||
require.NoError(t, store.InsertBlock(card, userID))
|
||||
}
|
||||
|
||||
count, err := store.GetUsedCardsCount()
|
||||
@ -88,21 +88,21 @@ func testGetUsedCardsCount(t *testing.T, store storeservice.Store) {
|
||||
|
||||
t.Run("should not take into account content blocks", func(t *testing.T) {
|
||||
// we add a couple of content blocks
|
||||
text := model.Block{
|
||||
text := &model.Block{
|
||||
ID: "text-id",
|
||||
ParentID: "card1",
|
||||
BoardID: "board1",
|
||||
Type: model.TypeText,
|
||||
}
|
||||
require.NoError(t, store.InsertBlock(&text, userID))
|
||||
require.NoError(t, store.InsertBlock(text, userID))
|
||||
|
||||
view := model.Block{
|
||||
view := &model.Block{
|
||||
ID: "view-id",
|
||||
ParentID: "board1",
|
||||
BoardID: "board1",
|
||||
Type: model.TypeView,
|
||||
}
|
||||
require.NoError(t, store.InsertBlock(&view, userID))
|
||||
require.NoError(t, store.InsertBlock(view, userID))
|
||||
|
||||
// and count should not change
|
||||
count, err := store.GetUsedCardsCount()
|
||||
@ -113,7 +113,7 @@ func testGetUsedCardsCount(t *testing.T, store storeservice.Store) {
|
||||
t.Run("should not take into account cards belonging to templates", func(t *testing.T) {
|
||||
// we add a template with cards
|
||||
templateID := "template-id"
|
||||
boardTemplate := model.Block{
|
||||
boardTemplate := &model.Block{
|
||||
ID: templateID,
|
||||
BoardID: templateID,
|
||||
Type: model.TypeBoard,
|
||||
@ -121,16 +121,16 @@ func testGetUsedCardsCount(t *testing.T, store storeservice.Store) {
|
||||
"isTemplate": true,
|
||||
},
|
||||
}
|
||||
require.NoError(t, store.InsertBlock(&boardTemplate, userID))
|
||||
require.NoError(t, store.InsertBlock(boardTemplate, userID))
|
||||
|
||||
for _, cardID := range []string{"card6", "card7", "card8"} {
|
||||
card := model.Block{
|
||||
card := &model.Block{
|
||||
ID: cardID,
|
||||
ParentID: templateID,
|
||||
BoardID: templateID,
|
||||
Type: model.TypeCard,
|
||||
}
|
||||
require.NoError(t, store.InsertBlock(&card, userID))
|
||||
require.NoError(t, store.InsertBlock(card, userID))
|
||||
}
|
||||
|
||||
// and count should still be the same
|
||||
@ -141,14 +141,14 @@ func testGetUsedCardsCount(t *testing.T, store storeservice.Store) {
|
||||
|
||||
t.Run("should not take into account deleted cards", func(t *testing.T) {
|
||||
// we create a ninth card on the first board
|
||||
card9 := model.Block{
|
||||
card9 := &model.Block{
|
||||
ID: "card9",
|
||||
ParentID: "board1",
|
||||
BoardID: "board1",
|
||||
Type: model.TypeCard,
|
||||
DeleteAt: utils.GetMillis(),
|
||||
}
|
||||
require.NoError(t, store.InsertBlock(&card9, userID))
|
||||
require.NoError(t, store.InsertBlock(card9, userID))
|
||||
|
||||
// and count should still be the same
|
||||
count, err := store.GetUsedCardsCount()
|
||||
@ -215,25 +215,25 @@ func testUpdateCardLimitTimestamp(t *testing.T, store storeservice.Store) {
|
||||
|
||||
// board 1 has five cards
|
||||
for _, cardID := range []string{"card1", "card2", "card3", "card4", "card5"} {
|
||||
card := model.Block{
|
||||
card := &model.Block{
|
||||
ID: cardID,
|
||||
ParentID: "board1",
|
||||
BoardID: "board1",
|
||||
Type: model.TypeCard,
|
||||
}
|
||||
require.NoError(t, store.InsertBlock(&card, userID))
|
||||
require.NoError(t, store.InsertBlock(card, userID))
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
// board 2 has five cards
|
||||
for _, cardID := range []string{"card6", "card7", "card8", "card9", "card10"} {
|
||||
card := model.Block{
|
||||
card := &model.Block{
|
||||
ID: cardID,
|
||||
ParentID: "board2",
|
||||
BoardID: "board2",
|
||||
Type: model.TypeCard,
|
||||
}
|
||||
require.NoError(t, store.InsertBlock(&card, userID))
|
||||
require.NoError(t, store.InsertBlock(card, userID))
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
|
@ -48,30 +48,30 @@ func LoadData(t *testing.T, store store.Store) {
|
||||
board, err := store.InsertBoard(&validBoard, testUserID)
|
||||
require.NoError(t, err)
|
||||
|
||||
validBlock := model.Block{
|
||||
validBlock := &model.Block{
|
||||
ID: "id-test",
|
||||
BoardID: board.ID,
|
||||
ModifiedBy: testUserID,
|
||||
}
|
||||
|
||||
validBlock2 := model.Block{
|
||||
validBlock2 := &model.Block{
|
||||
ID: "id-test2",
|
||||
BoardID: board.ID,
|
||||
ModifiedBy: testUserID,
|
||||
}
|
||||
validBlock3 := model.Block{
|
||||
validBlock3 := &model.Block{
|
||||
ID: "id-test3",
|
||||
BoardID: board.ID,
|
||||
ModifiedBy: testUserID,
|
||||
}
|
||||
|
||||
validBlock4 := model.Block{
|
||||
validBlock4 := &model.Block{
|
||||
ID: "id-test4",
|
||||
BoardID: board.ID,
|
||||
ModifiedBy: testUserID,
|
||||
}
|
||||
|
||||
newBlocks := []model.Block{validBlock, validBlock2, validBlock3, validBlock4}
|
||||
newBlocks := []*model.Block{validBlock, validBlock2, validBlock3, validBlock4}
|
||||
|
||||
err = store.InsertBlocks(newBlocks, testUserID)
|
||||
require.NoError(t, err)
|
||||
|
@ -8,21 +8,21 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func InsertBlocks(t *testing.T, s store.Store, blocks []model.Block, userID string) {
|
||||
func InsertBlocks(t *testing.T, s store.Store, blocks []*model.Block, userID string) {
|
||||
for i := range blocks {
|
||||
err := s.InsertBlock(&blocks[i], userID)
|
||||
err := s.InsertBlock(blocks[i], userID)
|
||||
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, blocks []*model.Block, modifiedBy string) {
|
||||
for _, block := range blocks {
|
||||
err := s.DeleteBlock(block.ID, modifiedBy)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
func ContainsBlockWithID(blocks []model.Block, blockID string) bool {
|
||||
func ContainsBlockWithID(blocks []*model.Block, blockID string) bool {
|
||||
for _, block := range blocks {
|
||||
if block.ID == blockID {
|
||||
return true
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
// NotifyUpdate calls webhooks.
|
||||
func (wh *Client) NotifyUpdate(block model.Block) {
|
||||
func (wh *Client) NotifyUpdate(block *model.Block) {
|
||||
if len(wh.config.WebhookUpdate) < 1 {
|
||||
return
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ func TestClientUpdateNotify(t *testing.T) {
|
||||
|
||||
client := NewClient(cfg, logger)
|
||||
|
||||
client.NotifyUpdate(model.Block{})
|
||||
client.NotifyUpdate(&model.Block{})
|
||||
|
||||
if !isNotified {
|
||||
t.Error("webhook url not be notified")
|
||||
|
@ -28,7 +28,7 @@ type Store interface {
|
||||
}
|
||||
|
||||
type Adapter interface {
|
||||
BroadcastBlockChange(teamID string, block model.Block)
|
||||
BroadcastBlockChange(teamID string, block *model.Block)
|
||||
BroadcastBlockDelete(teamID, blockID, boardID string)
|
||||
BroadcastBoardChange(teamID string, board *model.Board)
|
||||
BroadcastBoardDelete(teamID, boardID string)
|
||||
|
@ -14,9 +14,9 @@ type UpdateCategoryMessage struct {
|
||||
|
||||
// UpdateBlockMsg is sent on block updates.
|
||||
type UpdateBlockMsg struct {
|
||||
Action string `json:"action"`
|
||||
TeamID string `json:"teamId"`
|
||||
Block model.Block `json:"block"`
|
||||
Action string `json:"action"`
|
||||
TeamID string `json:"teamId"`
|
||||
Block *model.Block `json:"block"`
|
||||
}
|
||||
|
||||
// UpdateBoardMsg is sent on block updates.
|
||||
|
@ -26,7 +26,7 @@ type PluginAdapterInterface interface {
|
||||
OnWebSocketDisconnect(webConnID, userID string)
|
||||
WebSocketMessageHasBeenPosted(webConnID, userID string, req *mmModel.WebSocketRequest)
|
||||
BroadcastConfigChange(clientConfig model.ClientConfig)
|
||||
BroadcastBlockChange(teamID string, block model.Block)
|
||||
BroadcastBlockChange(teamID string, block *model.Block)
|
||||
BroadcastBlockDelete(teamID, blockID, parentID string)
|
||||
BroadcastSubscriptionChange(teamID string, subscription *model.Subscription)
|
||||
BroadcastCardLimitTimestampChange(cardLimitTimestamp int64)
|
||||
@ -453,7 +453,7 @@ func (pa *PluginAdapter) sendBoardMessage(teamID, boardID string, payload map[st
|
||||
pa.sendBoardMessageSkipCluster(teamID, boardID, payload, ensureUserIDs...)
|
||||
}
|
||||
|
||||
func (pa *PluginAdapter) BroadcastBlockChange(teamID string, block model.Block) {
|
||||
func (pa *PluginAdapter) BroadcastBlockChange(teamID string, block *model.Block) {
|
||||
pa.logger.Debug("BroadcastingBlockChange",
|
||||
mlog.String("teamID", teamID),
|
||||
mlog.String("boardID", block.BoardID),
|
||||
@ -527,7 +527,7 @@ func (pa *PluginAdapter) BroadcastCategoryBoardChange(teamID, userID string, boa
|
||||
|
||||
func (pa *PluginAdapter) BroadcastBlockDelete(teamID, blockID, boardID string) {
|
||||
now := utils.GetMillis()
|
||||
block := model.Block{}
|
||||
block := &model.Block{}
|
||||
block.ID = blockID
|
||||
block.BoardID = boardID
|
||||
block.UpdateAt = now
|
||||
|
@ -511,7 +511,7 @@ func (ws *Server) getListenersForTeamAndBoard(teamID, boardID string, ensureUser
|
||||
// BroadcastBlockDelete broadcasts delete messages to clients.
|
||||
func (ws *Server) BroadcastBlockDelete(teamID, blockID, boardID string) {
|
||||
now := utils.GetMillis()
|
||||
block := model.Block{}
|
||||
block := &model.Block{}
|
||||
block.ID = blockID
|
||||
block.BoardID = boardID
|
||||
block.UpdateAt = now
|
||||
@ -521,7 +521,7 @@ func (ws *Server) BroadcastBlockDelete(teamID, blockID, boardID string) {
|
||||
}
|
||||
|
||||
// BroadcastBlockChange broadcasts update messages to clients.
|
||||
func (ws *Server) BroadcastBlockChange(teamID string, block model.Block) {
|
||||
func (ws *Server) BroadcastBlockChange(teamID string, block *model.Block) {
|
||||
blockIDsToNotify := []string{block.ID, block.ParentID}
|
||||
|
||||
message := UpdateBlockMsg{
|
||||
|
@ -257,6 +257,8 @@
|
||||
"SidebarTour.SidebarCategories.Body": "All your boards are now organized under your new sidebar. No more switching between workspaces. One-time custom categories based on your prior workspaces may have automatically been created for you as part of your v7.2 upgrade. These can be removed or edited to your preference.",
|
||||
"SidebarTour.SidebarCategories.Link": "Learn more",
|
||||
"SidebarTour.SidebarCategories.Title": "Sidebar categories",
|
||||
"SiteStats.total_boards": "Total Boards",
|
||||
"SiteStats.total_cards": "Total Cards",
|
||||
"TableComponent.add-icon": "Add icon",
|
||||
"TableComponent.name": "Name",
|
||||
"TableComponent.plus-new": "+ New",
|
||||
|
@ -4,7 +4,7 @@
|
||||
import React, {useState, useEffect} from 'react'
|
||||
|
||||
import {useIntl, FormattedMessage} from 'react-intl'
|
||||
import {generatePath, useRouteMatch} from 'react-router'
|
||||
import {generatePath, useRouteMatch} from 'react-router-dom'
|
||||
import Select from 'react-select/async'
|
||||
import {CSSObject} from '@emotion/serialize'
|
||||
|
||||
|
@ -65,6 +65,7 @@ type Props = {
|
||||
|
||||
const ViewHeader = (props: Props) => {
|
||||
const [showFilter, setShowFilter] = useState(false)
|
||||
const [lockFilterOnClose, setLockFilterOnClose] = useState(false)
|
||||
const intl = useIntl()
|
||||
const canEditBoardProperties = useHasCurrentBoardPermissions([Permission.ManageBoardProperties])
|
||||
|
||||
@ -197,7 +198,9 @@ const ViewHeader = (props: Props) => {
|
||||
<ModalWrapper>
|
||||
<Button
|
||||
active={hasFilter}
|
||||
onClick={() => setShowFilter(true)}
|
||||
onClick={() => setShowFilter(!showFilter)}
|
||||
onMouseOver={() => setLockFilterOnClose(true)}
|
||||
onMouseLeave={() => setLockFilterOnClose(false)}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='ViewHeader.filter'
|
||||
@ -208,7 +211,11 @@ const ViewHeader = (props: Props) => {
|
||||
<FilterComponent
|
||||
board={board}
|
||||
activeView={activeView}
|
||||
onClose={() => setShowFilter(false)}
|
||||
onClose={() => {
|
||||
if (!lockFilterOnClose) {
|
||||
setShowFilter(false)
|
||||
}
|
||||
}}
|
||||
/>}
|
||||
</ModalWrapper>
|
||||
|
||||
|
@ -17,6 +17,7 @@ import {Constants} from './constants'
|
||||
|
||||
import {BoardsCloudLimits} from './boardsCloudLimits'
|
||||
import {TopBoardResponse} from './insights'
|
||||
import {BoardSiteStatistics} from './statistics'
|
||||
|
||||
//
|
||||
// OctoClient is the client interface to the server APIs
|
||||
@ -921,6 +922,18 @@ class OctoClient {
|
||||
return limits
|
||||
}
|
||||
|
||||
async getSiteStatistics(): Promise<BoardSiteStatistics | undefined> {
|
||||
const path = '/api/v2/statistics'
|
||||
const response = await fetch(this.getBaseURL() + path, {headers: this.headers()})
|
||||
if (response.status !== 200) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const stats = (await this.getJson(response, {})) as BoardSiteStatistics
|
||||
Utils.log(`Site Statistics: cards=${stats.card_count} boards=${stats.board_count}`)
|
||||
return stats
|
||||
}
|
||||
|
||||
// insights
|
||||
async getMyTopBoards(timeRange: string, page: number, perPage: number, teamId: string): Promise<TopBoardResponse | undefined> {
|
||||
const path = `/api/v2/users/me/boards/insights?time_range=${timeRange}&page=${page}&per_page=${perPage}&team_id=${teamId}`
|
||||
|
7
webapp/src/statistics/index.ts
Normal file
7
webapp/src/statistics/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export interface BoardSiteStatistics {
|
||||
board_count: number
|
||||
card_count: number
|
||||
}
|
@ -7,7 +7,13 @@ import {default as client} from '../octoClient'
|
||||
import {Board, BoardMember} from '../blocks/board'
|
||||
import {IUser} from '../user'
|
||||
|
||||
import {initialLoad, initialReadOnlyLoad, loadBoardData, loadBoards} from './initialLoad'
|
||||
import {
|
||||
initialLoad,
|
||||
initialReadOnlyLoad,
|
||||
loadBoardData,
|
||||
loadBoards,
|
||||
loadMyBoardsMemberships,
|
||||
} from './initialLoad'
|
||||
|
||||
import {addBoardUsers, removeBoardUsersById, setBoardUsers} from './users'
|
||||
|
||||
@ -191,6 +197,12 @@ const boardsSlice = createSlice({
|
||||
state.boards[board.id] = board
|
||||
})
|
||||
})
|
||||
builder.addCase(loadMyBoardsMemberships.fulfilled, (state, action) => {
|
||||
state.myBoardMemberships = {}
|
||||
action.payload.boardsMemberships.forEach((boardMember) => {
|
||||
state.myBoardMemberships[boardMember.boardId] = boardMember
|
||||
})
|
||||
})
|
||||
builder.addCase(fetchBoardMembers.fulfilled, (state, action) => {
|
||||
if (action.payload.length === 0) {
|
||||
return
|
||||
|
@ -81,6 +81,16 @@ export const loadBoards = createAsyncThunk(
|
||||
},
|
||||
)
|
||||
|
||||
export const loadMyBoardsMemberships = createAsyncThunk(
|
||||
'loadMyBoardsMemberships',
|
||||
async () => {
|
||||
const boardsMemberships = await client.getMyBoardMemberships()
|
||||
return {
|
||||
boardsMemberships,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
export const getUserBlockSubscriptions = (state: RootState): Subscription[] => state.users.blockSubscriptions
|
||||
|
||||
export const getUserBlockSubscriptionList = createSelector(
|
||||
|
@ -7,6 +7,8 @@ import {Utils} from '../../utils'
|
||||
|
||||
type Props = {
|
||||
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void
|
||||
onMouseOver?: (e: React.MouseEvent<HTMLButtonElement>) => void
|
||||
onMouseLeave?: (e: React.MouseEvent<HTMLButtonElement>) => void
|
||||
onBlur?: (e: React.FocusEvent<HTMLButtonElement>) => void
|
||||
children?: React.ReactNode
|
||||
title?: string
|
||||
@ -37,6 +39,8 @@ function Button(props: Props): JSX.Element {
|
||||
<button
|
||||
type={props.submit ? 'submit' : 'button'}
|
||||
onClick={props.onClick}
|
||||
onMouseOver={props.onMouseOver}
|
||||
onMouseLeave={props.onMouseLeave}
|
||||
className={Utils.generateClassName(classNames)}
|
||||
title={props.title}
|
||||
onBlur={props.onBlur}
|
||||
|
@ -1,7 +1,7 @@
|
||||
.GuestBadge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin: 0 0 0 4px;
|
||||
margin: 0 10px 0 4px;
|
||||
}
|
||||
|
||||
.GuestBadge__box {
|
||||
|
Loading…
Reference in New Issue
Block a user