mirror of
https://github.com/mattermost/focalboard.git
synced 2024-11-24 08:22:29 +02:00
Merge branch 'main' into MM-47238_boards-dev-server-url
This commit is contained in:
commit
1d5cf123c9
19
.github/workflows/ci.yml
vendored
19
.github/workflows/ci.yml
vendored
@ -9,7 +9,7 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
|
||||
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
|
||||
EXCLUDE_ENTERPRISE: true
|
||||
|
||||
jobs:
|
||||
@ -22,6 +22,7 @@ jobs:
|
||||
db:
|
||||
- sqlite
|
||||
- mysql
|
||||
- mariadb
|
||||
- postgres
|
||||
|
||||
steps:
|
||||
@ -34,14 +35,14 @@ jobs:
|
||||
continue-on-error: true
|
||||
with:
|
||||
repository: "mattermost/mattermost-server"
|
||||
fetch-depth: "20"
|
||||
fetch-depth: "20"
|
||||
path: "mattermost-server"
|
||||
ref: ${{ env.BRANCH_NAME }}
|
||||
- uses: actions/checkout@v3
|
||||
if: steps.mattermostServer.outcome == 'failure'
|
||||
with:
|
||||
repository: "mattermost/mattermost-server"
|
||||
fetch-depth: "20"
|
||||
fetch-depth: "20"
|
||||
path: "mattermost-server"
|
||||
ref : "master"
|
||||
- name: Set up Go
|
||||
@ -64,14 +65,14 @@ jobs:
|
||||
continue-on-error: true
|
||||
with:
|
||||
repository: "mattermost/mattermost-server"
|
||||
fetch-depth: "20"
|
||||
fetch-depth: "20"
|
||||
path: "mattermost-server"
|
||||
ref: ${{ env.BRANCH_NAME }}
|
||||
- uses: actions/checkout@v3
|
||||
if: steps.mattermostServer.outcome == 'failure'
|
||||
with:
|
||||
repository: "mattermost/mattermost-server"
|
||||
fetch-depth: "20"
|
||||
fetch-depth: "20"
|
||||
path: "mattermost-server"
|
||||
ref : "master"
|
||||
- name: npm ci
|
||||
@ -122,14 +123,14 @@ jobs:
|
||||
continue-on-error: true
|
||||
with:
|
||||
repository: "mattermost/mattermost-server"
|
||||
fetch-depth: "20"
|
||||
fetch-depth: "20"
|
||||
path: "mattermost-server"
|
||||
ref: ${{ env.BRANCH_NAME }}
|
||||
- uses: actions/checkout@v3
|
||||
if: steps.mattermostServer.outcome == 'failure'
|
||||
with:
|
||||
repository: "mattermost/mattermost-server"
|
||||
fetch-depth: "20"
|
||||
fetch-depth: "20"
|
||||
path: "mattermost-server"
|
||||
ref : "master"
|
||||
|
||||
@ -159,14 +160,14 @@ jobs:
|
||||
continue-on-error: true
|
||||
with:
|
||||
repository: "mattermost/mattermost-server"
|
||||
fetch-depth: "20"
|
||||
fetch-depth: "20"
|
||||
path: "mattermost-server"
|
||||
ref: ${{ env.BRANCH_NAME }}
|
||||
- uses: actions/checkout@v3
|
||||
if: steps.mattermostServer.outcome == 'failure'
|
||||
with:
|
||||
repository: "mattermost/mattermost-server"
|
||||
fetch-depth: "20"
|
||||
fetch-depth: "20"
|
||||
path: "mattermost-server"
|
||||
ref : "master"
|
||||
|
||||
|
16
Makefile
16
Makefile
@ -126,7 +126,7 @@ watch-single-user: modd-precheck ## Run both server and webapp in single user mo
|
||||
watch-server-test: modd-precheck ## Run server tests watching for changes
|
||||
env FOCALBOARD_BUILD_TAGS='$(BUILD_TAGS)' modd -f modd-servertest.conf
|
||||
|
||||
server-test: server-test-sqlite server-test-mysql server-test-postgres ## Run server tests
|
||||
server-test: server-test-sqlite server-test-mysql server-test-mariadb server-test-postgres ## Run server tests
|
||||
|
||||
server-test-sqlite: export FOCALBOARD_UNIT_TESTING=1
|
||||
|
||||
@ -153,6 +153,20 @@ server-test-mysql: setup-go-work ## Run server tests using mysql
|
||||
cd mattermost-plugin/server; go tool cover -func plugin-mysql-profile.coverage
|
||||
docker-compose -f ./docker-testing/docker-compose-mysql.yml down -v --remove-orphans
|
||||
|
||||
server-test-mariadb: export FOCALBOARD_UNIT_TESTING=1
|
||||
server-test-mariadb: export FOCALBOARD_STORE_TEST_DB_TYPE=mariadb
|
||||
server-test-mariadb: export FOCALBOARD_STORE_TEST_DOCKER_PORT=44445
|
||||
|
||||
server-test-mariadb: templates-archive ## Run server tests using mysql
|
||||
@echo Starting docker container for mariadb
|
||||
docker-compose -f ./docker-testing/docker-compose-mariadb.yml down -v --remove-orphans
|
||||
docker-compose -f ./docker-testing/docker-compose-mariadb.yml run start_dependencies
|
||||
cd server; go test -tags '$(BUILD_TAGS)' -race -v -coverpkg=./... -coverprofile=server-mariadb-profile.coverage -count=1 -timeout=30m ./...
|
||||
cd server; go tool cover -func server-mariadb-profile.coverage
|
||||
cd mattermost-plugin/server; go test -tags '$(BUILD_TAGS)' -race -v -coverpkg=./... -coverprofile=plugin-mariadb-profile.coverage -count=1 -timeout=30m ./...
|
||||
cd mattermost-plugin/server; go tool cover -func plugin-mariadb-profile.coverage
|
||||
docker-compose -f ./docker-testing/docker-compose-mariadb.yml down -v --remove-orphans
|
||||
|
||||
server-test-postgres: export FOCALBOARD_UNIT_TESTING=1
|
||||
server-test-postgres: export FOCALBOARD_STORE_TEST_DB_TYPE=postgres
|
||||
server-test-postgres: export FOCALBOARD_STORE_TEST_DOCKER_PORT=44446
|
||||
|
24
docker-testing/docker-compose-mariadb.yml
Normal file
24
docker-testing/docker-compose-mariadb.yml
Normal file
@ -0,0 +1,24 @@
|
||||
version: '2.4'
|
||||
services:
|
||||
mariadb:
|
||||
image: "mariadb:10.9.3"
|
||||
restart: always
|
||||
environment:
|
||||
MARIADB_ROOT_HOST: "%"
|
||||
MARIADB_ROOT_PASSWORD: mostest
|
||||
MARIADB_PASSWORD: mostest
|
||||
MARIADB_USER: mmuser
|
||||
healthcheck:
|
||||
test: ["CMD", "mariadb-admin", "ping", "-h", "localhost", "-u", "mmuser", "-pmostest"]
|
||||
interval: 5s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
tmpfs: /var/lib/mariadb
|
||||
ports:
|
||||
- 44445:3306
|
||||
|
||||
start_dependencies:
|
||||
image: mattermost/mattermost-wait-for-dep:latest
|
||||
depends_on:
|
||||
- mariadb
|
||||
command: mariadb:3306
|
@ -5,8 +5,8 @@ This node app converts an Asana json archive into a Focalboard archive. To use:
|
||||
2. Save it locally, e.g. to `asana.json`
|
||||
3. Run `npm install` from within `focalboard/webapp`
|
||||
4. Run `npm install` from within `focalboard/import/asana`
|
||||
5. Run `npx ts-node importAsana.ts -i <asana.json> -o archive.focalboard`
|
||||
6. In Focalboard, click `Settings`, then `Import archive` and select `archive.focalboard`
|
||||
5. Run `npx ts-node importAsana.ts -i <asana.json> -o archive.boardarchive`
|
||||
6. In Focalboard, click `Settings`, then `Import archive` and select `archive.boardarchive`
|
||||
|
||||
## Import scope
|
||||
|
||||
|
@ -34,7 +34,7 @@ function main() {
|
||||
const args: minimist.ParsedArgs = minimist(process.argv.slice(2))
|
||||
|
||||
const inputFile = args['i']
|
||||
const outputFile = args['o'] || 'archive.focalboard'
|
||||
const outputFile = args['o'] || 'archive.boardarchive'
|
||||
|
||||
if (!inputFile) {
|
||||
showHelp()
|
||||
@ -184,7 +184,7 @@ function convert(input: Asana): [Board[], Block[]] {
|
||||
}
|
||||
|
||||
function showHelp() {
|
||||
console.log('import -i <input.json> -o [output.focalboard]')
|
||||
console.log('import -i <input.json> -o [output.boardarchive]')
|
||||
exit(1)
|
||||
}
|
||||
|
||||
|
@ -6,8 +6,8 @@ This node app converts a Jira xml export into a Focalboard archive. To use:
|
||||
3. Save it locally, e.g. to `jira_export.xml`
|
||||
4. Run `npm install` from within `focalboard/webapp`
|
||||
5. Run `npm install` from within `focalboard/import/jira`
|
||||
6. Run `npx ts-node importJira.ts -i <path-to-jira.xml> -o archive.focalboard` (also from within `focalboard/import/jira`)
|
||||
7. In Focalboard, click `Settings`, then `Import archive` and select `archive.focalboard`
|
||||
6. Run `npx ts-node importJira.ts -i <path-to-jira.xml> -o archive.boardarchive` (also from within `focalboard/import/jira`)
|
||||
7. In Focalboard, click `Settings`, then `Import archive` and select `archive.boardarchive`
|
||||
|
||||
## Import scope and known limitations
|
||||
|
||||
|
@ -8,7 +8,7 @@ async function main() {
|
||||
const args: minimist.ParsedArgs = minimist(process.argv.slice(2))
|
||||
|
||||
const inputFile = args['i']
|
||||
const outputFile = args['o'] || 'archive.focalboard'
|
||||
const outputFile = args['o'] || 'archive.boardarchive'
|
||||
|
||||
return run(inputFile, outputFile)
|
||||
}
|
||||
|
@ -237,7 +237,7 @@ function optionForPropertyValue(cardProperty: IPropertyTemplate, propertyValue:
|
||||
}
|
||||
|
||||
function showHelp() {
|
||||
console.log('import -i <input.xml> -o [output.focalboard]')
|
||||
console.log('import -i <input.xml> -o [output.boardarchive]')
|
||||
exit(1)
|
||||
}
|
||||
|
||||
|
@ -4,10 +4,10 @@ This node app converts data from a Nextcloud Server with the [app Deck](https://
|
||||
|
||||
1. Run `npm install` from within `focalboard/webapp`
|
||||
2. Run `npm install` from within `focalboard/import/nextcloud-deck`
|
||||
3. Run `npx ts-node importDeck.ts -o archive.focalboard` (also from within `focalboard/import/nextcloud-deck`)
|
||||
3. Run `npx ts-node importDeck.ts -o archive.boardarchive` (also from within `focalboard/import/nextcloud-deck`)
|
||||
1. Enter URL and credentials (can also be provided via cli arguments)
|
||||
2. Enter ID of the board to convert
|
||||
4. In Focalboard, click `Settings`, then `Import archive` and select `archive.focalboard`
|
||||
4. In Focalboard, click `Settings`, then `Import archive` and select `archive.boardarchive`
|
||||
|
||||
## Import scope
|
||||
|
||||
|
@ -47,7 +47,7 @@ async function main() {
|
||||
const password = args['p'] ?? readline.question('Password: ', {hideEchoBack: true})
|
||||
const boardIdString = args['b']
|
||||
|
||||
const outputFile = args['o'] || 'archive.focalboard'
|
||||
const outputFile = args['o'] || 'archive.boardarchive'
|
||||
|
||||
// Create Client
|
||||
const deckClient = new NextcloudDeckClient({auth: {username, password}, url})
|
||||
|
@ -6,8 +6,8 @@ This node app converts a Notion CSV and markdown export into a Focalboard archiv
|
||||
3. Save it locally, and unzip the folder e.g. to `notion-export`
|
||||
4. Run `npm install` from within `focalboard/webapp`
|
||||
5. Run `npm install` from within `focalboard/import/notion`
|
||||
6. Run `npx ts-node importNotion.ts -i <path to the notion-export folder> -o archive.focalboard`
|
||||
7. In Focalboard, click `Settings`, then `Import archive` and select `archive.focalboard`
|
||||
6. Run `npx ts-node importNotion.ts -i <path to the notion-export folder> -o archive.boardarchive`
|
||||
7. In Focalboard, click `Settings`, then `Import archive` and select `archive.boardarchive`
|
||||
|
||||
## Import scope
|
||||
|
||||
|
@ -35,7 +35,7 @@ async function main() {
|
||||
const args: minimist.ParsedArgs = minimist(process.argv.slice(2))
|
||||
|
||||
const inputFolder = args['i']
|
||||
const outputFile = args['o'] || 'archive.focalboard'
|
||||
const outputFile = args['o'] || 'archive.boardarchive'
|
||||
|
||||
if (!inputFolder) {
|
||||
showHelp()
|
||||
@ -217,7 +217,7 @@ function convert(input: any[], title: string): [Board[], Block[]] {
|
||||
}
|
||||
|
||||
function showHelp() {
|
||||
console.log('import -i <input.json> -o [output.focalboard]')
|
||||
console.log('import -i <input.json> -o [output.boardarchive]')
|
||||
exit(1)
|
||||
}
|
||||
|
||||
|
@ -8,5 +8,5 @@ This node app converts a Todoist json archive into a Focalboard archive. To use:
|
||||
1. Note the name and location of the downloaded *json* file.
|
||||
3. Run `npm install` from within `focalboard/webapp`
|
||||
4. Run `npm install` from within `focalboard/import/todoist`
|
||||
5. Run `npx ts-node importTodoist.ts -i <path-to-todoist.json> -o archive.focalboard` (also from within `focalboard/import/todoist`)
|
||||
6. In Focalboard, click `Settings`, then `Import archive` and select `archive.focalboard`
|
||||
5. Run `npx ts-node importTodoist.ts -i <path-to-todoist.json> -o archive.boardarchive` (also from within `focalboard/import/todoist`)
|
||||
6. In Focalboard, click `Settings`, then `Import archive` and select `archive.boardarchive`
|
||||
|
@ -42,7 +42,7 @@ function main() {
|
||||
const args: minimist.ParsedArgs = minimist(process.argv.slice(2))
|
||||
|
||||
const inputFile = args['i']
|
||||
const outputFile = args['o'] || 'archive.focalboard'
|
||||
const outputFile = args['o'] || 'archive.boardarchive'
|
||||
|
||||
if (!inputFile) {
|
||||
showHelp()
|
||||
|
@ -6,8 +6,8 @@ This node app converts a Trello json archive into a Focalboard archive. To use:
|
||||
3. Save it locally, e.g. to `trello.json`
|
||||
4. Run `npm install` from within `focalboard/webapp`
|
||||
5. Run `npm install` from within `focalboard/import/trello`
|
||||
6. Run `npx ts-node importTrello.ts -i <path-to-trello.json> -o archive.focalboard` (also from within `focalboard/import/trello`)
|
||||
7. In Focalboard, click `Settings`, then `Import archive` and select `archive.focalboard`
|
||||
6. Run `npx ts-node importTrello.ts -i <path-to-trello.json> -o archive.boardarchive` (also from within `focalboard/import/trello`)
|
||||
7. In Focalboard, click `Settings`, then `Import archive` and select `archive.boardarchive`
|
||||
|
||||
## Import scope
|
||||
|
||||
|
@ -35,7 +35,7 @@ function main() {
|
||||
const args: minimist.ParsedArgs = minimist(process.argv.slice(2))
|
||||
|
||||
const inputFile = args['i']
|
||||
const outputFile = args['o'] || 'archive.focalboard'
|
||||
const outputFile = args['o'] || 'archive.boardarchive'
|
||||
|
||||
if (!inputFile) {
|
||||
showHelp()
|
||||
@ -169,7 +169,7 @@ function convert(input: Trello): [Board[], Block[]] {
|
||||
}
|
||||
|
||||
function showHelp() {
|
||||
console.log('import -i <input.json> -o [output.focalboard]')
|
||||
console.log('import -i <input.json> -o [output.boardarchive]')
|
||||
exit(1)
|
||||
}
|
||||
|
||||
|
@ -13,15 +13,15 @@ ALTER TABLE {{.prefix}}blocks_history ADD COLUMN board_id VARCHAR(36);
|
||||
{{- /* cleanup incorrect data format in column calculations */ -}}
|
||||
{{- /* then move from 'board' type to 'view' type*/ -}}
|
||||
{{if .mysql}}
|
||||
UPDATE {{.prefix}}blocks SET fields = JSON_SET(fields, '$.columnCalculations', cast('{}' as json)) WHERE fields->'$.columnCalculations' = cast('[]' as json);
|
||||
UPDATE {{.prefix}}blocks SET fields = JSON_SET(fields, '$.columnCalculations', JSON_OBJECT()) WHERE JSON_EXTRACT(fields, '$.columnCalculations') = JSON_ARRAY();
|
||||
|
||||
UPDATE {{.prefix}}blocks b
|
||||
JOIN (
|
||||
SELECT id, fields->'$.columnCalculations' as board_calculations from {{.prefix}}blocks
|
||||
WHERE fields -> '$.columnCalculations' <> cast('{}' as json)
|
||||
SELECT id, JSON_EXTRACT(fields, '$.columnCalculations') as board_calculations from {{.prefix}}blocks
|
||||
WHERE JSON_EXTRACT(fields, '$.columnCalculations') <> JSON_OBJECT()
|
||||
) AS s on s.id = b.root_id
|
||||
SET fields = JSON_SET(fields, '$.columnCalculations', cast(s.board_calculations as json))
|
||||
WHERE b.fields->'$.viewType' = 'table'
|
||||
SET fields = JSON_SET(fields, '$.columnCalculations', JSON_ARRAY(s.board_calculations))
|
||||
WHERE JSON_EXTRACT(b.fields, '$.viewType') = 'table'
|
||||
AND b.type = 'view';
|
||||
{{end}}
|
||||
{{if .postgres}}
|
||||
@ -166,10 +166,10 @@ CREATE TABLE IF NOT EXISTS {{.prefix}}boards_history (
|
||||
COALESCE(B.title, ''),
|
||||
COALESCE(JSON_UNQUOTE(JSON_EXTRACT(B.fields,'$.description')), ''),
|
||||
JSON_UNQUOTE(JSON_EXTRACT(B.fields,'$.icon')),
|
||||
COALESCE(B.fields->'$.showDescription', 'false') = 'true',
|
||||
COALESCE(JSON_EXTRACT(B.fields, '$.showDescription'), 'false') = 'true',
|
||||
COALESCE(JSON_EXTRACT(B.fields, '$.isTemplate'), 'false') = 'true',
|
||||
COALESCE(B.fields->'$.templateVer', 0),
|
||||
'{}', B.fields->'$.cardProperties', B.create_at,
|
||||
COALESCE(JSON_EXTRACT(B.fields, '$.templateVer'), 0),
|
||||
'{}', JSON_EXTRACT(B.fields, '$.cardProperties'), B.create_at,
|
||||
B.update_at, B.delete_at
|
||||
FROM {{.prefix}}blocks AS B
|
||||
INNER JOIN Channels AS C ON C.Id=B.channel_id
|
||||
@ -180,10 +180,10 @@ CREATE TABLE IF NOT EXISTS {{.prefix}}boards_history (
|
||||
COALESCE(B.title, ''),
|
||||
COALESCE(JSON_UNQUOTE(JSON_EXTRACT(B.fields,'$.description')), ''),
|
||||
JSON_UNQUOTE(JSON_EXTRACT(B.fields,'$.icon')),
|
||||
COALESCE(B.fields->'$.showDescription', 'false') = 'true',
|
||||
COALESCE(JSON_EXTRACT(B.fields, '$.showDescription'), 'false') = 'true',
|
||||
COALESCE(JSON_EXTRACT(B.fields, '$.isTemplate'), 'false') = 'true',
|
||||
COALESCE(B.fields->'$.templateVer', 0),
|
||||
'{}', B.fields->'$.cardProperties', B.create_at,
|
||||
COALESCE(JSON_EXTRACT(B.fields, '$.templateVer'), 0),
|
||||
'{}', JSON_EXTRACT(B.fields, '$.cardProperties'), B.create_at,
|
||||
B.update_at, B.delete_at
|
||||
FROM {{.prefix}}blocks_history AS B
|
||||
INNER JOIN Channels AS C ON C.Id=B.channel_id
|
||||
@ -225,10 +225,10 @@ CREATE TABLE IF NOT EXISTS {{.prefix}}boards_history (
|
||||
COALESCE(B.title, ''),
|
||||
COALESCE(JSON_UNQUOTE(JSON_EXTRACT(B.fields,'$.description')), ''),
|
||||
JSON_UNQUOTE(JSON_EXTRACT(fields,'$.icon')),
|
||||
COALESCE(B.fields->'$.showDescription', 'false') = 'true',
|
||||
COALESCE(JSON_EXTRACT(B.fields, '$.showDescription'), 'false') = 'true',
|
||||
COALESCE(JSON_EXTRACT(B.fields, '$.isTemplate'), 'false') = 'true',
|
||||
COALESCE(B.fields->'$.templateVer', 0),
|
||||
'{}', fields->'$.cardProperties', create_at,
|
||||
COALESCE(JSON_EXTRACT(B.fields, '$.templateVer'), 0),
|
||||
'{}', JSON_EXTRACT(fields, '$.cardProperties'), create_at,
|
||||
update_at, delete_at
|
||||
FROM {{.prefix}}blocks AS B
|
||||
WHERE type='board'
|
||||
@ -238,10 +238,10 @@ CREATE TABLE IF NOT EXISTS {{.prefix}}boards_history (
|
||||
COALESCE(B.title, ''),
|
||||
COALESCE(JSON_UNQUOTE(JSON_EXTRACT(B.fields,'$.description')), ''),
|
||||
JSON_UNQUOTE(JSON_EXTRACT(fields,'$.icon')),
|
||||
COALESCE(B.fields->'$.showDescription', 'false') = 'true',
|
||||
COALESCE(JSON_EXTRACT(B.fields, '$.showDescription'), 'false') = 'true',
|
||||
COALESCE(JSON_EXTRACT(B.fields, '$.isTemplate'), 'false') = 'true',
|
||||
COALESCE(B.fields->'$.templateVer', 0),
|
||||
'{}', fields->'$.cardProperties', create_at,
|
||||
COALESCE(JSON_EXTRACT(B.fields, '$.templateVer'), 0),
|
||||
'{}', JSON_EXTRACT(fields, '$.cardProperties'), create_at,
|
||||
update_at, delete_at
|
||||
FROM {{.prefix}}blocks_history AS B
|
||||
WHERE type='board'
|
||||
|
@ -1,7 +1,10 @@
|
||||
INSERT INTO Channels (Id, CreateAt, UpdateAt, DeleteAt, TeamId, Type, Name, CreatorId) VALUES ('chan-id', 123, 123, 0, 'team-id', 'O', 'channel', 'user-id');
|
||||
|
||||
INSERT INTO focalboard_blocks
|
||||
(id, workspace_id, root_id, parent_id, created_by, modified_by, type, title, create_at, update_at, delete_at)
|
||||
(id, workspace_id, root_id, parent_id, created_by, modified_by, type, title, create_at, update_at, delete_at, fields)
|
||||
VALUES
|
||||
('board-id', 'chan-id', 'board-id', 'board-id', 'user-id', 'user-id', 'board', 'My Board', 123, 123, 0),
|
||||
('card-id', 'chan-id', 'board-id', 'board-id', 'user-id', 'user-id', 'card', 'A card', 123, 123, 0);
|
||||
('board-id', 'chan-id', 'board-id', 'board-id', 'user-id', 'user-id', 'board', 'My Board', 123, 123, 0, '{"columnCalculations": {"__title":"countUniqueValue"}}'),
|
||||
('card-id', 'chan-id', 'board-id', 'board-id', 'user-id', 'user-id', 'card', 'A card', 123, 123, 0, '{}'),
|
||||
('view-id', 'chan-id', 'board-id', 'board-id', 'user-id', 'user-id', 'view', 'A view', 123, 123, 0, '{"viewType":"table"}'),
|
||||
('view-id2', 'chan-id', 'board-id', 'board-id', 'user-id', 'user-id', 'view', 'A view2', 123, 123, 0, '{"viewType":"board"}'),
|
||||
('board-id2', 'chan-id', 'board-id2', 'board-id2', 'user-id', 'user-id', 'board', 'My Board Two', 123, 123, 0, '{"description": "My Description","showDescription":true,"isTemplate":true,"templateVer":1,"columnCalculations":[]}');
|
||||
|
@ -1,6 +1,7 @@
|
||||
package migrationstests
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -15,22 +16,39 @@ func Test18AddTeamsAndBoardsSQLMigration(t *testing.T) {
|
||||
ExecFile("./fixtures/test18AddTeamsAndBoardsSQLMigrationFixtures.sql")
|
||||
|
||||
board := struct {
|
||||
ID string
|
||||
Title string
|
||||
Type string
|
||||
ID string
|
||||
Title string
|
||||
Type string
|
||||
Fields string
|
||||
Description string
|
||||
Show_Description bool
|
||||
Is_Template bool
|
||||
Template_Version int
|
||||
}{}
|
||||
|
||||
// we check first that the board is inside the blocks table as
|
||||
// a block of board type
|
||||
err := th.f.DB().Get(&board, "SELECT id, title, type FROM focalboard_blocks WHERE id = 'board-id'")
|
||||
// a block of board type and columnCalculations exists if Fields
|
||||
err := th.f.DB().Get(&board, "SELECT id, title, type, fields FROM focalboard_blocks WHERE id = 'board-id'")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "My Board", board.Title)
|
||||
require.Equal(t, "board", board.Type)
|
||||
require.Contains(t, board.Fields, "columnCalculations")
|
||||
|
||||
// we check another board is inside the blocks table as
|
||||
// a block of board type and has several different properties in boards
|
||||
err = th.f.DB().Get(&board, "SELECT id, title, type, fields FROM focalboard_blocks WHERE id = 'board-id2'")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "My Board Two", board.Title)
|
||||
require.Equal(t, "board", board.Type)
|
||||
require.Contains(t, board.Fields, "description")
|
||||
require.Contains(t, board.Fields, "showDescription")
|
||||
require.Contains(t, board.Fields, "isTemplate")
|
||||
require.Contains(t, board.Fields, "templateVer")
|
||||
|
||||
// then we run the migration
|
||||
th.f.MigrateToStep(18)
|
||||
|
||||
// we assert that the board is now a block
|
||||
// we assert that the board is now in the boards table
|
||||
bErr := th.f.DB().Get(&board, "SELECT id, title, type FROM focalboard_boards WHERE id = 'board-id'")
|
||||
require.NoError(t, bErr)
|
||||
require.Equal(t, "My Board", board.Title)
|
||||
@ -43,12 +61,46 @@ func Test18AddTeamsAndBoardsSQLMigration(t *testing.T) {
|
||||
Board_ID string
|
||||
}{}
|
||||
|
||||
// we fetch the card to ensure that the
|
||||
// we fetch the card to ensure that the card is still in the blocks table
|
||||
cErr := th.f.DB().Get(&card, "SELECT title, type, parent_id, board_id FROM focalboard_blocks WHERE id = 'card-id'")
|
||||
require.NoError(t, cErr)
|
||||
require.Equal(t, "A card", card.Title)
|
||||
require.Equal(t, "card", card.Type)
|
||||
require.Equal(t, board.ID, card.Parent_ID)
|
||||
require.Equal(t, board.ID, card.Board_ID)
|
||||
|
||||
// we assert that the board is now a board and properties from JSON Fields
|
||||
dErr := th.f.DB().Get(&board, "SELECT id, title, type, description, show_description, is_template, template_version FROM focalboard_boards WHERE id = 'board-id2'")
|
||||
require.NoError(t, dErr)
|
||||
require.Equal(t, "My Board Two", board.Title)
|
||||
require.Equal(t, "O", board.Type)
|
||||
require.Equal(t, "My Description", board.Description)
|
||||
require.Equal(t, true, board.Show_Description)
|
||||
require.Equal(t, true, board.Is_Template)
|
||||
require.Equal(t, 1, board.Template_Version)
|
||||
|
||||
view := struct {
|
||||
Title string
|
||||
Type string
|
||||
Parent_ID string
|
||||
Board_ID string
|
||||
Fields string
|
||||
}{}
|
||||
|
||||
// we fetch the views to ensure that the calculation columns exist on views
|
||||
eErr := th.f.DB().Get(&view, "SELECT title, type, parent_id, board_id, fields FROM focalboard_blocks WHERE id = 'view-id'")
|
||||
require.NoError(t, eErr)
|
||||
require.Contains(t, view.Fields, "columnCalculations")
|
||||
var fields map[string]interface{}
|
||||
|
||||
// Make sure a valid JSON object
|
||||
json.Unmarshal([]byte(view.Fields), &fields)
|
||||
require.NotNil(t, fields["columnCalculations"])
|
||||
require.NotEmpty(t, fields["columnCalculations"])
|
||||
|
||||
// Board View should not have columnCalculations
|
||||
fErr := th.f.DB().Get(&view, "SELECT title, type, parent_id, board_id, fields FROM focalboard_blocks WHERE id = 'view-id2'")
|
||||
require.NoError(t, fErr)
|
||||
require.NotContains(t, view.Fields, "columnCalculations")
|
||||
})
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package sqlstore
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
@ -17,9 +16,6 @@ import (
|
||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||
)
|
||||
|
||||
//nolint:lll
|
||||
var ErrInvalidMariaDB = errors.New("MariaDB database is not supported, you can find more information at https://docs.mattermost.com/install/software-hardware-requirements.html#database-software")
|
||||
|
||||
// SQLStore is a SQL database.
|
||||
type SQLStore struct {
|
||||
db *sql.DB
|
||||
@ -58,10 +54,6 @@ func New(params Params) (*SQLStore, error) {
|
||||
servicesAPI: params.ServicesAPI,
|
||||
}
|
||||
|
||||
if store.IsMariaDB() {
|
||||
return nil, ErrInvalidMariaDB
|
||||
}
|
||||
|
||||
var err error
|
||||
store.isBinaryParam, err = store.computeBinaryParam()
|
||||
if err != nil {
|
||||
|
@ -42,6 +42,9 @@ func PrepareNewTestDatabase() (dbType string, connectionString string, err error
|
||||
if dbType == "" {
|
||||
dbType = model.SqliteDBType
|
||||
}
|
||||
if dbType == "mariadb" {
|
||||
dbType = model.MysqlDBType
|
||||
}
|
||||
|
||||
var dbName string
|
||||
var rootUser string
|
||||
|
@ -12,6 +12,7 @@ import IconButton from '../../widgets/buttons/iconButton'
|
||||
import OptionsIcon from '../../widgets/icons/options'
|
||||
import Menu from '../../widgets/menu'
|
||||
import MenuWrapper from '../../widgets/menuWrapper'
|
||||
import {Utils} from '../../utils'
|
||||
|
||||
import ModalWrapper from '../modalWrapper'
|
||||
import {sendFlashMessage} from '../flashMessages'
|
||||
@ -84,6 +85,7 @@ function onExportCsvTrigger(board: Board, activeView: BoardView, cards: Card[],
|
||||
})
|
||||
sendFlashMessage({content: exportCompleteMessage, severity: 'normal'})
|
||||
} catch (e) {
|
||||
Utils.logError(`ExportCSV ERROR: ${e}`)
|
||||
const exportFailedMessage = intl.formatMessage({
|
||||
id: 'ViewHeader.export-failed',
|
||||
defaultMessage: 'Export failed!',
|
||||
|
@ -39,7 +39,7 @@ class Constants {
|
||||
static readonly versionString = '7.5.0'
|
||||
static readonly versionDisplayString = 'Nov 2022'
|
||||
|
||||
static readonly archiveHelpPage = 'https://docs.mattermost.com/boards/data-and-archives.html'
|
||||
static readonly archiveHelpPage = 'https://docs.mattermost.com/boards/migrate-to-boards.html'
|
||||
static readonly imports = [
|
||||
{
|
||||
id: 'trello',
|
||||
|
@ -141,11 +141,15 @@ const BoardPage = (props: Props): JSX.Element => {
|
||||
}
|
||||
}
|
||||
|
||||
const dispatchLoadAction = () => {
|
||||
dispatch(loadAction(match.params.boardId))
|
||||
}
|
||||
|
||||
Utils.log('useWEbsocket adding onChange handler')
|
||||
wsClient.addOnChange(incrementalBlockUpdate, 'block')
|
||||
wsClient.addOnChange(incrementalBoardUpdate, 'board')
|
||||
wsClient.addOnChange(incrementalBoardMemberUpdate, 'boardMembers')
|
||||
wsClient.addOnReconnect(() => dispatch(loadAction(match.params.boardId)))
|
||||
wsClient.addOnReconnect(dispatchLoadAction)
|
||||
|
||||
wsClient.setOnFollowBlock((_: WSClient, subscription: Subscription): void => {
|
||||
if (subscription.subscriberId === me?.id) {
|
||||
@ -163,7 +167,7 @@ const BoardPage = (props: Props): JSX.Element => {
|
||||
wsClient.removeOnChange(incrementalBlockUpdate, 'block')
|
||||
wsClient.removeOnChange(incrementalBoardUpdate, 'board')
|
||||
wsClient.removeOnChange(incrementalBoardMemberUpdate, 'boardMembers')
|
||||
wsClient.removeOnReconnect(() => dispatch(loadAction(match.params.boardId)))
|
||||
wsClient.removeOnReconnect(dispatchLoadAction)
|
||||
}
|
||||
}, [me?.id, activeBoardId])
|
||||
|
||||
|
@ -58,7 +58,12 @@ export abstract class PropertyType {
|
||||
|
||||
exportValue = (value: string | string[] | undefined, card: Card, template: IPropertyTemplate, intl: IntlShape): string => {
|
||||
const displayValue = this.displayValue(value, card, template, intl)
|
||||
return `"${encodeText(displayValue as string)}"`
|
||||
if (typeof displayValue === 'string') {
|
||||
return `"${encodeText(displayValue)}"`
|
||||
} else if (Array.isArray(displayValue)) {
|
||||
return `"${encodeText((displayValue as string[]).join('|'))}"`
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
valueClassName = (readonly: boolean): string => {
|
||||
|
@ -20,7 +20,13 @@ import {RootState} from './index'
|
||||
|
||||
export const fetchMe = createAsyncThunk(
|
||||
'users/fetchMe',
|
||||
async () => client.getMe(),
|
||||
async () => {
|
||||
const [me, myConfig] = await Promise.all([
|
||||
client.getMe(),
|
||||
client.getMyConfig(),
|
||||
])
|
||||
return {me, myConfig}
|
||||
},
|
||||
)
|
||||
|
||||
export const versionProperty = 'version72MessageCanceled'
|
||||
@ -84,12 +90,16 @@ const usersSlice = createSlice({
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(fetchMe.fulfilled, (state, action) => {
|
||||
state.me = action.payload || null
|
||||
state.me = action.payload.me || null
|
||||
state.loggedIn = Boolean(state.me)
|
||||
if (action.payload.myConfig) {
|
||||
state.myConfig = parseUserProps(action.payload.myConfig)
|
||||
}
|
||||
})
|
||||
builder.addCase(fetchMe.rejected, (state) => {
|
||||
state.me = null
|
||||
state.loggedIn = false
|
||||
state.myConfig = {}
|
||||
})
|
||||
|
||||
// TODO: change this when the initial load is complete
|
||||
|
Loading…
Reference in New Issue
Block a user