mirror of
https://github.com/mattermost/focalboard.git
synced 2024-12-21 13:38:56 +02:00
Merge branch 'main' into gh-4374-attachment-delet-hover
This commit is contained in:
commit
be21726878
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@ -48,7 +48,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18.1
|
||||
go-version: 1.19.5
|
||||
|
||||
- name: "Test server: ${{matrix['db']}}"
|
||||
run: cd focalboard; make server-test-${{matrix['db']}}
|
||||
@ -83,7 +83,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18.1
|
||||
go-version: 1.19.5
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
@ -137,7 +137,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18.1
|
||||
go-version: 1.19.5
|
||||
|
||||
- name: "Test server (minimum): ${{matrix['db']}}"
|
||||
run: cd focalboard; make server-test-mini-${{matrix['db']}}
|
||||
@ -174,7 +174,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18.1
|
||||
go-version: 1.19.5
|
||||
|
||||
- name: "Test server (minimum): ${{matrix['db']}}"
|
||||
run: cd focalboard; make server-test-mini-${{matrix['db']}}
|
||||
|
8
.github/workflows/dev-release.yml
vendored
8
.github/workflows/dev-release.yml
vendored
@ -54,7 +54,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18.1
|
||||
go-version: 1.19.5
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
@ -129,7 +129,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18.1
|
||||
go-version: 1.19.5
|
||||
|
||||
- name: List Xcode versions
|
||||
run: ls -n /Applications/ | grep Xcode*
|
||||
@ -190,7 +190,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18.1
|
||||
go-version: 1.19.5
|
||||
|
||||
- name: Setup NuGet
|
||||
uses: nuget/setup-nuget@v1
|
||||
@ -258,7 +258,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18.1
|
||||
go-version: 1.19.5
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v3
|
||||
|
4
.github/workflows/lint-server.yml
vendored
4
.github/workflows/lint-server.yml
vendored
@ -30,7 +30,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18.1
|
||||
go-version: 1.19.5
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
path: "focalboard"
|
||||
@ -50,7 +50,7 @@ jobs:
|
||||
path: "mattermost-server"
|
||||
ref : "master"
|
||||
- name: set up golangci-lint
|
||||
run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.46.2
|
||||
run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.50.1
|
||||
- name: lint
|
||||
run: |
|
||||
cd focalboard
|
||||
|
8
.github/workflows/prod-release.yml
vendored
8
.github/workflows/prod-release.yml
vendored
@ -50,7 +50,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18.1
|
||||
go-version: 1.19.5
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
@ -126,7 +126,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18.1
|
||||
go-version: 1.19.5
|
||||
|
||||
- name: List Xcode versions
|
||||
run: ls -n /Applications/ | grep Xcode*
|
||||
@ -188,7 +188,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18.1
|
||||
go-version: 1.19.5
|
||||
|
||||
- name: Setup NuGet
|
||||
uses: nuget/setup-nuget@v1
|
||||
@ -257,7 +257,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18.1
|
||||
go-version: 1.19.5
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v3
|
||||
|
@ -4,7 +4,7 @@ stages:
|
||||
|
||||
variables:
|
||||
BUILD: "yes"
|
||||
IMAGE_BUILDER: $CI_REGISTRY/mattermost/ci/images/builder:go-1.18.1-node-16.15.0-1
|
||||
IMAGE_BUILDER: $CI_REGISTRY/mattermost/ci/images/builder:go-1.19.5-node-16.15.0-1
|
||||
|
||||
include:
|
||||
- project: mattermost/ci/focalboard
|
||||
|
@ -51,7 +51,7 @@ func makeGoWork(ci bool) string {
|
||||
|
||||
var b strings.Builder
|
||||
|
||||
b.WriteString("go 1.18\n\n")
|
||||
b.WriteString("go 1.19\n\n")
|
||||
b.WriteString("use ./server\n")
|
||||
|
||||
for repo, envVarName := range repos {
|
||||
|
@ -1,6 +1,6 @@
|
||||
module github.com/mattermost/focalboard/linux
|
||||
|
||||
go 1.18
|
||||
go 1.19
|
||||
|
||||
replace github.com/mattermost/focalboard/server => ../server
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
module github.com/mattermost/mattermost-plugin-starter-template/build
|
||||
|
||||
go 1.18
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/go-git/go-git/v5 v5.1.0
|
||||
|
@ -1,6 +1,6 @@
|
||||
module github.com/mattermost/focalboard/mattermost-plugin
|
||||
|
||||
go 1.18
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/golang/mock v1.6.0
|
||||
@ -12,136 +12,69 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
code.sajari.com/docconv v1.3.5 // indirect
|
||||
github.com/JalfResi/justext v0.0.0-20221106200834-be571e3e3052 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.1.1 // indirect
|
||||
github.com/BurntSushi/toml v1.2.0 // indirect
|
||||
github.com/Masterminds/squirrel v1.5.2 // indirect
|
||||
github.com/PuerkitoBio/goquery v1.8.0 // indirect
|
||||
github.com/RoaringBitmap/roaring v1.2.1 // indirect
|
||||
github.com/advancedlogic/GoOse v0.0.0-20210820140952-9d5822d4a625 // indirect
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.1 // indirect
|
||||
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de // indirect
|
||||
github.com/avct/uasurfer v0.0.0-20191028135549-26b5daa857f1 // indirect
|
||||
github.com/aws/aws-sdk-go v1.44.138 // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.3.3 // indirect
|
||||
github.com/blang/semver v3.5.1+incompatible // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/blevesearch/bleve/v2 v2.3.6-0.20221111171245-56dc9b25507e // indirect
|
||||
github.com/blevesearch/bleve_index_api v1.0.5 // indirect
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
|
||||
github.com/blevesearch/gtreap v0.1.1 // indirect
|
||||
github.com/blevesearch/mmap-go v1.0.4 // indirect
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.1.4 // indirect
|
||||
github.com/blevesearch/segment v0.9.0 // indirect
|
||||
github.com/blevesearch/snowballstem v0.9.0 // indirect
|
||||
github.com/blevesearch/upsidedown_store_api v1.0.1 // indirect
|
||||
github.com/blevesearch/vellum v1.0.9 // indirect
|
||||
github.com/blevesearch/zapx/v11 v11.3.7 // indirect
|
||||
github.com/blevesearch/zapx/v12 v12.3.7 // indirect
|
||||
github.com/blevesearch/zapx/v13 v13.3.7 // indirect
|
||||
github.com/blevesearch/zapx/v14 v14.3.7 // indirect
|
||||
github.com/blevesearch/zapx/v15 v15.3.7 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
|
||||
github.com/dgryski/dgoogauth v0.0.0-20190221195224-5a805980a5f3 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/disintegration/imaging v1.6.2 // indirect
|
||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
|
||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||
github.com/dyatlov/go-opengraph/opengraph v0.0.0-20220524092352-606d7b1e5f8a // indirect
|
||||
github.com/fatih/color v1.13.0 // indirect
|
||||
github.com/fatih/set v0.2.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.3 // indirect
|
||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/getsentry/sentry-go v0.15.0 // indirect
|
||||
github.com/gigawattio/window v0.0.0-20180317192513-0f5467e35573 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
|
||||
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
||||
github.com/go-resty/resty/v2 v2.7.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
||||
github.com/golang-migrate/migrate/v4 v4.15.2 // indirect
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/gopherjs/gopherjs v1.17.2 // indirect
|
||||
github.com/gorilla/css v1.0.0 // indirect
|
||||
github.com/gorilla/handlers v1.5.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/graph-gophers/graphql-go v1.4.0 // indirect
|
||||
github.com/h2non/go-is-svg v0.0.0-20160927212452-35e8c4b0612c // indirect
|
||||
github.com/hashicorp/go-hclog v1.3.1 // indirect
|
||||
github.com/hashicorp/go-plugin v1.4.6 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/jaytaylor/html2text v0.0.0-20211105163654-bc68cce691ba // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/jmoiron/sqlx v1.3.5 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/klauspost/compress v1.15.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.1 // indirect
|
||||
github.com/klauspost/pgzip v1.2.5 // indirect
|
||||
github.com/krolaw/zipstream v0.0.0-20180621105154-0a2661891f94 // indirect
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
||||
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 // indirect
|
||||
github.com/levigross/exp-html v0.0.0-20120902181939-8df60c69a8f5 // indirect
|
||||
github.com/lib/pq v1.10.7 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404 // indirect
|
||||
github.com/mattermost/ldap v0.0.0-20201202150706-ee0e6284187d // indirect
|
||||
github.com/mattermost/logr/v2 v2.0.15 // indirect
|
||||
github.com/mattermost/morph v1.0.5-0.20221115094356-4c18a75b1f5e // indirect
|
||||
github.com/mattermost/rsc v0.0.0-20160330161541-bbaefb05eaa0 // indirect
|
||||
github.com/mattermost/squirrel v0.2.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
||||
github.com/mholt/archiver/v3 v3.5.1 // indirect
|
||||
github.com/microcosm-cc/bluemonday v1.0.21 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/minio/minio-go/v7 v7.0.43 // indirect
|
||||
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/mschoch/smat v0.2.0 // indirect
|
||||
github.com/nwaples/rardecode v1.1.3 // indirect
|
||||
github.com/oklog/run v1.1.0 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/oov/psd v0.0.0-20220121172623-5db5eafcecbb // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/otiai10/gosseract/v2 v2.4.0 // indirect
|
||||
github.com/pborman/uuid v1.2.1 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/philhofer/fwd v1.1.1 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.17 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.12.1 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.33.0 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/reflog/dateconstraints v0.2.1 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||
github.com/richardlehane/mscfb v1.0.4 // indirect
|
||||
github.com/richardlehane/msoleps v1.0.3 // indirect
|
||||
github.com/rivo/uniseg v0.4.3 // indirect
|
||||
github.com/rs/cors v1.8.2 // indirect
|
||||
github.com/rs/xid v1.4.0 // indirect
|
||||
github.com/rudderlabs/analytics-go v3.3.3+incompatible // indirect
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd // indirect
|
||||
github.com/segmentio/backo-go v1.0.1 // indirect
|
||||
github.com/sergi/go-diff v1.2.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||
@ -150,31 +83,19 @@ require (
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.10.1 // indirect
|
||||
github.com/splitio/go-client/v6 v6.2.1 // indirect
|
||||
github.com/splitio/go-split-commons/v3 v3.1.0 // indirect
|
||||
github.com/splitio/go-toolkit/v4 v4.2.0 // indirect
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
github.com/subosito/gotenv v1.2.0 // indirect
|
||||
github.com/throttled/throttled v2.2.5+incompatible // indirect
|
||||
github.com/tidwall/gjson v1.14.3 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tinylib/msgp v1.1.6 // indirect
|
||||
github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect
|
||||
github.com/uber/jaeger-lib v2.4.1+incompatible // indirect
|
||||
github.com/ulikunitz/xz v0.5.10 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
github.com/wiggin77/merror v1.0.4 // indirect
|
||||
github.com/wiggin77/srslog v1.0.1 // indirect
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
|
||||
github.com/yuin/goldmark v1.5.3 // indirect
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
go.uber.org/atomic v1.10.0 // indirect
|
||||
golang.org/x/crypto v0.2.0 // indirect
|
||||
golang.org/x/image v0.1.0 // indirect
|
||||
golang.org/x/mod v0.7.0 // indirect
|
||||
golang.org/x/net v0.2.0 // indirect
|
||||
golang.org/x/sync v0.1.0 // indirect
|
||||
@ -184,9 +105,7 @@ require (
|
||||
google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1 // indirect
|
||||
google.golang.org/grpc v1.50.1 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/mail.v2 v2.3.1 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,7 @@
|
||||
"support_url": "https://github.com/mattermost/focalboard/issues",
|
||||
"release_notes_url": "https://github.com/mattermost/focalboard/releases",
|
||||
"icon_path": "assets/starter-template-icon.svg",
|
||||
"version": "7.8.0",
|
||||
"version": "7.9.0",
|
||||
"min_server_version": "7.2.0",
|
||||
"server": {
|
||||
"executables": {
|
||||
|
@ -49,6 +49,11 @@ func (a *serviceAPIAdapter) GetDirectChannel(userID1, userID2 string) (*mm_model
|
||||
return channel, normalizeAppErr(appErr)
|
||||
}
|
||||
|
||||
func (a *serviceAPIAdapter) GetDirectChannelOrCreate(userID1, userID2 string) (*mm_model.Channel, error) {
|
||||
channel, appErr := a.api.channelService.GetDirectChannelOrCreate(userID1, userID2)
|
||||
return channel, normalizeAppErr(appErr)
|
||||
}
|
||||
|
||||
func (a *serviceAPIAdapter) GetChannelByID(channelID string) (*mm_model.Channel, error) {
|
||||
channel, appErr := a.api.channelService.GetChannelByID(channelID)
|
||||
return channel, normalizeAppErr(appErr)
|
||||
|
@ -54,6 +54,12 @@ func (a *pluginAPIAdapter) GetDirectChannel(userID1, userID2 string) (*mm_model.
|
||||
return channel, normalizeAppErr(appErr)
|
||||
}
|
||||
|
||||
func (a *pluginAPIAdapter) GetDirectChannelOrCreate(userID1, userID2 string) (*mm_model.Channel, error) {
|
||||
// plugin API's GetDirectChannel will create channel if it does not exist.
|
||||
channel, appErr := a.api.GetDirectChannel(userID1, userID2)
|
||||
return channel, normalizeAppErr(appErr)
|
||||
}
|
||||
|
||||
func (a *pluginAPIAdapter) GetChannelByID(channelID string) (*mm_model.Channel, error) {
|
||||
channel, appErr := a.api.GetChannel(channelID)
|
||||
return channel, normalizeAppErr(appErr)
|
||||
|
@ -80,8 +80,12 @@ func createBoardsConfig(mmconfig mm_model.Config, baseURL string, serverID strin
|
||||
showFullName = *mmconfig.PrivacySettings.ShowFullName
|
||||
}
|
||||
|
||||
serverRoot := baseURL + "/plugins/focalboard"
|
||||
if mmconfig.FeatureFlags.BoardsProduct {
|
||||
serverRoot = baseURL + "/boards"
|
||||
}
|
||||
return &config.Configuration{
|
||||
ServerRoot: baseURL + "/plugins/focalboard",
|
||||
ServerRoot: serverRoot,
|
||||
Port: -1,
|
||||
DBType: *mmconfig.SqlSettings.DriverName,
|
||||
DBConfigString: *mmconfig.SqlSettings.DataSource,
|
||||
|
@ -80,6 +80,9 @@ func (b *BoardsApp) OnConfigurationChange() error {
|
||||
if mmconfig.PluginSettings.Plugins[PluginName][SharedBoardsName] == true {
|
||||
enableShareBoards = true
|
||||
}
|
||||
if mmconfig.ProductSettings.EnablePublicSharedBoards != nil {
|
||||
enableShareBoards = *mmconfig.ProductSettings.EnablePublicSharedBoards
|
||||
}
|
||||
configuration := &configuration{
|
||||
EnablePublicSharedBoards: enableShareBoards,
|
||||
}
|
||||
|
@ -95,8 +95,8 @@ func (a *appAPI) GetBlockHistory(blockID string, opts model.QueryBlockHistoryOpt
|
||||
return a.store.GetBlockHistory(blockID, opts)
|
||||
}
|
||||
|
||||
func (a *appAPI) GetSubTree2(boardID, blockID string, opts model.QuerySubtreeOptions) ([]*model.Block, error) {
|
||||
return a.store.GetSubTree2(boardID, blockID, opts)
|
||||
func (a *appAPI) GetBlockHistoryNewestChildren(parentID string, opts model.QueryBlockHistoryChildOptions) ([]*model.Block, bool, error) {
|
||||
return a.store.GetBlockHistoryNewestChildren(parentID, opts)
|
||||
}
|
||||
|
||||
func (a *appAPI) GetBoardAndCardByID(blockID string) (board *model.Board, card *model.Block, err error) {
|
||||
|
2
mattermost-plugin/server/manifest.go
generated
2
mattermost-plugin/server/manifest.go
generated
@ -20,7 +20,7 @@ const manifestStr = `
|
||||
"support_url": "https://github.com/mattermost/focalboard/issues",
|
||||
"release_notes_url": "https://github.com/mattermost/focalboard/releases",
|
||||
"icon_path": "assets/starter-template-icon.svg",
|
||||
"version": "7.8.0",
|
||||
"version": "7.9.0",
|
||||
"min_server_version": "7.2.0",
|
||||
"server": {
|
||||
"executables": {
|
||||
|
@ -6,7 +6,8 @@ function blockList(line) {
|
||||
line.startsWith('.GlobalHeaderComponent') ||
|
||||
line.startsWith('.boards-rhs-icon') ||
|
||||
line.startsWith('.focalboard-plugin-root') ||
|
||||
line.startsWith('.FocalboardUnfurl');
|
||||
line.startsWith('.FocalboardUnfurl') ||
|
||||
line.startsWith('.CreateBoardFromTemplate');
|
||||
}
|
||||
|
||||
module.exports = function loader(source) {
|
||||
|
537
mattermost-plugin/webapp/package-lock.json
generated
537
mattermost-plugin/webapp/package-lock.json
generated
@ -14,6 +14,7 @@
|
||||
"react-intl": "^5.20.0",
|
||||
"react-redux": "^7.2.0",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-select": "^5.2.2",
|
||||
"trim-newlines": "4.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -2038,6 +2039,150 @@
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/babel-plugin": {
|
||||
"version": "11.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.5.tgz",
|
||||
"integrity": "sha512-xE7/hyLHJac7D2Ve9dKroBBZqBT7WuPQmWcq7HSGb84sUuP4mlOWoB8dvVfD9yk5DHkU1m6RW7xSoDtnQHNQeA==",
|
||||
"dependencies": {
|
||||
"@babel/helper-module-imports": "^7.16.7",
|
||||
"@babel/plugin-syntax-jsx": "^7.17.12",
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/hash": "^0.9.0",
|
||||
"@emotion/memoize": "^0.8.0",
|
||||
"@emotion/serialize": "^1.1.1",
|
||||
"babel-plugin-macros": "^3.1.0",
|
||||
"convert-source-map": "^1.5.0",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"find-root": "^1.1.0",
|
||||
"source-map": "^0.5.7",
|
||||
"stylis": "4.1.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/babel-plugin/node_modules/@babel/runtime": {
|
||||
"version": "7.20.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz",
|
||||
"integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==",
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.13.11"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/cache": {
|
||||
"version": "11.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.10.5.tgz",
|
||||
"integrity": "sha512-dGYHWyzTdmK+f2+EnIGBpkz1lKc4Zbj2KHd4cX3Wi8/OWr5pKslNjc3yABKH4adRGCvSX4VDC0i04mrrq0aiRA==",
|
||||
"dependencies": {
|
||||
"@emotion/memoize": "^0.8.0",
|
||||
"@emotion/sheet": "^1.2.1",
|
||||
"@emotion/utils": "^1.2.0",
|
||||
"@emotion/weak-memoize": "^0.3.0",
|
||||
"stylis": "4.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/hash": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.0.tgz",
|
||||
"integrity": "sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ=="
|
||||
},
|
||||
"node_modules/@emotion/memoize": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz",
|
||||
"integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA=="
|
||||
},
|
||||
"node_modules/@emotion/react": {
|
||||
"version": "11.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.10.5.tgz",
|
||||
"integrity": "sha512-TZs6235tCJ/7iF6/rvTaOH4oxQg2gMAcdHemjwLKIjKz4rRuYe1HJ2TQJKnAcRAfOUDdU8XoDadCe1rl72iv8A==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/babel-plugin": "^11.10.5",
|
||||
"@emotion/cache": "^11.10.5",
|
||||
"@emotion/serialize": "^1.1.1",
|
||||
"@emotion/use-insertion-effect-with-fallbacks": "^1.0.0",
|
||||
"@emotion/utils": "^1.2.0",
|
||||
"@emotion/weak-memoize": "^0.3.0",
|
||||
"hoist-non-react-statics": "^3.3.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0",
|
||||
"react": ">=16.8.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@babel/core": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/react/node_modules/@babel/runtime": {
|
||||
"version": "7.20.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz",
|
||||
"integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==",
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.13.11"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/serialize": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.1.tgz",
|
||||
"integrity": "sha512-Zl/0LFggN7+L1liljxXdsVSVlg6E/Z/olVWpfxUTxOAmi8NU7YoeWeLfi1RmnB2TATHoaWwIBRoL+FvAJiTUQA==",
|
||||
"dependencies": {
|
||||
"@emotion/hash": "^0.9.0",
|
||||
"@emotion/memoize": "^0.8.0",
|
||||
"@emotion/unitless": "^0.8.0",
|
||||
"@emotion/utils": "^1.2.0",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/sheet": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.1.tgz",
|
||||
"integrity": "sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA=="
|
||||
},
|
||||
"node_modules/@emotion/unitless": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz",
|
||||
"integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw=="
|
||||
},
|
||||
"node_modules/@emotion/use-insertion-effect-with-fallbacks": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz",
|
||||
"integrity": "sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==",
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/utils": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.0.tgz",
|
||||
"integrity": "sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw=="
|
||||
},
|
||||
"node_modules/@emotion/weak-memoize": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz",
|
||||
"integrity": "sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg=="
|
||||
},
|
||||
"node_modules/@eslint/eslintrc": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz",
|
||||
@ -2103,6 +2248,19 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/core": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.1.0.tgz",
|
||||
"integrity": "sha512-zbsLwtnHo84w1Kc8rScAo5GMk1GdecSlrflIbfnEBJwvTSj1SL6kkOYV+nHraMCPEy+RNZZUaZyL8JosDGCtGQ=="
|
||||
},
|
||||
"node_modules/@floating-ui/dom": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.1.0.tgz",
|
||||
"integrity": "sha512-TSogMPVxbRe77QCj1dt8NmRiJasPvuc+eT5jnJ6YpLqgOD2zXc5UA3S1qwybN+GVCDNdKfpKy1oj8RpzLJvh6A==",
|
||||
"dependencies": {
|
||||
"@floating-ui/core": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/ecma402-abstract": {
|
||||
"version": "1.11.3",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.3.tgz",
|
||||
@ -4615,6 +4773,11 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz",
|
||||
"integrity": "sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw=="
|
||||
},
|
||||
"node_modules/@types/parse-json": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
|
||||
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
|
||||
},
|
||||
"node_modules/@types/prettier": {
|
||||
"version": "2.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.6.3.tgz",
|
||||
@ -4703,7 +4866,6 @@
|
||||
"version": "4.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz",
|
||||
"integrity": "sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
@ -5922,6 +6084,35 @@
|
||||
"node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-plugin-macros": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
|
||||
"integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"cosmiconfig": "^7.0.0",
|
||||
"resolve": "^1.19.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10",
|
||||
"npm": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-plugin-macros/node_modules/cosmiconfig": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
|
||||
"integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
|
||||
"dependencies": {
|
||||
"@types/parse-json": "^4.0.0",
|
||||
"import-fresh": "^3.2.1",
|
||||
"parse-json": "^5.0.0",
|
||||
"path-type": "^4.0.0",
|
||||
"yaml": "^1.10.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-plugin-polyfill-corejs2": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz",
|
||||
@ -6895,7 +7086,6 @@
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
@ -8203,6 +8393,15 @@
|
||||
"integrity": "sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/dom-helpers": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
|
||||
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.8.7",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/dom-serializer": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
|
||||
@ -10199,8 +10398,7 @@
|
||||
"node_modules/find-root": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
|
||||
"integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==",
|
||||
"dev": true
|
||||
"integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
|
||||
},
|
||||
"node_modules/find-up": {
|
||||
"version": "4.1.0",
|
||||
@ -11689,7 +11887,6 @@
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"parent-module": "^1.0.0",
|
||||
"resolve-from": "^4.0.0"
|
||||
@ -11705,7 +11902,6 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
@ -14736,8 +14932,7 @@
|
||||
"node_modules/json-parse-even-better-errors": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
|
||||
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
|
||||
"dev": true
|
||||
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
|
||||
},
|
||||
"node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
@ -14865,8 +15060,7 @@
|
||||
"node_modules/lines-and-columns": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
|
||||
},
|
||||
"node_modules/linked-list": {
|
||||
"version": "0.1.0",
|
||||
@ -17247,7 +17441,6 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"callsites": "^3.0.0"
|
||||
},
|
||||
@ -17259,7 +17452,6 @@
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
||||
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
"error-ex": "^1.3.1",
|
||||
@ -17338,7 +17530,6 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@ -18067,6 +18258,31 @@
|
||||
"react": ">=15"
|
||||
}
|
||||
},
|
||||
"node_modules/react-select": {
|
||||
"version": "5.7.0",
|
||||
"resolved": "https://registry.npmjs.org/react-select/-/react-select-5.7.0.tgz",
|
||||
"integrity": "sha512-lJGiMxCa3cqnUr2Jjtg9YHsaytiZqeNOKeibv6WF5zbK/fPegZ1hg3y/9P1RZVLhqBTs0PfqQLKuAACednYGhQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.0",
|
||||
"@emotion/cache": "^11.4.0",
|
||||
"@emotion/react": "^11.8.1",
|
||||
"@floating-ui/dom": "^1.0.1",
|
||||
"@types/react-transition-group": "^4.4.0",
|
||||
"memoize-one": "^6.0.0",
|
||||
"prop-types": "^15.6.0",
|
||||
"react-transition-group": "^4.3.0",
|
||||
"use-isomorphic-layout-effect": "^1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-select/node_modules/memoize-one": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
|
||||
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
|
||||
},
|
||||
"node_modules/react-shallow-renderer": {
|
||||
"version": "16.14.1",
|
||||
"resolved": "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.14.1.tgz",
|
||||
@ -18080,6 +18296,21 @@
|
||||
"react": "^16.0.0 || ^17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-transition-group": {
|
||||
"version": "4.4.5",
|
||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
||||
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"dom-helpers": "^5.0.1",
|
||||
"loose-envify": "^1.4.0",
|
||||
"prop-types": "^15.6.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.6.0",
|
||||
"react-dom": ">=16.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/readable-stream": {
|
||||
"version": "2.3.7",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
||||
@ -18518,9 +18749,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/regenerator-runtime": {
|
||||
"version": "0.13.9",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
|
||||
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
|
||||
"version": "0.13.11",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
|
||||
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
|
||||
},
|
||||
"node_modules/regenerator-transform": {
|
||||
"version": "0.15.0",
|
||||
@ -20116,6 +20347,11 @@
|
||||
"webpack": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/stylis": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.1.3.tgz",
|
||||
"integrity": "sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA=="
|
||||
},
|
||||
"node_modules/sudo-prompt": {
|
||||
"version": "9.2.1",
|
||||
"resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.2.1.tgz",
|
||||
@ -21113,6 +21349,19 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/use-isomorphic-layout-effect": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz",
|
||||
"integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/use-sync-external-store": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
|
||||
@ -21915,6 +22164,14 @@
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
|
||||
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
"version": "16.2.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
|
||||
@ -23275,6 +23532,125 @@
|
||||
"integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==",
|
||||
"dev": true
|
||||
},
|
||||
"@emotion/babel-plugin": {
|
||||
"version": "11.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.5.tgz",
|
||||
"integrity": "sha512-xE7/hyLHJac7D2Ve9dKroBBZqBT7WuPQmWcq7HSGb84sUuP4mlOWoB8dvVfD9yk5DHkU1m6RW7xSoDtnQHNQeA==",
|
||||
"requires": {
|
||||
"@babel/helper-module-imports": "^7.16.7",
|
||||
"@babel/plugin-syntax-jsx": "^7.17.12",
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/hash": "^0.9.0",
|
||||
"@emotion/memoize": "^0.8.0",
|
||||
"@emotion/serialize": "^1.1.1",
|
||||
"babel-plugin-macros": "^3.1.0",
|
||||
"convert-source-map": "^1.5.0",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"find-root": "^1.1.0",
|
||||
"source-map": "^0.5.7",
|
||||
"stylis": "4.1.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.20.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz",
|
||||
"integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.11"
|
||||
}
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@emotion/cache": {
|
||||
"version": "11.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.10.5.tgz",
|
||||
"integrity": "sha512-dGYHWyzTdmK+f2+EnIGBpkz1lKc4Zbj2KHd4cX3Wi8/OWr5pKslNjc3yABKH4adRGCvSX4VDC0i04mrrq0aiRA==",
|
||||
"requires": {
|
||||
"@emotion/memoize": "^0.8.0",
|
||||
"@emotion/sheet": "^1.2.1",
|
||||
"@emotion/utils": "^1.2.0",
|
||||
"@emotion/weak-memoize": "^0.3.0",
|
||||
"stylis": "4.1.3"
|
||||
}
|
||||
},
|
||||
"@emotion/hash": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.0.tgz",
|
||||
"integrity": "sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ=="
|
||||
},
|
||||
"@emotion/memoize": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz",
|
||||
"integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA=="
|
||||
},
|
||||
"@emotion/react": {
|
||||
"version": "11.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.10.5.tgz",
|
||||
"integrity": "sha512-TZs6235tCJ/7iF6/rvTaOH4oxQg2gMAcdHemjwLKIjKz4rRuYe1HJ2TQJKnAcRAfOUDdU8XoDadCe1rl72iv8A==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/babel-plugin": "^11.10.5",
|
||||
"@emotion/cache": "^11.10.5",
|
||||
"@emotion/serialize": "^1.1.1",
|
||||
"@emotion/use-insertion-effect-with-fallbacks": "^1.0.0",
|
||||
"@emotion/utils": "^1.2.0",
|
||||
"@emotion/weak-memoize": "^0.3.0",
|
||||
"hoist-non-react-statics": "^3.3.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.20.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz",
|
||||
"integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.11"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@emotion/serialize": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.1.tgz",
|
||||
"integrity": "sha512-Zl/0LFggN7+L1liljxXdsVSVlg6E/Z/olVWpfxUTxOAmi8NU7YoeWeLfi1RmnB2TATHoaWwIBRoL+FvAJiTUQA==",
|
||||
"requires": {
|
||||
"@emotion/hash": "^0.9.0",
|
||||
"@emotion/memoize": "^0.8.0",
|
||||
"@emotion/unitless": "^0.8.0",
|
||||
"@emotion/utils": "^1.2.0",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"@emotion/sheet": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.1.tgz",
|
||||
"integrity": "sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA=="
|
||||
},
|
||||
"@emotion/unitless": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz",
|
||||
"integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw=="
|
||||
},
|
||||
"@emotion/use-insertion-effect-with-fallbacks": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz",
|
||||
"integrity": "sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==",
|
||||
"requires": {}
|
||||
},
|
||||
"@emotion/utils": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.0.tgz",
|
||||
"integrity": "sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw=="
|
||||
},
|
||||
"@emotion/weak-memoize": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz",
|
||||
"integrity": "sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg=="
|
||||
},
|
||||
"@eslint/eslintrc": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz",
|
||||
@ -23324,6 +23700,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"@floating-ui/core": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.1.0.tgz",
|
||||
"integrity": "sha512-zbsLwtnHo84w1Kc8rScAo5GMk1GdecSlrflIbfnEBJwvTSj1SL6kkOYV+nHraMCPEy+RNZZUaZyL8JosDGCtGQ=="
|
||||
},
|
||||
"@floating-ui/dom": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.1.0.tgz",
|
||||
"integrity": "sha512-TSogMPVxbRe77QCj1dt8NmRiJasPvuc+eT5jnJ6YpLqgOD2zXc5UA3S1qwybN+GVCDNdKfpKy1oj8RpzLJvh6A==",
|
||||
"requires": {
|
||||
"@floating-ui/core": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"@formatjs/ecma402-abstract": {
|
||||
"version": "1.11.3",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.3.tgz",
|
||||
@ -25370,6 +25759,11 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz",
|
||||
"integrity": "sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw=="
|
||||
},
|
||||
"@types/parse-json": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
|
||||
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
|
||||
},
|
||||
"@types/prettier": {
|
||||
"version": "2.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.6.3.tgz",
|
||||
@ -25457,7 +25851,6 @@
|
||||
"version": "4.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz",
|
||||
"integrity": "sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
@ -26375,6 +26768,30 @@
|
||||
"@types/babel__traverse": "^7.0.6"
|
||||
}
|
||||
},
|
||||
"babel-plugin-macros": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
|
||||
"integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"cosmiconfig": "^7.0.0",
|
||||
"resolve": "^1.19.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"cosmiconfig": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
|
||||
"integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
|
||||
"requires": {
|
||||
"@types/parse-json": "^4.0.0",
|
||||
"import-fresh": "^3.2.1",
|
||||
"parse-json": "^5.0.0",
|
||||
"path-type": "^4.0.0",
|
||||
"yaml": "^1.10.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"babel-plugin-polyfill-corejs2": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz",
|
||||
@ -27157,8 +27574,7 @@
|
||||
"callsites": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
|
||||
},
|
||||
"camelcase": {
|
||||
"version": "5.3.1",
|
||||
@ -28159,6 +28575,15 @@
|
||||
"integrity": "sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg==",
|
||||
"dev": true
|
||||
},
|
||||
"dom-helpers": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
|
||||
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.8.7",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"dom-serializer": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
|
||||
@ -29709,8 +30134,7 @@
|
||||
"find-root": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
|
||||
"integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==",
|
||||
"dev": true
|
||||
"integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
|
||||
},
|
||||
"find-up": {
|
||||
"version": "4.1.0",
|
||||
@ -30796,7 +31220,6 @@
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"parent-module": "^1.0.0",
|
||||
"resolve-from": "^4.0.0"
|
||||
@ -30805,8 +31228,7 @@
|
||||
"resolve-from": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
|
||||
"dev": true
|
||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -33068,8 +33490,7 @@
|
||||
"json-parse-even-better-errors": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
|
||||
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
|
||||
"dev": true
|
||||
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
@ -33170,8 +33591,7 @@
|
||||
"lines-and-columns": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
|
||||
},
|
||||
"linked-list": {
|
||||
"version": "0.1.0",
|
||||
@ -35065,7 +35485,6 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"callsites": "^3.0.0"
|
||||
}
|
||||
@ -35074,7 +35493,6 @@
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
||||
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
"error-ex": "^1.3.1",
|
||||
@ -35131,8 +35549,7 @@
|
||||
"path-type": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="
|
||||
},
|
||||
"pend": {
|
||||
"version": "1.2.0",
|
||||
@ -35675,6 +36092,29 @@
|
||||
"tiny-warning": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"react-select": {
|
||||
"version": "5.7.0",
|
||||
"resolved": "https://registry.npmjs.org/react-select/-/react-select-5.7.0.tgz",
|
||||
"integrity": "sha512-lJGiMxCa3cqnUr2Jjtg9YHsaytiZqeNOKeibv6WF5zbK/fPegZ1hg3y/9P1RZVLhqBTs0PfqQLKuAACednYGhQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.12.0",
|
||||
"@emotion/cache": "^11.4.0",
|
||||
"@emotion/react": "^11.8.1",
|
||||
"@floating-ui/dom": "^1.0.1",
|
||||
"@types/react-transition-group": "^4.4.0",
|
||||
"memoize-one": "^6.0.0",
|
||||
"prop-types": "^15.6.0",
|
||||
"react-transition-group": "^4.3.0",
|
||||
"use-isomorphic-layout-effect": "^1.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"memoize-one": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
|
||||
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-shallow-renderer": {
|
||||
"version": "16.14.1",
|
||||
"resolved": "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.14.1.tgz",
|
||||
@ -35685,6 +36125,17 @@
|
||||
"react-is": "^16.12.0 || ^17.0.0"
|
||||
}
|
||||
},
|
||||
"react-transition-group": {
|
||||
"version": "4.4.5",
|
||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
||||
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"dom-helpers": "^5.0.1",
|
||||
"loose-envify": "^1.4.0",
|
||||
"prop-types": "^15.6.2"
|
||||
}
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "2.3.7",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
||||
@ -36054,9 +36505,9 @@
|
||||
}
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.13.9",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
|
||||
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
|
||||
"version": "0.13.11",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
|
||||
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
|
||||
},
|
||||
"regenerator-transform": {
|
||||
"version": "0.15.0",
|
||||
@ -37307,6 +37758,11 @@
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"stylis": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.1.3.tgz",
|
||||
"integrity": "sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA=="
|
||||
},
|
||||
"sudo-prompt": {
|
||||
"version": "9.2.1",
|
||||
"resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.2.1.tgz",
|
||||
@ -38049,6 +38505,12 @@
|
||||
"integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
|
||||
"peer": true
|
||||
},
|
||||
"use-isomorphic-layout-effect": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz",
|
||||
"integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==",
|
||||
"requires": {}
|
||||
},
|
||||
"use-sync-external-store": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
|
||||
@ -38632,6 +39094,11 @@
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
},
|
||||
"yaml": {
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
|
||||
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="
|
||||
},
|
||||
"yargs": {
|
||||
"version": "16.2.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
|
||||
|
@ -93,7 +93,8 @@
|
||||
"react-intl": "^5.20.0",
|
||||
"react-redux": "^7.2.0",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"trim-newlines": "4.0.2"
|
||||
"trim-newlines": "4.0.2",
|
||||
"react-select": "^5.2.2"
|
||||
},
|
||||
"jest": {
|
||||
"testEnvironment": "jsdom",
|
||||
|
@ -6,7 +6,7 @@ exports[`components/boardSelector escape button should unmount the component 1`]
|
||||
class="focalboard-body"
|
||||
>
|
||||
<div
|
||||
class="Dialog dialog-back BoardSelector"
|
||||
class="Dialog dialog-back BoardSelector size--medium"
|
||||
>
|
||||
<div
|
||||
class="backdrop"
|
||||
@ -111,7 +111,7 @@ exports[`components/boardSelector renders with no results 1`] = `
|
||||
class="focalboard-body"
|
||||
>
|
||||
<div
|
||||
class="Dialog dialog-back BoardSelector"
|
||||
class="Dialog dialog-back BoardSelector size--medium"
|
||||
>
|
||||
<div
|
||||
class="backdrop"
|
||||
@ -217,7 +217,7 @@ exports[`components/boardSelector renders with some results 1`] = `
|
||||
class="focalboard-body"
|
||||
>
|
||||
<div
|
||||
class="Dialog dialog-back BoardSelector"
|
||||
class="Dialog dialog-back BoardSelector size--medium"
|
||||
>
|
||||
<div
|
||||
class="backdrop"
|
||||
@ -421,7 +421,7 @@ exports[`components/boardSelector renders without start searching 1`] = `
|
||||
class="focalboard-body"
|
||||
>
|
||||
<div
|
||||
class="Dialog dialog-back BoardSelector"
|
||||
class="Dialog dialog-back BoardSelector size--medium"
|
||||
>
|
||||
<div
|
||||
class="backdrop"
|
||||
|
@ -0,0 +1,27 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`components/createBoardFromTemplate renders the Create Boards from template component and match snapshot 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="CreateBoardFromTemplate"
|
||||
>
|
||||
<div
|
||||
class="add-board-to-channel"
|
||||
>
|
||||
<label>
|
||||
<input
|
||||
data-testid="add-board-to-channel-check"
|
||||
id="add-board-to-channel"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span>
|
||||
Create a board for this channel
|
||||
</span>
|
||||
<i
|
||||
class="icon-information-outline"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -0,0 +1,78 @@
|
||||
.CreateBoardFromTemplate {
|
||||
width: 100%;
|
||||
|
||||
.add-board-to-channel {
|
||||
display: flex;
|
||||
margin-top: 24px;
|
||||
padding-bottom: 5px;
|
||||
flex-direction: column;
|
||||
|
||||
label {
|
||||
display: flex;
|
||||
color: rgba(var(--center-channel-color-rgb), 0.8);
|
||||
cursor: pointer;
|
||||
font-weight: 400;
|
||||
|
||||
input[type=checkbox] {
|
||||
margin-top: 0 !important;
|
||||
-moz-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
-o-appearance: none;
|
||||
content: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input[type=checkbox]::before {
|
||||
display: block;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border: 1px solid rgba(var(--center-channel-color-rgb), 0.24);
|
||||
margin-right: 7px;
|
||||
background: var(--center-channel-bg);
|
||||
border-radius: 2px;
|
||||
color: transparent !important;
|
||||
content: "\f00c";
|
||||
font-family: "FontAwesome";
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
input[type=checkbox]:checked::before {
|
||||
background: var(--button-bg);
|
||||
color: var(--center-channel-bg) !important;
|
||||
}
|
||||
|
||||
span {
|
||||
margin-top: -3px;
|
||||
}
|
||||
}
|
||||
|
||||
i.icon-information-outline {
|
||||
color: rgba(var(--center-channel-color-rgb), 0.7);
|
||||
margin-top: -3px;
|
||||
margin-left: 3px;
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.templates-selector {
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.CreateBoardFromTemplate--templates-selector__menu-portal {
|
||||
&__option {
|
||||
&__icon {
|
||||
display: inline-block;
|
||||
width: 19px;
|
||||
}
|
||||
&__title {
|
||||
margin-left: 10px;
|
||||
}
|
||||
&__description {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
margin-left: 29px;
|
||||
color: rgba(var(--center-channel-color-rgb), 0.5);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
import React from 'react'
|
||||
import {Provider as ReduxProvider} from 'react-redux'
|
||||
import {render, screen, act} from '@testing-library/react'
|
||||
|
||||
import userEvent from '@testing-library/user-event'
|
||||
|
||||
import {mockStateStore} from '../../../../webapp/src/testUtils'
|
||||
import {wrapIntl} from '../../../../webapp/src/testUtils'
|
||||
|
||||
import CreateBoardFromTemplate from './createBoardFromTemplate'
|
||||
|
||||
jest.mock('../../../../webapp/src/hooks/useGetAllTemplates', () => ({
|
||||
useGetAllTemplates: () => [{id: 'id', title: 'title', description: 'description', icon: '🍔'}]
|
||||
}))
|
||||
|
||||
describe('components/createBoardFromTemplate', () => {
|
||||
const state = {
|
||||
language: {
|
||||
value: 'en',
|
||||
},
|
||||
}
|
||||
|
||||
it('renders the Create Boards from template component and match snapshot', async () => {
|
||||
const store = mockStateStore([], state)
|
||||
let container: Element | DocumentFragment | null = null
|
||||
const setCanCreate = jest.fn
|
||||
const setAction = jest.fn
|
||||
const newBoardInfoIcon = (<i className="icon-information-outline" />)
|
||||
|
||||
await act(async () => {
|
||||
const result = render(wrapIntl(
|
||||
<ReduxProvider store={store}>
|
||||
<CreateBoardFromTemplate
|
||||
setAction={setAction}
|
||||
setCanCreate={setCanCreate}
|
||||
newBoardInfoIcon={newBoardInfoIcon}
|
||||
/>
|
||||
</ReduxProvider>
|
||||
))
|
||||
container = result.container
|
||||
})
|
||||
|
||||
expect(container).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('clicking checkbox toggles the templates selector', async () => {
|
||||
const store = mockStateStore([], state)
|
||||
const setCanCreate = jest.fn
|
||||
const setAction = jest.fn
|
||||
const newBoardInfoIcon = (<i className="icon-information-outline" />)
|
||||
|
||||
await act(async () => {
|
||||
render(wrapIntl(
|
||||
<ReduxProvider store={store}>
|
||||
<CreateBoardFromTemplate
|
||||
setAction={setAction}
|
||||
setCanCreate={setCanCreate}
|
||||
newBoardInfoIcon={newBoardInfoIcon}
|
||||
/>
|
||||
</ReduxProvider>
|
||||
))
|
||||
})
|
||||
|
||||
// click to show the template selector
|
||||
let checkbox = screen.getByRole('checkbox', {checked: false})
|
||||
await act(async () => {
|
||||
await userEvent.click(checkbox)
|
||||
const templatesSelector = screen.queryByText('Select a template')
|
||||
expect(templatesSelector).toBeTruthy()
|
||||
})
|
||||
|
||||
// click to hide the template selector
|
||||
checkbox = screen.getByRole('checkbox', {checked: true})
|
||||
await act(async () => {
|
||||
await userEvent.click(checkbox)
|
||||
const templatesSelector = screen.queryByText('Select a template')
|
||||
expect(templatesSelector).toBeNull()
|
||||
})
|
||||
|
||||
})
|
||||
})
|
@ -0,0 +1,261 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {useCallback, useEffect, useRef, useState} from 'react'
|
||||
|
||||
import {createIntl, createIntlCache, IntlProvider} from 'react-intl'
|
||||
|
||||
import Select from 'react-select/async'
|
||||
import {components, FormatOptionLabelMeta, GroupBase, PlaceholderProps} from 'react-select'
|
||||
import {SingleValue} from 'react-select'
|
||||
|
||||
import {CSSObject} from '@emotion/serialize'
|
||||
|
||||
import {getCurrentLanguage, getMessages} from '../../../../webapp/src/i18n'
|
||||
import {getLanguage} from '../../../../webapp/src/store/language'
|
||||
import CompassIcon from '../../../../webapp/src/widgets/icons/compassIcon'
|
||||
import {useAppSelector} from '../../../../webapp/src/store/hooks'
|
||||
import {mutator} from '../../../../webapp/src/mutator'
|
||||
import {useGetAllTemplates} from '../../../../webapp/src/hooks/useGetAllTemplates'
|
||||
|
||||
import './createBoardFromTemplate.scss'
|
||||
import {Board} from '../../../../webapp/src/blocks/board'
|
||||
|
||||
type Props = {
|
||||
setCanCreate: (canCreate: boolean) => void;
|
||||
setAction: (fn: () => (channelId: string, teamId: string) => Promise<Board | undefined>) => void;
|
||||
newBoardInfoIcon: React.ReactNode;
|
||||
}
|
||||
|
||||
type ReactSelectItem = {
|
||||
id: string;
|
||||
title: string;
|
||||
icon?: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
const EMPTY_BOARD = 'empty_board'
|
||||
const TEMPLATE_DESCRIPTION_LENGTH = 70
|
||||
|
||||
const cache = createIntlCache()
|
||||
const intl = createIntl({
|
||||
locale: getCurrentLanguage(),
|
||||
messages: getMessages(getCurrentLanguage())
|
||||
}, cache)
|
||||
|
||||
const {ValueContainer, Placeholder} = components
|
||||
|
||||
const CreateBoardFromTemplate = (props: Props) => {
|
||||
const {formatMessage} = intl
|
||||
|
||||
const [addBoard, setAddBoard] = useState(false)
|
||||
const allTemplates = useGetAllTemplates()
|
||||
const [selectedBoardTemplateId, setSelectedBoardTemplateId] = useState<string>('')
|
||||
|
||||
const addBoardRef = useRef(false)
|
||||
addBoardRef.current = addBoard
|
||||
const templateIdRef = useRef('')
|
||||
templateIdRef.current = selectedBoardTemplateId
|
||||
|
||||
|
||||
const showNewBoardTemplateSelector = async () => {
|
||||
setAddBoard((prev: boolean) => !prev)
|
||||
}
|
||||
|
||||
// CreateBoardFromTemplate
|
||||
const addBoardToChannel = async (channelId: string, teamId: string) => {
|
||||
if (!addBoardRef.current || !templateIdRef.current) {
|
||||
return
|
||||
}
|
||||
|
||||
const ACTION_DESCRIPTION = 'board created from channel'
|
||||
const LINKED_CHANNEL = 'linked channel'
|
||||
const asTemplate = false
|
||||
|
||||
let boardsAndBlocks = undefined
|
||||
|
||||
if (selectedBoardTemplateId === EMPTY_BOARD) {
|
||||
boardsAndBlocks = await mutator.addEmptyBoard(teamId, intl)
|
||||
} else {
|
||||
boardsAndBlocks = await mutator.duplicateBoard(templateIdRef.current as string, ACTION_DESCRIPTION, asTemplate, undefined, undefined, teamId)
|
||||
}
|
||||
|
||||
const board = boardsAndBlocks.boards[0]
|
||||
await mutator.updateBoard({...board, channelId: channelId}, board, LINKED_CHANNEL)
|
||||
return board
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
props.setAction(() => addBoardToChannel)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
props.setCanCreate(!addBoard || (addBoard && selectedBoardTemplateId !== ''))
|
||||
}, [addBoard, selectedBoardTemplateId])
|
||||
|
||||
const getSubstringWithCompleteWords = (str: string, len: number) => {
|
||||
if (str?.length <= len) {
|
||||
return str
|
||||
}
|
||||
|
||||
// get the final part of the string in order to find the next whitespace if any
|
||||
const finalStringPart = str.substring(len)
|
||||
const wordBreakingIndex = finalStringPart.indexOf(' ')
|
||||
|
||||
// if there is no whitespace is because the lenght in this case falls into an entire word and doesn't affect the display, so just return it
|
||||
if (wordBreakingIndex === -1) {
|
||||
return str
|
||||
}
|
||||
return `${str.substring(0, (len + wordBreakingIndex))}…`
|
||||
}
|
||||
|
||||
const formatOptionLabel = ({ id, title, icon, description }: ReactSelectItem, optionLabel: FormatOptionLabelMeta<ReactSelectItem>) => {
|
||||
const cssPrefix = 'CreateBoardFromTemplate--templates-selector__menu-portal__option'
|
||||
|
||||
const descriptionLabel = description ? getSubstringWithCompleteWords(description, TEMPLATE_DESCRIPTION_LENGTH) : 'ㅤ'
|
||||
|
||||
const templateDescription = (
|
||||
<span className={`${cssPrefix}__description`}>
|
||||
{descriptionLabel}
|
||||
</span>
|
||||
)
|
||||
|
||||
// do not show the description for the selected option so the input only show the icon and title of the template
|
||||
const selectedOption = id === optionLabel.selectValue[0]?.id
|
||||
return (
|
||||
<div key={id}>
|
||||
<span className={`${cssPrefix}__icon`}>
|
||||
{icon || <CompassIcon icon='product-boards'/>}
|
||||
</span>
|
||||
<span className={`${cssPrefix}__title`}>
|
||||
{title}
|
||||
</span>
|
||||
{!selectedOption && templateDescription}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const CustomValueContainer = ({ children, ...props }: any) => {
|
||||
return (
|
||||
<ValueContainer {...props}>
|
||||
<Placeholder {...props}>
|
||||
{props.selectProps.placeholder}
|
||||
</Placeholder>
|
||||
{React.Children.map(children, (child) =>
|
||||
child && child.type !== Placeholder ? child : null
|
||||
)}
|
||||
</ValueContainer>
|
||||
)
|
||||
}
|
||||
|
||||
const loadOptions = useCallback(async (value = '') => {
|
||||
let templates = allTemplates.map((template) => {
|
||||
return {
|
||||
id: template.id,
|
||||
title: template.title,
|
||||
icon: template.icon,
|
||||
description: template.description,
|
||||
}
|
||||
})
|
||||
|
||||
const emptyBoard = {
|
||||
id: EMPTY_BOARD,
|
||||
title: formatMessage({id: 'new_channel_modal.create_board.empty_board_title', defaultMessage: 'Empty board'}),
|
||||
icon: '',
|
||||
description: formatMessage({id: 'new_channel_modal.create_board.empty_board_description', defaultMessage: 'Create a new empty board'}),
|
||||
}
|
||||
|
||||
templates.push(emptyBoard)
|
||||
|
||||
if (value !== '') {
|
||||
templates = templates.filter(template => template.title.toLowerCase().includes(value.toLowerCase()))
|
||||
}
|
||||
return templates
|
||||
}, [allTemplates])
|
||||
|
||||
const onChange = useCallback((item: SingleValue<ReactSelectItem>) => {
|
||||
if (item) {
|
||||
setSelectedBoardTemplateId(item.id)
|
||||
}
|
||||
}, [setSelectedBoardTemplateId])
|
||||
|
||||
const selectorStyles = {
|
||||
menu: (baseStyles: CSSObject): CSSObject => ({
|
||||
...baseStyles,
|
||||
height: '164px',
|
||||
}),
|
||||
menuList: (baseStyles: CSSObject): CSSObject => ({
|
||||
...baseStyles,
|
||||
height: '160px',
|
||||
}),
|
||||
menuPortal: (baseStyles: CSSObject): CSSObject => ({
|
||||
...baseStyles,
|
||||
zIndex: 9999,
|
||||
}),
|
||||
valueContainer: (baseStyles: CSSObject): CSSObject => ({
|
||||
...baseStyles,
|
||||
overflow: 'visible'
|
||||
}),
|
||||
placeholder: (baseStyles: CSSObject, state: PlaceholderProps<ReactSelectItem, false, GroupBase<ReactSelectItem>>): CSSObject => {
|
||||
const modifyPlaceholder = state.selectProps.menuIsOpen || (!state.selectProps.menuIsOpen && state.hasValue)
|
||||
return {
|
||||
...baseStyles,
|
||||
position: 'absolute',
|
||||
backgroundColor: 'var(--sys-center-channel-bg)',
|
||||
padding: '0 3px',
|
||||
top: modifyPlaceholder ? -15 : '18%',
|
||||
transition: 'top 0.5s, font-size 0.5s, color 0.5s',
|
||||
fontSize: modifyPlaceholder ? 10 : 16,
|
||||
color: modifyPlaceholder ? 'var(--sidebar-text-active-border)' : 'rgba(var(--center-channel-color-rgb), 0.42)',
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='CreateBoardFromTemplate'>
|
||||
<div className='add-board-to-channel'>
|
||||
<label>
|
||||
<input
|
||||
type='checkbox'
|
||||
onChange={showNewBoardTemplateSelector}
|
||||
checked={addBoard}
|
||||
id={'add-board-to-channel'}
|
||||
data-testid='add-board-to-channel-check'
|
||||
/>
|
||||
<span>
|
||||
{formatMessage({id: 'new_channel_modal.create_board.title', defaultMessage: 'Create a board for this channel'})}
|
||||
</span>
|
||||
{props.newBoardInfoIcon}
|
||||
</label>
|
||||
{addBoard && <div className='templates-selector'>
|
||||
<Select
|
||||
classNamePrefix={'CreateBoardFromTemplate--templates-selector'}
|
||||
placeholder={formatMessage({id: 'new_channel_modal.create_board.select_template_placeholder', defaultMessage: 'Select a template'})}
|
||||
onChange={onChange}
|
||||
components={{IndicatorSeparator: () => null, ValueContainer: CustomValueContainer}}
|
||||
loadOptions={loadOptions}
|
||||
getOptionValue={(v) => v.id}
|
||||
getOptionLabel={(v) => v.title}
|
||||
formatOptionLabel={formatOptionLabel}
|
||||
styles={selectorStyles}
|
||||
menuPortalTarget={document.body}
|
||||
defaultOptions={true}
|
||||
/>
|
||||
</div>}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const IntlCreateBoardFromTemplate = (props: Props) => {
|
||||
const language = useAppSelector<string>(getLanguage)
|
||||
return (
|
||||
<IntlProvider
|
||||
locale={language.split(/[_]/)[0]}
|
||||
messages={getMessages(language)}
|
||||
>
|
||||
<CreateBoardFromTemplate {...props}/>
|
||||
</IntlProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export default IntlCreateBoardFromTemplate
|
@ -39,6 +39,7 @@ import '../../../webapp/src/styles/main.scss'
|
||||
import '../../../webapp/src/styles/labels.scss'
|
||||
import octoClient from '../../../webapp/src/octoClient'
|
||||
import {Constants} from '../../../webapp/src/constants'
|
||||
import {Board} from '../../../webapp/src/blocks/board'
|
||||
|
||||
import appBarIcon from '../../../webapp/static/app-bar-icon.png'
|
||||
|
||||
@ -66,6 +67,7 @@ import {PluginRegistry} from './types/mattermost-webapp'
|
||||
|
||||
import './plugin.scss'
|
||||
import CloudUpgradeNudge from "./components/cloudUpgradeNudge/cloudUpgradeNudge"
|
||||
import CreateBoardFromTemplate from './components/createBoardFromTemplate'
|
||||
|
||||
function getSubpath(siteURL: string): string {
|
||||
const url = new URL(siteURL)
|
||||
@ -336,6 +338,22 @@ export default class Plugin {
|
||||
this.registry.registerAppBarComponent(Utils.buildURL(appBarIcon, true), () => mmStore.dispatch(toggleRHSPlugin), intl.formatMessage({id: 'AppBar.Tooltip', defaultMessage: 'Toggle Linked Boards'}))
|
||||
}
|
||||
|
||||
if (this.registry.registerActionAfterChannelCreation) {
|
||||
this.registry.registerActionAfterChannelCreation((props: {
|
||||
setCanCreate: (canCreate: boolean) => void,
|
||||
setAction: (fn: () => (channelId: string, teamId: string) => Promise<Board | undefined>) => void,
|
||||
newBoardInfoIcon: React.ReactNode,
|
||||
}) => (
|
||||
<ReduxProvider store={store}>
|
||||
<CreateBoardFromTemplate
|
||||
setCanCreate={props.setCanCreate}
|
||||
setAction={props.setAction}
|
||||
newBoardInfoIcon={props.newBoardInfoIcon}
|
||||
/>
|
||||
</ReduxProvider>
|
||||
))
|
||||
}
|
||||
|
||||
this.registry.registerPostWillRenderEmbedComponent(
|
||||
(embed) => embed.type === 'boards',
|
||||
(props: {embed: {data: string}, webSocketClient: MMWebSocketClient}) => (
|
||||
|
@ -20,5 +20,7 @@ export interface PluginRegistry {
|
||||
registerInsightsHandler(handler: (timeRange: string, page: number, perPage: number, teamId: string, insightType: string) => void)
|
||||
registerSiteStatisticsHandler(handler: () => void)
|
||||
|
||||
registerActionAfterChannelCreation(component: React.Element)
|
||||
|
||||
// Add more if needed from https://developers.mattermost.com/extend/plugins/webapp/reference
|
||||
}
|
||||
|
@ -172,6 +172,7 @@ if (TARGET_IS_PRODUCT) {
|
||||
|
||||
config.output = {
|
||||
path: path.join(__dirname, '/dist'),
|
||||
chunkFilename: '[name].[contenthash].js',
|
||||
};
|
||||
} else {
|
||||
config.resolve.alias['react-intl'] = path.resolve(__dirname, '../../webapp/node_modules/react-intl/');
|
||||
|
@ -27,10 +27,7 @@ linters:
|
||||
enable:
|
||||
- gofmt
|
||||
- goimports
|
||||
- deadcode
|
||||
- ineffassign
|
||||
- structcheck
|
||||
- varcheck
|
||||
- unparam
|
||||
- errcheck
|
||||
- govet
|
||||
|
@ -97,6 +97,7 @@ func (a *API) RegisterRoutes(r *mux.Router) {
|
||||
a.registerBlocksRoutes(apiv2)
|
||||
a.registerContentBlocksRoutes(apiv2)
|
||||
a.registerStatisticsRoutes(apiv2)
|
||||
a.registerComplianceRoutes(apiv2)
|
||||
|
||||
// V3 routes
|
||||
a.registerCardsRoutes(apiv2)
|
||||
@ -220,7 +221,7 @@ func stringResponse(w http.ResponseWriter, message string) {
|
||||
_, _ = fmt.Fprint(w, message)
|
||||
}
|
||||
|
||||
func jsonStringResponse(w http.ResponseWriter, code int, message string) {
|
||||
func jsonStringResponse(w http.ResponseWriter, code int, message string) { //nolint:unparam
|
||||
setResponseHeader(w, "Content-Type", "application/json")
|
||||
w.WriteHeader(code)
|
||||
fmt.Fprint(w, message)
|
||||
@ -232,7 +233,7 @@ func jsonBytesResponse(w http.ResponseWriter, code int, json []byte) {
|
||||
_, _ = w.Write(json)
|
||||
}
|
||||
|
||||
func setResponseHeader(w http.ResponseWriter, key string, value string) {
|
||||
func setResponseHeader(w http.ResponseWriter, key string, value string) { //nolint:unparam
|
||||
header := w.Header()
|
||||
if header == nil {
|
||||
return
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"github.com/mattermost/focalboard/server/model"
|
||||
"github.com/mattermost/focalboard/server/services/audit"
|
||||
|
||||
mmModel "github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||
)
|
||||
|
||||
@ -55,9 +56,15 @@ func (a *API) handleArchiveExportBoard(w http.ResponseWriter, r *http.Request) {
|
||||
boardID := vars["boardID"]
|
||||
userID := getUserID(r)
|
||||
|
||||
// check user has permission to board
|
||||
if !a.permissions.HasPermissionToBoard(userID, boardID, model.PermissionViewBoard) {
|
||||
a.errorResponse(w, r, model.NewErrPermission("access denied to board"))
|
||||
return
|
||||
// if this user has `manage_system` permission and there is a license with the compliance
|
||||
// feature enabled, then we will allow the export.
|
||||
license := a.app.GetLicense()
|
||||
if !a.permissions.HasPermissionTo(userID, mmModel.PermissionManageSystem) || license == nil || !(*license.Features.Compliance) {
|
||||
a.errorResponse(w, r, model.NewErrPermission("access denied to board"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
auditRec := a.makeAuditRecord(r, "archiveExportBoard", audit.Fail)
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
// makeAuditRecord creates an audit record pre-populated with data from the request.
|
||||
func (a *API) makeAuditRecord(r *http.Request, event string, initialStatus string) *audit.Record {
|
||||
func (a *API) makeAuditRecord(r *http.Request, event string, initialStatus string) *audit.Record { //nolint:unparam
|
||||
ctx := r.Context()
|
||||
var sessionID string
|
||||
var userID string
|
||||
|
@ -383,12 +383,13 @@ func (a *API) attachSession(handler func(w http.ResponseWriter, r *http.Request)
|
||||
|
||||
authService := session.AuthService
|
||||
if authService != a.authService {
|
||||
a.logger.Error(`Session authService mismatch`,
|
||||
msg := `Session authService mismatch`
|
||||
a.logger.Error(msg,
|
||||
mlog.String("sessionID", session.ID),
|
||||
mlog.String("want", a.authService),
|
||||
mlog.String("got", authService),
|
||||
)
|
||||
a.errorResponse(w, r, model.NewErrUnauthorized(err.Error()))
|
||||
a.errorResponse(w, r, model.NewErrUnauthorized(msg))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,8 @@ func (a *API) registerCategoriesRoutes(r *mux.Router) {
|
||||
r.HandleFunc("/teams/{teamID}/categories", a.sessionRequired(a.handleGetUserCategoryBoards)).Methods(http.MethodGet)
|
||||
r.HandleFunc("/teams/{teamID}/categories/{categoryID}/boards/reorder", a.sessionRequired(a.handleReorderCategoryBoards)).Methods(http.MethodPut)
|
||||
r.HandleFunc("/teams/{teamID}/categories/{categoryID}/boards/{boardID}", a.sessionRequired(a.handleUpdateCategoryBoard)).Methods(http.MethodPost)
|
||||
r.HandleFunc("/teams/{teamID}/categories/{categoryID}/boards/{boardID}/hide", a.sessionRequired(a.handleHideBoard)).Methods(http.MethodPut)
|
||||
r.HandleFunc("/teams/{teamID}/categories/{categoryID}/boards/{boardID}/unhide", a.sessionRequired(a.handleUnhideBoard)).Methods(http.MethodPut)
|
||||
}
|
||||
|
||||
func (a *API) handleCreateCategory(w http.ResponseWriter, r *http.Request) {
|
||||
@ -89,6 +91,11 @@ func (a *API) handleCreateCategory(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if !a.permissions.HasPermissionToTeam(session.UserID, teamID, model.PermissionViewTeam) {
|
||||
a.errorResponse(w, r, model.NewErrPermission("access denied to team"))
|
||||
return
|
||||
}
|
||||
|
||||
createdCategory, err := a.app.CreateCategory(&category)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, err)
|
||||
@ -182,6 +189,11 @@ func (a *API) handleUpdateCategory(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if !a.permissions.HasPermissionToTeam(session.UserID, teamID, model.PermissionViewTeam) {
|
||||
a.errorResponse(w, r, model.NewErrPermission("access denied to team"))
|
||||
return
|
||||
}
|
||||
|
||||
updatedCategory, err := a.app.UpdateCategory(&category)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, err)
|
||||
@ -238,6 +250,11 @@ func (a *API) handleDeleteCategory(w http.ResponseWriter, r *http.Request) {
|
||||
auditRec := a.makeAuditRecord(r, "deleteCategory", audit.Fail)
|
||||
defer a.audit.LogRecord(audit.LevelModify, auditRec)
|
||||
|
||||
if !a.permissions.HasPermissionToTeam(session.UserID, teamID, model.PermissionViewTeam) {
|
||||
a.errorResponse(w, r, model.NewErrPermission("access denied to team"))
|
||||
return
|
||||
}
|
||||
|
||||
deletedCategory, err := a.app.DeleteCategory(categoryID, userID, teamID)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, err)
|
||||
@ -292,6 +309,11 @@ func (a *API) handleGetUserCategoryBoards(w http.ResponseWriter, r *http.Request
|
||||
auditRec := a.makeAuditRecord(r, "getUserCategoryBoards", audit.Fail)
|
||||
defer a.audit.LogRecord(audit.LevelModify, auditRec)
|
||||
|
||||
if !a.permissions.HasPermissionToTeam(session.UserID, teamID, model.PermissionViewTeam) {
|
||||
a.errorResponse(w, r, model.NewErrPermission("access denied to team"))
|
||||
return
|
||||
}
|
||||
|
||||
categoryBlocks, err := a.app.GetUserCategoryBoards(userID, teamID)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, err)
|
||||
@ -354,8 +376,13 @@ func (a *API) handleUpdateCategoryBoard(w http.ResponseWriter, r *http.Request)
|
||||
session := ctx.Value(sessionContextKey).(*model.Session)
|
||||
userID := session.UserID
|
||||
|
||||
if !a.permissions.HasPermissionToTeam(session.UserID, teamID, model.PermissionViewTeam) {
|
||||
a.errorResponse(w, r, model.NewErrPermission("access denied to team"))
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Check the category and the team matches
|
||||
err := a.app.AddUpdateUserCategoryBoard(teamID, userID, map[string]string{boardID: categoryID})
|
||||
err := a.app.AddUpdateUserCategoryBoard(teamID, userID, categoryID, []string{boardID})
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, err)
|
||||
return
|
||||
@ -521,3 +548,125 @@ func (a *API) handleReorderCategoryBoards(w http.ResponseWriter, r *http.Request
|
||||
jsonBytesResponse(w, http.StatusOK, data)
|
||||
auditRec.Success()
|
||||
}
|
||||
|
||||
func (a *API) handleHideBoard(w http.ResponseWriter, r *http.Request) {
|
||||
// swagger:operation POST /teams/{teamID}/categories/{categoryID}/boards/{boardID}/hide hideBoard
|
||||
//
|
||||
// Hide the specified board for the user
|
||||
//
|
||||
// ---
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: teamID
|
||||
// in: path
|
||||
// description: Team ID
|
||||
// required: true
|
||||
// type: string
|
||||
// - name: categoryID
|
||||
// in: path
|
||||
// description: Category ID to which the board to be hidden belongs to
|
||||
// required: true
|
||||
// type: string
|
||||
// - name: boardID
|
||||
// in: path
|
||||
// description: ID of board to be hidden
|
||||
// required: true
|
||||
// type: string
|
||||
// security:
|
||||
// - BearerAuth: []
|
||||
// responses:
|
||||
// '200':
|
||||
// description: success
|
||||
// schema:
|
||||
// "$ref": "#/definitions/Category"
|
||||
// default:
|
||||
// description: internal error
|
||||
// schema:
|
||||
// "$ref": "#/definitions/ErrorResponse"
|
||||
|
||||
userID := getUserID(r)
|
||||
vars := mux.Vars(r)
|
||||
teamID := vars["teamID"]
|
||||
boardID := vars["boardID"]
|
||||
categoryID := vars["categoryID"]
|
||||
|
||||
if !a.permissions.HasPermissionToTeam(userID, teamID, model.PermissionViewTeam) {
|
||||
a.errorResponse(w, r, model.NewErrPermission("access denied to category"))
|
||||
return
|
||||
}
|
||||
|
||||
auditRec := a.makeAuditRecord(r, "hideBoard", audit.Fail)
|
||||
defer a.audit.LogRecord(audit.LevelModify, auditRec)
|
||||
auditRec.AddMeta("board_id", boardID)
|
||||
auditRec.AddMeta("team_id", teamID)
|
||||
auditRec.AddMeta("category_id", categoryID)
|
||||
|
||||
if err := a.app.SetBoardVisibility(teamID, userID, categoryID, boardID, false); err != nil {
|
||||
a.errorResponse(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
jsonStringResponse(w, http.StatusOK, "{}")
|
||||
auditRec.Success()
|
||||
}
|
||||
|
||||
func (a *API) handleUnhideBoard(w http.ResponseWriter, r *http.Request) {
|
||||
// swagger:operation POST /teams/{teamID}/categories/{categoryID}/boards/{boardID}/hide unhideBoard
|
||||
//
|
||||
// Unhides the specified board for the user
|
||||
//
|
||||
// ---
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: teamID
|
||||
// in: path
|
||||
// description: Team ID
|
||||
// required: true
|
||||
// type: string
|
||||
// - name: categoryID
|
||||
// in: path
|
||||
// description: Category ID to which the board to be unhidden belongs to
|
||||
// required: true
|
||||
// type: string
|
||||
// - name: boardID
|
||||
// in: path
|
||||
// description: ID of board to be unhidden
|
||||
// required: true
|
||||
// type: string
|
||||
// security:
|
||||
// - BearerAuth: []
|
||||
// responses:
|
||||
// '200':
|
||||
// description: success
|
||||
// schema:
|
||||
// "$ref": "#/definitions/Category"
|
||||
// default:
|
||||
// description: internal error
|
||||
// schema:
|
||||
// "$ref": "#/definitions/ErrorResponse"
|
||||
|
||||
userID := getUserID(r)
|
||||
vars := mux.Vars(r)
|
||||
teamID := vars["teamID"]
|
||||
boardID := vars["boardID"]
|
||||
categoryID := vars["categoryID"]
|
||||
|
||||
if !a.permissions.HasPermissionToTeam(userID, teamID, model.PermissionViewTeam) {
|
||||
a.errorResponse(w, r, model.NewErrPermission("access denied to category"))
|
||||
return
|
||||
}
|
||||
|
||||
auditRec := a.makeAuditRecord(r, "unhideBoard", audit.Fail)
|
||||
defer a.audit.LogRecord(audit.LevelModify, auditRec)
|
||||
auditRec.AddMeta("boardID", boardID)
|
||||
|
||||
if err := a.app.SetBoardVisibility(teamID, userID, categoryID, boardID, true); err != nil {
|
||||
a.errorResponse(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
jsonStringResponse(w, http.StatusOK, "{}")
|
||||
auditRec.Success()
|
||||
}
|
||||
|
447
server/api/compliance.go
Normal file
447
server/api/compliance.go
Normal file
@ -0,0 +1,447 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/mattermost/focalboard/server/model"
|
||||
|
||||
mm_model "github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||
)
|
||||
|
||||
const (
|
||||
complianceDefaultPage = "0"
|
||||
complianceDefaultPerPage = "60"
|
||||
)
|
||||
|
||||
func (a *API) registerComplianceRoutes(r *mux.Router) {
|
||||
// Compliance APIs
|
||||
r.HandleFunc("/admin/boards", a.sessionRequired(a.handleGetBoardsForCompliance)).Methods("GET")
|
||||
r.HandleFunc("/admin/boards_history", a.sessionRequired(a.handleGetBoardsComplianceHistory)).Methods("GET")
|
||||
r.HandleFunc("/admin/blocks_history", a.sessionRequired(a.handleGetBlocksComplianceHistory)).Methods("GET")
|
||||
}
|
||||
|
||||
func (a *API) handleGetBoardsForCompliance(w http.ResponseWriter, r *http.Request) {
|
||||
// swagger:operation GET /admin/boards getBoardsForCompliance
|
||||
//
|
||||
// Returns boards for a specific team, or all teams.
|
||||
//
|
||||
// Requires a license that includes Compliance feature. Caller must have `manage_system` permissions.
|
||||
//
|
||||
// ---
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: team_id
|
||||
// in: query
|
||||
// description: Team ID. If empty then boards across all teams are included.
|
||||
// required: false
|
||||
// type: string
|
||||
// - name: page
|
||||
// in: query
|
||||
// description: The page to select (default=0)
|
||||
// required: false
|
||||
// type: integer
|
||||
// - name: per_page
|
||||
// in: query
|
||||
// description: Number of boards to return per page(default=60)
|
||||
// required: false
|
||||
// type: integer
|
||||
// security:
|
||||
// - BearerAuth: []
|
||||
// responses:
|
||||
// '200':
|
||||
// description: success
|
||||
// schema:
|
||||
// type: object
|
||||
// items:
|
||||
// "$ref": "#/definitions/BoardsComplianceResponse"
|
||||
// default:
|
||||
// description: internal error
|
||||
// schema:
|
||||
// "$ref": "#/definitions/ErrorResponse"
|
||||
|
||||
query := r.URL.Query()
|
||||
teamID := query.Get("team_id")
|
||||
strPage := query.Get("page")
|
||||
strPerPage := query.Get("per_page")
|
||||
|
||||
// check for permission `manage_system`
|
||||
userID := getUserID(r)
|
||||
if !a.permissions.HasPermissionTo(userID, mm_model.PermissionManageSystem) {
|
||||
a.errorResponse(w, r, model.NewErrUnauthorized("access denied Compliance Export getAllBoards"))
|
||||
return
|
||||
}
|
||||
|
||||
// check for valid license feature: compliance
|
||||
license := a.app.GetLicense()
|
||||
if license == nil || !(*license.Features.Compliance) {
|
||||
a.errorResponse(w, r, model.NewErrNotImplemented("insufficient license Compliance Export getAllBoards"))
|
||||
return
|
||||
}
|
||||
|
||||
// check for valid team if specified
|
||||
if teamID != "" {
|
||||
_, err := a.app.GetTeam(teamID)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, model.NewErrBadRequest("invalid team id: "+teamID))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if strPage == "" {
|
||||
strPage = complianceDefaultPage
|
||||
}
|
||||
if strPerPage == "" {
|
||||
strPerPage = complianceDefaultPerPage
|
||||
}
|
||||
page, err := strconv.Atoi(strPage)
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("invalid `page` parameter: %s", err)
|
||||
a.errorResponse(w, r, model.NewErrBadRequest(message))
|
||||
return
|
||||
}
|
||||
perPage, err := strconv.Atoi(strPerPage)
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("invalid `per_page` parameter: %s", err)
|
||||
a.errorResponse(w, r, model.NewErrBadRequest(message))
|
||||
return
|
||||
}
|
||||
|
||||
opts := model.QueryBoardsForComplianceOptions{
|
||||
TeamID: teamID,
|
||||
Page: page,
|
||||
PerPage: perPage,
|
||||
}
|
||||
|
||||
boards, more, err := a.app.GetBoardsForCompliance(opts)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
a.logger.Debug("GetBoardsForCompliance",
|
||||
mlog.String("teamID", teamID),
|
||||
mlog.Int("boardsCount", len(boards)),
|
||||
mlog.Bool("hasNext", more),
|
||||
)
|
||||
|
||||
response := model.BoardsComplianceResponse{
|
||||
HasNext: more,
|
||||
Results: boards,
|
||||
}
|
||||
data, err := json.Marshal(response)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
jsonBytesResponse(w, http.StatusOK, data)
|
||||
}
|
||||
|
||||
func (a *API) handleGetBoardsComplianceHistory(w http.ResponseWriter, r *http.Request) {
|
||||
// swagger:operation GET /admin/boards_history getBoardsComplianceHistory
|
||||
//
|
||||
// Returns boards histories for a specific team, or all teams.
|
||||
//
|
||||
// Requires a license that includes Compliance feature. Caller must have `manage_system` permissions.
|
||||
//
|
||||
// ---
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: modified_since
|
||||
// in: query
|
||||
// description: Filters for boards modified since timestamp; Unix time in milliseconds
|
||||
// required: true
|
||||
// type: integer
|
||||
// - name: include_deleted
|
||||
// in: query
|
||||
// description: When true then deleted boards are included. Default=false
|
||||
// required: false
|
||||
// type: boolean
|
||||
// - name: team_id
|
||||
// in: query
|
||||
// description: Team ID. If empty then board histories across all teams are included
|
||||
// required: false
|
||||
// type: string
|
||||
// - name: page
|
||||
// in: query
|
||||
// description: The page to select (default=0)
|
||||
// required: false
|
||||
// type: integer
|
||||
// - name: per_page
|
||||
// in: query
|
||||
// description: Number of board histories to return per page (default=60)
|
||||
// required: false
|
||||
// type: integer
|
||||
// security:
|
||||
// - BearerAuth: []
|
||||
// responses:
|
||||
// '200':
|
||||
// description: success
|
||||
// schema:
|
||||
// type: object
|
||||
// items:
|
||||
// "$ref": "#/definitions/BoardsComplianceHistoryResponse"
|
||||
// default:
|
||||
// description: internal error
|
||||
// schema:
|
||||
// "$ref": "#/definitions/ErrorResponse"
|
||||
|
||||
query := r.URL.Query()
|
||||
strModifiedSince := query.Get("modified_since") // required, everything else optional
|
||||
includeDeleted := query.Get("include_deleted") == "true"
|
||||
strPage := query.Get("page")
|
||||
strPerPage := query.Get("per_page")
|
||||
teamID := query.Get("team_id")
|
||||
|
||||
if strModifiedSince == "" {
|
||||
a.errorResponse(w, r, model.NewErrBadRequest("`modified_since` parameter required"))
|
||||
return
|
||||
}
|
||||
|
||||
// check for permission `manage_system`
|
||||
userID := getUserID(r)
|
||||
if !a.permissions.HasPermissionTo(userID, mm_model.PermissionManageSystem) {
|
||||
a.errorResponse(w, r, model.NewErrUnauthorized("access denied Compliance Export getBoardsHistory"))
|
||||
return
|
||||
}
|
||||
|
||||
// check for valid license feature: compliance
|
||||
license := a.app.GetLicense()
|
||||
if license == nil || !(*license.Features.Compliance) {
|
||||
a.errorResponse(w, r, model.NewErrNotImplemented("insufficient license Compliance Export getBoardsHistory"))
|
||||
return
|
||||
}
|
||||
|
||||
// check for valid team if specified
|
||||
if teamID != "" {
|
||||
_, err := a.app.GetTeam(teamID)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, model.NewErrBadRequest("invalid team id: "+teamID))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if strPage == "" {
|
||||
strPage = complianceDefaultPage
|
||||
}
|
||||
if strPerPage == "" {
|
||||
strPerPage = complianceDefaultPerPage
|
||||
}
|
||||
page, err := strconv.Atoi(strPage)
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("invalid `page` parameter: %s", err)
|
||||
a.errorResponse(w, r, model.NewErrBadRequest(message))
|
||||
return
|
||||
}
|
||||
perPage, err := strconv.Atoi(strPerPage)
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("invalid `per_page` parameter: %s", err)
|
||||
a.errorResponse(w, r, model.NewErrBadRequest(message))
|
||||
return
|
||||
}
|
||||
modifiedSince, err := strconv.ParseInt(strModifiedSince, 10, 64)
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("invalid `modified_since` parameter: %s", err)
|
||||
a.errorResponse(w, r, model.NewErrBadRequest(message))
|
||||
return
|
||||
}
|
||||
|
||||
opts := model.QueryBoardsComplianceHistoryOptions{
|
||||
ModifiedSince: modifiedSince,
|
||||
IncludeDeleted: includeDeleted,
|
||||
TeamID: teamID,
|
||||
Page: page,
|
||||
PerPage: perPage,
|
||||
}
|
||||
|
||||
boards, more, err := a.app.GetBoardsComplianceHistory(opts)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
a.logger.Debug("GetBoardsComplianceHistory",
|
||||
mlog.String("teamID", teamID),
|
||||
mlog.Int("boardsCount", len(boards)),
|
||||
mlog.Bool("hasNext", more),
|
||||
)
|
||||
|
||||
response := model.BoardsComplianceHistoryResponse{
|
||||
HasNext: more,
|
||||
Results: boards,
|
||||
}
|
||||
data, err := json.Marshal(response)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
jsonBytesResponse(w, http.StatusOK, data)
|
||||
}
|
||||
|
||||
func (a *API) handleGetBlocksComplianceHistory(w http.ResponseWriter, r *http.Request) {
|
||||
// swagger:operation GET /admin/blocks_history getBlocksComplianceHistory
|
||||
//
|
||||
// Returns block histories for a specific team, specific board, or all teams and boards.
|
||||
//
|
||||
// Requires a license that includes Compliance feature. Caller must have `manage_system` permissions.
|
||||
//
|
||||
// ---
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: modified_since
|
||||
// in: query
|
||||
// description: Filters for boards modified since timestamp; Unix time in milliseconds
|
||||
// required: true
|
||||
// type: integer
|
||||
// - name: include_deleted
|
||||
// in: query
|
||||
// description: When true then deleted boards are included. Default=false
|
||||
// required: false
|
||||
// type: boolean
|
||||
// - name: team_id
|
||||
// in: query
|
||||
// description: Team ID. If empty then block histories across all teams are included
|
||||
// required: false
|
||||
// type: string
|
||||
// - name: board_id
|
||||
// in: query
|
||||
// description: Board ID. If empty then block histories for all boards are included
|
||||
// required: false
|
||||
// type: string
|
||||
// - name: page
|
||||
// in: query
|
||||
// description: The page to select (default=0)
|
||||
// required: false
|
||||
// type: integer
|
||||
// - name: per_page
|
||||
// in: query
|
||||
// description: Number of block histories to return per page (default=60)
|
||||
// required: false
|
||||
// type: integer
|
||||
// security:
|
||||
// - BearerAuth: []
|
||||
// responses:
|
||||
// '200':
|
||||
// description: success
|
||||
// schema:
|
||||
// type: object
|
||||
// items:
|
||||
// "$ref": "#/definitions/BlocksComplianceHistoryResponse"
|
||||
// default:
|
||||
// description: internal error
|
||||
// schema:
|
||||
// "$ref": "#/definitions/ErrorResponse"
|
||||
|
||||
query := r.URL.Query()
|
||||
strModifiedSince := query.Get("modified_since") // required, everything else optional
|
||||
includeDeleted := query.Get("include_deleted") == "true"
|
||||
strPage := query.Get("page")
|
||||
strPerPage := query.Get("per_page")
|
||||
teamID := query.Get("team_id")
|
||||
boardID := query.Get("board_id")
|
||||
|
||||
if strModifiedSince == "" {
|
||||
a.errorResponse(w, r, model.NewErrBadRequest("`modified_since` parameter required"))
|
||||
return
|
||||
}
|
||||
|
||||
// check for permission `manage_system`
|
||||
userID := getUserID(r)
|
||||
if !a.permissions.HasPermissionTo(userID, mm_model.PermissionManageSystem) {
|
||||
a.errorResponse(w, r, model.NewErrUnauthorized("access denied Compliance Export getBlocksHistory"))
|
||||
return
|
||||
}
|
||||
|
||||
// check for valid license feature: compliance
|
||||
license := a.app.GetLicense()
|
||||
if license == nil || !(*license.Features.Compliance) {
|
||||
a.errorResponse(w, r, model.NewErrNotImplemented("insufficient license Compliance Export getBlocksHistory"))
|
||||
return
|
||||
}
|
||||
|
||||
// check for valid team if specified
|
||||
if teamID != "" {
|
||||
_, err := a.app.GetTeam(teamID)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, model.NewErrBadRequest("invalid team id: "+teamID))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// check for valid board if specified
|
||||
if boardID != "" {
|
||||
_, err := a.app.GetBoard(boardID)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, model.NewErrBadRequest("invalid board id: "+boardID))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if strPage == "" {
|
||||
strPage = complianceDefaultPage
|
||||
}
|
||||
if strPerPage == "" {
|
||||
strPerPage = complianceDefaultPerPage
|
||||
}
|
||||
page, err := strconv.Atoi(strPage)
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("invalid `page` parameter: %s", err)
|
||||
a.errorResponse(w, r, model.NewErrBadRequest(message))
|
||||
return
|
||||
}
|
||||
perPage, err := strconv.Atoi(strPerPage)
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("invalid `per_page` parameter: %s", err)
|
||||
a.errorResponse(w, r, model.NewErrBadRequest(message))
|
||||
return
|
||||
}
|
||||
modifiedSince, err := strconv.ParseInt(strModifiedSince, 10, 64)
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("invalid `modified_since` parameter: %s", err)
|
||||
a.errorResponse(w, r, model.NewErrBadRequest(message))
|
||||
return
|
||||
}
|
||||
|
||||
opts := model.QueryBlocksComplianceHistoryOptions{
|
||||
ModifiedSince: modifiedSince,
|
||||
IncludeDeleted: includeDeleted,
|
||||
TeamID: teamID,
|
||||
BoardID: boardID,
|
||||
Page: page,
|
||||
PerPage: perPage,
|
||||
}
|
||||
|
||||
blocks, more, err := a.app.GetBlocksComplianceHistory(opts)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
a.logger.Debug("GetBlocksComplianceHistory",
|
||||
mlog.String("teamID", teamID),
|
||||
mlog.String("boardID", boardID),
|
||||
mlog.Int("blocksCount", len(blocks)),
|
||||
mlog.Bool("hasNext", more),
|
||||
)
|
||||
|
||||
response := model.BlocksComplianceHistoryResponse{
|
||||
HasNext: more,
|
||||
Results: blocks,
|
||||
}
|
||||
data, err := json.Marshal(response)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
jsonBytesResponse(w, http.StatusOK, data)
|
||||
}
|
@ -146,7 +146,7 @@ func (a *API) handleAddMember(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if reqBoardMember.UserID == "" {
|
||||
a.errorResponse(w, r, model.NewErrBadRequest(err.Error()))
|
||||
a.errorResponse(w, r, model.NewErrBadRequest("empty userID"))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -110,3 +110,7 @@ func (a *App) SetCardLimit(cardLimit int) {
|
||||
defer a.cardLimitMux.Unlock()
|
||||
a.cardLimit = cardLimit
|
||||
}
|
||||
|
||||
func (a *App) GetLicense() *mm_model.License {
|
||||
return a.store.GetLicense()
|
||||
}
|
||||
|
@ -154,8 +154,8 @@ func (a *App) setBoardCategoryFromSource(sourceBoardID, destinationBoardID, user
|
||||
var destinationCategoryID string
|
||||
|
||||
for _, categoryBoard := range userCategoryBoards {
|
||||
for _, boardID := range categoryBoard.BoardIDs {
|
||||
if boardID == sourceBoardID {
|
||||
for _, metadata := range categoryBoard.BoardMetadata {
|
||||
if metadata.BoardID == sourceBoardID {
|
||||
// category found!
|
||||
destinationCategoryID = categoryBoard.ID
|
||||
break
|
||||
@ -175,7 +175,7 @@ func (a *App) setBoardCategoryFromSource(sourceBoardID, destinationBoardID, user
|
||||
|
||||
// now that we have source board's category,
|
||||
// we send destination board to the same category
|
||||
return a.AddUpdateUserCategoryBoard(teamID, userID, map[string]string{destinationBoardID: destinationCategoryID})
|
||||
return a.AddUpdateUserCategoryBoard(teamID, userID, destinationCategoryID, []string{destinationBoardID})
|
||||
}
|
||||
|
||||
func (a *App) DuplicateBoard(boardID, userID, toTeam string, asTemplate bool) (*model.BoardsAndBlocks, []*model.BoardMember, error) {
|
||||
@ -329,12 +329,12 @@ func (a *App) addBoardsToDefaultCategory(userID, teamID string, boards []*model.
|
||||
return fmt.Errorf("%w userID: %s", errNoDefaultCategoryFound, userID)
|
||||
}
|
||||
|
||||
boardCategoryMapping := map[string]string{}
|
||||
for _, board := range boards {
|
||||
boardCategoryMapping[board.ID] = defaultCategoryID
|
||||
boardIDs := make([]string, len(boards))
|
||||
for i := range boards {
|
||||
boardIDs[i] = boards[i].ID
|
||||
}
|
||||
|
||||
if err := a.AddUpdateUserCategoryBoard(teamID, userID, boardCategoryMapping); err != nil {
|
||||
if err := a.AddUpdateUserCategoryBoard(teamID, userID, defaultCategoryID, boardIDs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -51,8 +51,8 @@ func TestAddMemberToBoard(t *testing.T) {
|
||||
Type: "system",
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("user_id_1", map[string]string{"board_id_1": "default_category_id"}).Return(nil)
|
||||
}, nil).Times(2)
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("user_id_1", "default_category_id", []string{"board_id_1"}).Return(nil)
|
||||
|
||||
addedBoardMember, err := th.App.AddMemberToBoard(boardMember)
|
||||
require.NoError(t, err)
|
||||
@ -125,8 +125,8 @@ func TestAddMemberToBoard(t *testing.T) {
|
||||
Type: "system",
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("user_id_1", map[string]string{"board_id_1": "default_category_id"}).Return(nil)
|
||||
}, nil).Times(2)
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("user_id_1", "default_category_id", []string{"board_id_1"}).Return(nil)
|
||||
|
||||
addedBoardMember, err := th.App.AddMemberToBoard(boardMember)
|
||||
require.NoError(t, err)
|
||||
@ -411,45 +411,72 @@ func TestBoardCategory(t *testing.T) {
|
||||
th, tearDown := SetupTestHelper(t)
|
||||
defer tearDown()
|
||||
|
||||
t.Run("test addBoardsToDefaultCategory", func(t *testing.T) {
|
||||
t.Run("no boards default category exists", func(t *testing.T) {
|
||||
th.Store.EXPECT().GetUserCategoryBoards("user_id", "team_id").Return([]model.CategoryBoards{
|
||||
{
|
||||
Category: model.Category{ID: "category_id_1", Name: "Category 1"},
|
||||
BoardIDs: []string{"board_id_1", "board_id_2"},
|
||||
t.Run("no boards default category exists", func(t *testing.T) {
|
||||
th.Store.EXPECT().GetUserCategoryBoards("user_id", "team_id").Return([]model.CategoryBoards{
|
||||
{
|
||||
Category: model.Category{ID: "category_id_1", Name: "Category 1"},
|
||||
BoardMetadata: []model.CategoryBoardMetadata{
|
||||
{BoardID: "board_id_1"},
|
||||
{BoardID: "board_id_2"},
|
||||
},
|
||||
{
|
||||
Category: model.Category{ID: "category_id_2", Name: "Category 2"},
|
||||
BoardIDs: []string{"board_id_3"},
|
||||
},
|
||||
{
|
||||
Category: model.Category{ID: "category_id_2", Name: "Category 2"},
|
||||
BoardMetadata: []model.CategoryBoardMetadata{
|
||||
{BoardID: "board_id_3"},
|
||||
},
|
||||
{
|
||||
Category: model.Category{ID: "category_id_3", Name: "Category 3"},
|
||||
BoardIDs: []string{},
|
||||
},
|
||||
{
|
||||
Category: model.Category{ID: "category_id_3", Name: "Category 3"},
|
||||
BoardMetadata: []model.CategoryBoardMetadata{},
|
||||
},
|
||||
}, nil).Times(1)
|
||||
|
||||
// when this function is called the second time, the default category is created
|
||||
th.Store.EXPECT().GetUserCategoryBoards("user_id", "team_id").Return([]model.CategoryBoards{
|
||||
{
|
||||
Category: model.Category{ID: "category_id_1", Name: "Category 1"},
|
||||
BoardMetadata: []model.CategoryBoardMetadata{
|
||||
{BoardID: "board_id_1"},
|
||||
{BoardID: "board_id_2"},
|
||||
},
|
||||
}, nil)
|
||||
},
|
||||
{
|
||||
Category: model.Category{ID: "category_id_2", Name: "Category 2"},
|
||||
BoardMetadata: []model.CategoryBoardMetadata{
|
||||
{BoardID: "board_id_3"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Category: model.Category{ID: "category_id_3", Name: "Category 3"},
|
||||
BoardMetadata: []model.CategoryBoardMetadata{},
|
||||
},
|
||||
{
|
||||
Category: model.Category{ID: "default_category_id", Type: model.CategoryTypeSystem, Name: "Boards"},
|
||||
},
|
||||
}, nil).Times(1)
|
||||
|
||||
th.Store.EXPECT().CreateCategory(utils.Anything).Return(nil)
|
||||
th.Store.EXPECT().GetCategory(utils.Anything).Return(&model.Category{
|
||||
ID: "default_category_id",
|
||||
Name: "Boards",
|
||||
}, nil)
|
||||
th.Store.EXPECT().GetMembersForUser("user_id").Return([]*model.BoardMember{}, nil)
|
||||
th.Store.EXPECT().GetBoardsForUserAndTeam("user_id", "team_id", false).Return([]*model.Board{}, nil)
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("user_id", map[string]string{
|
||||
"board_id_1": "default_category_id",
|
||||
"board_id_2": "default_category_id",
|
||||
"board_id_3": "default_category_id",
|
||||
}).Return(nil)
|
||||
th.Store.EXPECT().CreateCategory(utils.Anything).Return(nil)
|
||||
th.Store.EXPECT().GetCategory(utils.Anything).Return(&model.Category{
|
||||
ID: "default_category_id",
|
||||
Name: "Boards",
|
||||
}, nil)
|
||||
th.Store.EXPECT().GetMembersForUser("user_id").Return([]*model.BoardMember{}, nil)
|
||||
th.Store.EXPECT().GetBoardsForUserAndTeam("user_id", "team_id", false).Return([]*model.Board{}, nil)
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("user_id", "default_category_id", []string{
|
||||
"board_id_1",
|
||||
"board_id_2",
|
||||
"board_id_3",
|
||||
}).Return(nil)
|
||||
|
||||
boards := []*model.Board{
|
||||
{ID: "board_id_1"},
|
||||
{ID: "board_id_2"},
|
||||
{ID: "board_id_3"},
|
||||
}
|
||||
boards := []*model.Board{
|
||||
{ID: "board_id_1"},
|
||||
{ID: "board_id_2"},
|
||||
{ID: "board_id_3"},
|
||||
}
|
||||
|
||||
err := th.App.addBoardsToDefaultCategory("user_id", "team_id", boards)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
err := th.App.addBoardsToDefaultCategory("user_id", "team_id", boards)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
@ -491,9 +518,9 @@ func TestDuplicateBoard(t *testing.T) {
|
||||
Type: "system",
|
||||
},
|
||||
},
|
||||
}, nil).Times(2)
|
||||
}, nil).Times(3)
|
||||
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("user_id_1", utils.Anything).Return(nil)
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("user_id_1", "category_id_1", utils.Anything).Return(nil)
|
||||
|
||||
// for WS change broadcast
|
||||
th.Store.EXPECT().GetMembersForBoard(utils.Anything).Return([]*model.BoardMember{}, nil).Times(2)
|
||||
|
@ -178,13 +178,12 @@ func (a *App) moveBoardsToDefaultCategory(userID, teamID, sourceCategoryID strin
|
||||
return fmt.Errorf("moveBoardsToDefaultCategory: %w", errNoDefaultCategoryFound)
|
||||
}
|
||||
|
||||
boardCategoryMapping := map[string]string{}
|
||||
|
||||
for _, boardID := range sourceCategoryBoards.BoardIDs {
|
||||
boardCategoryMapping[boardID] = defaultCategoryID
|
||||
boardIDs := make([]string, len(sourceCategoryBoards.BoardMetadata))
|
||||
for i := range sourceCategoryBoards.BoardMetadata {
|
||||
boardIDs[i] = sourceCategoryBoards.BoardMetadata[i].BoardID
|
||||
}
|
||||
|
||||
if err := a.AddUpdateUserCategoryBoard(teamID, userID, boardCategoryMapping); err != nil {
|
||||
if err := a.AddUpdateUserCategoryBoard(teamID, userID, defaultCategoryID, boardIDs); err != nil {
|
||||
return fmt.Errorf("moveBoardsToDefaultCategory: %w", err)
|
||||
}
|
||||
|
||||
|
@ -79,8 +79,8 @@ func (a *App) createBoardsCategory(userID, teamID string, existingCategoryBoards
|
||||
}
|
||||
|
||||
createdCategoryBoards := &model.CategoryBoards{
|
||||
Category: *createdCategory,
|
||||
BoardIDs: []string{},
|
||||
Category: *createdCategory,
|
||||
BoardMetadata: []model.CategoryBoardMetadata{},
|
||||
}
|
||||
|
||||
// get user's current team's baords
|
||||
@ -89,6 +89,8 @@ func (a *App) createBoardsCategory(userID, teamID string, existingCategoryBoards
|
||||
return nil, fmt.Errorf("createBoardsCategory error fetching user's team's boards: %w", err)
|
||||
}
|
||||
|
||||
boardIDsToAdd := []string{}
|
||||
|
||||
for _, board := range userTeamBoards {
|
||||
boardMembership, ok := boardMemberByBoardID[board.ID]
|
||||
if !ok {
|
||||
@ -107,8 +109,8 @@ func (a *App) createBoardsCategory(userID, teamID string, existingCategoryBoards
|
||||
belongsToCategory := false
|
||||
|
||||
for _, categoryBoard := range existingCategoryBoards {
|
||||
for _, boardID := range categoryBoard.BoardIDs {
|
||||
if boardID == board.ID {
|
||||
for _, metadata := range categoryBoard.BoardMetadata {
|
||||
if metadata.BoardID == board.ID {
|
||||
belongsToCategory = true
|
||||
break
|
||||
}
|
||||
@ -122,29 +124,58 @@ func (a *App) createBoardsCategory(userID, teamID string, existingCategoryBoards
|
||||
}
|
||||
|
||||
if !belongsToCategory {
|
||||
if err := a.AddUpdateUserCategoryBoard(teamID, userID, map[string]string{board.ID: createdCategory.ID}); err != nil {
|
||||
return nil, fmt.Errorf("createBoardsCategory failed to add category-less board to the default category, defaultCategoryID: %s, error: %w", createdCategory.ID, err)
|
||||
boardIDsToAdd = append(boardIDsToAdd, board.ID)
|
||||
newBoardMetadata := model.CategoryBoardMetadata{
|
||||
BoardID: board.ID,
|
||||
Hidden: false,
|
||||
}
|
||||
createdCategoryBoards.BoardMetadata = append(createdCategoryBoards.BoardMetadata, newBoardMetadata)
|
||||
}
|
||||
}
|
||||
|
||||
createdCategoryBoards.BoardIDs = append(createdCategoryBoards.BoardIDs, board.ID)
|
||||
if len(boardIDsToAdd) > 0 {
|
||||
if err := a.AddUpdateUserCategoryBoard(teamID, userID, createdCategory.ID, boardIDsToAdd); err != nil {
|
||||
return nil, fmt.Errorf("createBoardsCategory failed to add category-less board to the default category, defaultCategoryID: %s, error: %w", createdCategory.ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
return createdCategoryBoards, nil
|
||||
}
|
||||
|
||||
func (a *App) AddUpdateUserCategoryBoard(teamID, userID string, boardCategoryMapping map[string]string) error {
|
||||
err := a.store.AddUpdateCategoryBoard(userID, boardCategoryMapping)
|
||||
func (a *App) AddUpdateUserCategoryBoard(teamID, userID, categoryID string, boardIDs []string) error {
|
||||
if len(boardIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := a.store.AddUpdateCategoryBoard(userID, categoryID, boardIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wsPayload := make([]*model.BoardCategoryWebsocketData, len(boardCategoryMapping))
|
||||
userCategoryBoards, err := a.GetUserCategoryBoards(userID, teamID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var updatedCategory *model.CategoryBoards
|
||||
for i := range userCategoryBoards {
|
||||
if userCategoryBoards[i].ID == categoryID {
|
||||
updatedCategory = &userCategoryBoards[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if updatedCategory == nil {
|
||||
return errCategoryNotFound
|
||||
}
|
||||
|
||||
wsPayload := make([]*model.BoardCategoryWebsocketData, len(updatedCategory.BoardMetadata))
|
||||
i := 0
|
||||
for boardID, categoryID := range boardCategoryMapping {
|
||||
for _, categoryBoardMetadata := range updatedCategory.BoardMetadata {
|
||||
wsPayload[i] = &model.BoardCategoryWebsocketData{
|
||||
BoardID: boardID,
|
||||
BoardID: categoryBoardMetadata.BoardID,
|
||||
CategoryID: categoryID,
|
||||
Hidden: categoryBoardMetadata.Hidden,
|
||||
}
|
||||
i++
|
||||
}
|
||||
@ -198,12 +229,12 @@ func (a *App) verifyNewCategoryBoardsMatchExisting(userID, teamID, categoryID st
|
||||
return fmt.Errorf("%w categoryID: %s", errCategoryNotFound, categoryID)
|
||||
}
|
||||
|
||||
if len(targetCategoryBoards.BoardIDs) != len(newBoardsOrder) {
|
||||
if len(targetCategoryBoards.BoardMetadata) != len(newBoardsOrder) {
|
||||
return fmt.Errorf(
|
||||
"%w length new category boards: %d, length existing category boards: %d, userID: %s, teamID: %s, categoryID: %s",
|
||||
errCategoryBoardsLengthMismatch,
|
||||
len(newBoardsOrder),
|
||||
len(targetCategoryBoards.BoardIDs),
|
||||
len(targetCategoryBoards.BoardMetadata),
|
||||
userID,
|
||||
teamID,
|
||||
categoryID,
|
||||
@ -211,8 +242,8 @@ func (a *App) verifyNewCategoryBoardsMatchExisting(userID, teamID, categoryID st
|
||||
}
|
||||
|
||||
existingBoardMap := map[string]bool{}
|
||||
for _, boardID := range targetCategoryBoards.BoardIDs {
|
||||
existingBoardMap[boardID] = true
|
||||
for _, metadata := range targetCategoryBoards.BoardMetadata {
|
||||
existingBoardMap[metadata.BoardID] = true
|
||||
}
|
||||
|
||||
for _, boardID := range newBoardsOrder {
|
||||
@ -230,3 +261,19 @@ func (a *App) verifyNewCategoryBoardsMatchExisting(userID, teamID, categoryID st
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) SetBoardVisibility(teamID, userID, categoryID, boardID string, visible bool) error {
|
||||
if err := a.store.SetBoardVisibility(userID, categoryID, boardID, visible); err != nil {
|
||||
return fmt.Errorf("SetBoardVisibility: failed to update board visibility: %w", err)
|
||||
}
|
||||
|
||||
a.wsAdapter.BroadcastCategoryBoardChange(teamID, userID, []*model.BoardCategoryWebsocketData{
|
||||
{
|
||||
BoardID: boardID,
|
||||
CategoryID: categoryID,
|
||||
Hidden: !visible,
|
||||
},
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -14,7 +14,16 @@ func TestGetUserCategoryBoards(t *testing.T) {
|
||||
defer tearDown()
|
||||
|
||||
t.Run("user had no default category and had boards", func(t *testing.T) {
|
||||
th.Store.EXPECT().GetUserCategoryBoards("user_id", "team_id").Return([]model.CategoryBoards{}, nil)
|
||||
th.Store.EXPECT().GetUserCategoryBoards("user_id", "team_id").Return([]model.CategoryBoards{}, nil).Times(1)
|
||||
th.Store.EXPECT().GetUserCategoryBoards("user_id", "team_id").Return([]model.CategoryBoards{
|
||||
{
|
||||
Category: model.Category{
|
||||
ID: "boards_category_id",
|
||||
Type: model.CategoryTypeSystem,
|
||||
Name: "Boards",
|
||||
},
|
||||
},
|
||||
}, nil).Times(1)
|
||||
th.Store.EXPECT().CreateCategory(utils.Anything).Return(nil)
|
||||
th.Store.EXPECT().GetCategory(utils.Anything).Return(&model.Category{
|
||||
ID: "boards_category_id",
|
||||
@ -49,18 +58,16 @@ func TestGetUserCategoryBoards(t *testing.T) {
|
||||
Synthetic: false,
|
||||
},
|
||||
}, nil)
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("user_id", map[string]string{"board_id_1": "boards_category_id"}).Return(nil)
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("user_id", map[string]string{"board_id_2": "boards_category_id"}).Return(nil)
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("user_id", map[string]string{"board_id_3": "boards_category_id"}).Return(nil)
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("user_id", "boards_category_id", []string{"board_id_1", "board_id_2", "board_id_3"}).Return(nil)
|
||||
|
||||
categoryBoards, err := th.App.GetUserCategoryBoards("user_id", "team_id")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(categoryBoards))
|
||||
assert.Equal(t, "Boards", categoryBoards[0].Name)
|
||||
assert.Equal(t, 3, len(categoryBoards[0].BoardIDs))
|
||||
assert.Contains(t, categoryBoards[0].BoardIDs, "board_id_1")
|
||||
assert.Contains(t, categoryBoards[0].BoardIDs, "board_id_2")
|
||||
assert.Contains(t, categoryBoards[0].BoardIDs, "board_id_3")
|
||||
assert.Equal(t, 3, len(categoryBoards[0].BoardMetadata))
|
||||
assert.Contains(t, categoryBoards[0].BoardMetadata, model.CategoryBoardMetadata{BoardID: "board_id_1", Hidden: false})
|
||||
assert.Contains(t, categoryBoards[0].BoardMetadata, model.CategoryBoardMetadata{BoardID: "board_id_2", Hidden: false})
|
||||
assert.Contains(t, categoryBoards[0].BoardMetadata, model.CategoryBoardMetadata{BoardID: "board_id_3", Hidden: false})
|
||||
})
|
||||
|
||||
t.Run("user had no default category BUT had no boards", func(t *testing.T) {
|
||||
@ -78,14 +85,17 @@ func TestGetUserCategoryBoards(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(categoryBoards))
|
||||
assert.Equal(t, "Boards", categoryBoards[0].Name)
|
||||
assert.Equal(t, 0, len(categoryBoards[0].BoardIDs))
|
||||
assert.Equal(t, 0, len(categoryBoards[0].BoardMetadata))
|
||||
})
|
||||
|
||||
t.Run("user already had a default Boards category with boards in it", func(t *testing.T) {
|
||||
th.Store.EXPECT().GetUserCategoryBoards("user_id", "team_id").Return([]model.CategoryBoards{
|
||||
{
|
||||
Category: model.Category{Name: "Boards"},
|
||||
BoardIDs: []string{"board_id_1", "board_id_2"},
|
||||
BoardMetadata: []model.CategoryBoardMetadata{
|
||||
{BoardID: "board_id_1", Hidden: false},
|
||||
{BoardID: "board_id_2", Hidden: false},
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
|
||||
@ -93,7 +103,7 @@ func TestGetUserCategoryBoards(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(categoryBoards))
|
||||
assert.Equal(t, "Boards", categoryBoards[0].Name)
|
||||
assert.Equal(t, 2, len(categoryBoards[0].BoardIDs))
|
||||
assert.Equal(t, 2, len(categoryBoards[0].BoardMetadata))
|
||||
})
|
||||
}
|
||||
|
||||
@ -116,7 +126,7 @@ func TestCreateBoardsCategory(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, boardsCategory)
|
||||
assert.Equal(t, "Boards", boardsCategory.Name)
|
||||
assert.Equal(t, 0, len(boardsCategory.BoardIDs))
|
||||
assert.Equal(t, 0, len(boardsCategory.BoardMetadata))
|
||||
})
|
||||
|
||||
t.Run("user has implicit access to some board", func(t *testing.T) {
|
||||
@ -150,7 +160,7 @@ func TestCreateBoardsCategory(t *testing.T) {
|
||||
|
||||
// there should still be no boards in the default category as
|
||||
// the user had only implicit access to boards
|
||||
assert.Equal(t, 0, len(boardsCategory.BoardIDs))
|
||||
assert.Equal(t, 0, len(boardsCategory.BoardMetadata))
|
||||
})
|
||||
|
||||
t.Run("user has explicit access to some board", func(t *testing.T) {
|
||||
@ -185,9 +195,17 @@ func TestCreateBoardsCategory(t *testing.T) {
|
||||
Synthetic: false,
|
||||
},
|
||||
}, nil)
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("user_id", map[string]string{"board_id_1": "boards_category_id"}).Return(nil)
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("user_id", map[string]string{"board_id_2": "boards_category_id"}).Return(nil)
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("user_id", map[string]string{"board_id_3": "boards_category_id"}).Return(nil)
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("user_id", "boards_category_id", []string{"board_id_1", "board_id_2", "board_id_3"}).Return(nil)
|
||||
|
||||
th.Store.EXPECT().GetUserCategoryBoards("user_id", "team_id").Return([]model.CategoryBoards{
|
||||
{
|
||||
Category: model.Category{
|
||||
Type: model.CategoryTypeSystem,
|
||||
ID: "boards_category_id",
|
||||
Name: "Boards",
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
|
||||
existingCategoryBoards := []model.CategoryBoards{}
|
||||
boardsCategory, err := th.App.createBoardsCategory("user_id", "team_id", existingCategoryBoards)
|
||||
@ -197,7 +215,7 @@ func TestCreateBoardsCategory(t *testing.T) {
|
||||
|
||||
// since user has explicit access to three boards,
|
||||
// they should all end up in the default category
|
||||
assert.Equal(t, 3, len(boardsCategory.BoardIDs))
|
||||
assert.Equal(t, 3, len(boardsCategory.BoardMetadata))
|
||||
})
|
||||
|
||||
t.Run("user has both implicit and explicit access to some board", func(t *testing.T) {
|
||||
@ -226,7 +244,17 @@ func TestCreateBoardsCategory(t *testing.T) {
|
||||
Synthetic: true,
|
||||
},
|
||||
}, nil)
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("user_id", map[string]string{"board_id_1": "boards_category_id"}).Return(nil)
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("user_id", "boards_category_id", []string{"board_id_1"}).Return(nil)
|
||||
|
||||
th.Store.EXPECT().GetUserCategoryBoards("user_id", "team_id").Return([]model.CategoryBoards{
|
||||
{
|
||||
Category: model.Category{
|
||||
Type: model.CategoryTypeSystem,
|
||||
ID: "boards_category_id",
|
||||
Name: "Boards",
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
|
||||
existingCategoryBoards := []model.CategoryBoards{}
|
||||
boardsCategory, err := th.App.createBoardsCategory("user_id", "team_id", existingCategoryBoards)
|
||||
@ -237,7 +265,7 @@ func TestCreateBoardsCategory(t *testing.T) {
|
||||
// there was only one explicit board access,
|
||||
// and so only that one should end up in the
|
||||
// default category
|
||||
assert.Equal(t, 1, len(boardsCategory.BoardIDs))
|
||||
assert.Equal(t, 1, len(boardsCategory.BoardMetadata))
|
||||
})
|
||||
}
|
||||
|
||||
@ -249,15 +277,20 @@ func TestReorderCategoryBoards(t *testing.T) {
|
||||
th.Store.EXPECT().GetUserCategoryBoards("user_id", "team_id").Return([]model.CategoryBoards{
|
||||
{
|
||||
Category: model.Category{ID: "category_id_1", Name: "Category 1"},
|
||||
BoardIDs: []string{"board_id_1", "board_id_2"},
|
||||
BoardMetadata: []model.CategoryBoardMetadata{
|
||||
{BoardID: "board_id_1", Hidden: false},
|
||||
{BoardID: "board_id_2", Hidden: false},
|
||||
},
|
||||
},
|
||||
{
|
||||
Category: model.Category{ID: "category_id_2", Name: "Boards", Type: "system"},
|
||||
BoardIDs: []string{"board_id_3"},
|
||||
BoardMetadata: []model.CategoryBoardMetadata{
|
||||
{BoardID: "board_id_3", Hidden: false},
|
||||
},
|
||||
},
|
||||
{
|
||||
Category: model.Category{ID: "category_id_3", Name: "Category 3"},
|
||||
BoardIDs: []string{},
|
||||
Category: model.Category{ID: "category_id_3", Name: "Category 3"},
|
||||
BoardMetadata: []model.CategoryBoardMetadata{},
|
||||
},
|
||||
}, nil)
|
||||
|
||||
@ -274,15 +307,21 @@ func TestReorderCategoryBoards(t *testing.T) {
|
||||
th.Store.EXPECT().GetUserCategoryBoards("user_id", "team_id").Return([]model.CategoryBoards{
|
||||
{
|
||||
Category: model.Category{ID: "category_id_1", Name: "Category 1"},
|
||||
BoardIDs: []string{"board_id_1", "board_id_2", "board_id_3"},
|
||||
BoardMetadata: []model.CategoryBoardMetadata{
|
||||
{BoardID: "board_id_1", Hidden: false},
|
||||
{BoardID: "board_id_2", Hidden: false},
|
||||
{BoardID: "board_id_3", Hidden: false},
|
||||
},
|
||||
},
|
||||
{
|
||||
Category: model.Category{ID: "category_id_2", Name: "Boards", Type: "system"},
|
||||
BoardIDs: []string{"board_id_3"},
|
||||
BoardMetadata: []model.CategoryBoardMetadata{
|
||||
{BoardID: "board_id_3", Hidden: false},
|
||||
},
|
||||
},
|
||||
{
|
||||
Category: model.Category{ID: "category_id_3", Name: "Category 3"},
|
||||
BoardIDs: []string{},
|
||||
Category: model.Category{ID: "category_id_3", Name: "Category 3"},
|
||||
BoardMetadata: []model.CategoryBoardMetadata{},
|
||||
},
|
||||
}, nil)
|
||||
|
||||
|
@ -278,7 +278,7 @@ func TestDeleteCategory(t *testing.T) {
|
||||
Type: "default",
|
||||
Name: "Boards",
|
||||
},
|
||||
BoardIDs: []string{},
|
||||
BoardMetadata: []model.CategoryBoardMetadata{},
|
||||
},
|
||||
{
|
||||
Category: model.Category{
|
||||
@ -289,12 +289,10 @@ func TestDeleteCategory(t *testing.T) {
|
||||
Type: "custom",
|
||||
Name: "Category 1",
|
||||
},
|
||||
BoardIDs: []string{},
|
||||
BoardMetadata: []model.CategoryBoardMetadata{},
|
||||
},
|
||||
}, nil)
|
||||
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("user_id_1", utils.Anything).Return(nil)
|
||||
|
||||
deletedCategory, err := th.App.DeleteCategory("category_id_1", "user_id_1", "team_id_1")
|
||||
assert.NotNil(t, deletedCategory)
|
||||
assert.NoError(t, err)
|
||||
@ -351,8 +349,6 @@ func TestMoveBoardsToDefaultCategory(t *testing.T) {
|
||||
},
|
||||
}, nil)
|
||||
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("user_id", utils.Anything).Return(nil)
|
||||
|
||||
err := th.App.moveBoardsToDefaultCategory("user_id", "team_id", "category_id_2")
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
@ -376,7 +372,6 @@ func TestMoveBoardsToDefaultCategory(t *testing.T) {
|
||||
}, nil)
|
||||
th.Store.EXPECT().GetMembersForUser("user_id").Return([]*model.BoardMember{}, nil)
|
||||
th.Store.EXPECT().GetBoardsForUserAndTeam("user_id", "team_id", false).Return([]*model.Board{}, nil)
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("user_id", utils.Anything).Return(nil)
|
||||
|
||||
err := th.App.moveBoardsToDefaultCategory("user_id", "team_id", "category_id_2")
|
||||
assert.NoError(t, err)
|
||||
|
15
server/app/compliance.go
Normal file
15
server/app/compliance.go
Normal file
@ -0,0 +1,15 @@
|
||||
package app
|
||||
|
||||
import "github.com/mattermost/focalboard/server/model"
|
||||
|
||||
func (a *App) GetBoardsForCompliance(opts model.QueryBoardsForComplianceOptions) ([]*model.Board, bool, error) {
|
||||
return a.store.GetBoardsForCompliance(opts)
|
||||
}
|
||||
|
||||
func (a *App) GetBoardsComplianceHistory(opts model.QueryBoardsComplianceHistoryOptions) ([]*model.BoardHistory, bool, error) {
|
||||
return a.store.GetBoardsComplianceHistory(opts)
|
||||
}
|
||||
|
||||
func (a *App) GetBlocksComplianceHistory(opts model.QueryBlocksComplianceHistoryOptions) ([]*model.BlockHistory, bool, error) {
|
||||
return a.store.GetBlocksComplianceHistory(opts)
|
||||
}
|
@ -92,14 +92,25 @@ func (a *App) writeArchiveBoard(zw *zip.Writer, board model.Board, opt model.Exp
|
||||
return err
|
||||
}
|
||||
if block.Type == model.TypeImage {
|
||||
filename, err := extractImageFilename(block)
|
||||
if err != nil {
|
||||
filename, err2 := extractImageFilename(block)
|
||||
if err2 != nil {
|
||||
return err
|
||||
}
|
||||
files = append(files, filename)
|
||||
}
|
||||
}
|
||||
|
||||
boardMembers, err := a.GetMembersForBoard(board.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, boardMember := range boardMembers {
|
||||
if err = a.writeArchiveBoardMemberLine(w, boardMember); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// write the files
|
||||
for _, filename := range files {
|
||||
if err := a.writeArchiveFile(zw, filename, board.ID, opt); err != nil {
|
||||
@ -109,6 +120,31 @@ func (a *App) writeArchiveBoard(zw *zip.Writer, board model.Board, opt model.Exp
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeArchiveBoardMemberLine writes a single boardMember to the archive.
|
||||
func (a *App) writeArchiveBoardMemberLine(w io.Writer, boardMember *model.BoardMember) error {
|
||||
bm, err := json.Marshal(&boardMember)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
line := model.ArchiveLine{
|
||||
Type: "boardMember",
|
||||
Data: bm,
|
||||
}
|
||||
|
||||
bm, err = json.Marshal(&line)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = w.Write(bm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = w.Write(newline)
|
||||
return err
|
||||
}
|
||||
|
||||
// writeArchiveBlockLine writes a single block to the archive.
|
||||
func (a *App) writeArchiveBlockLine(w io.Writer, block *model.Block) error {
|
||||
b, err := json.Marshal(&block)
|
||||
|
@ -137,6 +137,7 @@ func (a *App) ImportBoardJSONL(r io.Reader, opt model.ImportArchiveOptions) (str
|
||||
}
|
||||
now := utils.GetMillis()
|
||||
var boardID string
|
||||
var boardMembers []*model.BoardMember
|
||||
|
||||
lineNum := 1
|
||||
firstLine := true
|
||||
@ -196,6 +197,12 @@ func (a *App) ImportBoardJSONL(r io.Reader, opt model.ImportArchiveOptions) (str
|
||||
block.UpdateAt = now
|
||||
block.BoardID = boardID
|
||||
boardsAndBlocks.Blocks = append(boardsAndBlocks.Blocks, block)
|
||||
case "boardMember":
|
||||
var boardMember *model.BoardMember
|
||||
if err2 := json.Unmarshal(archiveLine.Data, &boardMember); err2 != nil {
|
||||
return "", fmt.Errorf("invalid board Member in archive line %d: %w", lineNum, err2)
|
||||
}
|
||||
boardMembers = append(boardMembers, boardMember)
|
||||
default:
|
||||
return "", model.NewErrUnsupportedArchiveLineType(lineNum, archiveLine.Type)
|
||||
}
|
||||
@ -212,6 +219,13 @@ func (a *App) ImportBoardJSONL(r io.Reader, opt model.ImportArchiveOptions) (str
|
||||
lineNum++
|
||||
}
|
||||
|
||||
// loop to remove the people how are not part of the team and system
|
||||
for i := len(boardMembers) - 1; i >= 0; i-- {
|
||||
if _, err := a.GetUser(boardMembers[i].UserID); err != nil {
|
||||
boardMembers = append(boardMembers[:i], boardMembers[i+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
a.fixBoardsandBlocks(boardsAndBlocks, opt)
|
||||
|
||||
var err error
|
||||
@ -225,16 +239,31 @@ func (a *App) ImportBoardJSONL(r io.Reader, opt model.ImportArchiveOptions) (str
|
||||
return "", fmt.Errorf("error inserting archive blocks: %w", err)
|
||||
}
|
||||
|
||||
// add user to all the new boards (if not the fake system user).
|
||||
if opt.ModifiedBy != model.SystemUserID {
|
||||
for _, board := range boardsAndBlocks.Boards {
|
||||
boardMember := &model.BoardMember{
|
||||
BoardID: board.ID,
|
||||
UserID: opt.ModifiedBy,
|
||||
SchemeAdmin: true,
|
||||
// add users to all the new boards (if not the fake system user).
|
||||
for _, board := range boardsAndBlocks.Boards {
|
||||
// make sure an admin user gets added
|
||||
adminMember := &model.BoardMember{
|
||||
BoardID: board.ID,
|
||||
UserID: opt.ModifiedBy,
|
||||
SchemeAdmin: true,
|
||||
}
|
||||
if _, err2 := a.AddMemberToBoard(adminMember); err2 != nil {
|
||||
return "", fmt.Errorf("cannot add adminMember to board: %w", err2)
|
||||
}
|
||||
for _, boardMember := range boardMembers {
|
||||
bm := &model.BoardMember{
|
||||
BoardID: board.ID,
|
||||
UserID: boardMember.UserID,
|
||||
Roles: boardMember.Roles,
|
||||
MinimumRole: boardMember.MinimumRole,
|
||||
SchemeAdmin: boardMember.SchemeAdmin,
|
||||
SchemeEditor: boardMember.SchemeEditor,
|
||||
SchemeCommenter: boardMember.SchemeCommenter,
|
||||
SchemeViewer: boardMember.SchemeViewer,
|
||||
Synthetic: boardMember.Synthetic,
|
||||
}
|
||||
if _, err := a.AddMemberToBoard(boardMember); err != nil {
|
||||
return "", fmt.Errorf("cannot add member to board: %w", err)
|
||||
if _, err2 := a.AddMemberToBoard(bm); err2 != nil {
|
||||
return "", fmt.Errorf("cannot add member to board: %w", err2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,15 @@ func TestApp_ImportArchive(t *testing.T) {
|
||||
th.Store.EXPECT().GetMembersForBoard(board.ID).AnyTimes().Return([]*model.BoardMember{boardMember}, nil)
|
||||
th.Store.EXPECT().GetBoard(board.ID).Return(board, nil)
|
||||
th.Store.EXPECT().GetMemberForBoard(board.ID, "user").Return(boardMember, nil)
|
||||
th.Store.EXPECT().GetUserCategoryBoards("user", "test-team").Return([]model.CategoryBoards{
|
||||
{
|
||||
Category: model.Category{
|
||||
Type: "default",
|
||||
Name: "Boards",
|
||||
ID: "boards_category_id",
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
th.Store.EXPECT().GetUserCategoryBoards("user", "test-team")
|
||||
th.Store.EXPECT().CreateCategory(utils.Anything).Return(nil)
|
||||
th.Store.EXPECT().GetCategory(utils.Anything).Return(&model.Category{
|
||||
@ -57,11 +66,78 @@ func TestApp_ImportArchive(t *testing.T) {
|
||||
}, nil)
|
||||
th.Store.EXPECT().GetBoardsForUserAndTeam("user", "test-team", false).Return([]*model.Board{}, nil)
|
||||
th.Store.EXPECT().GetMembersForUser("user").Return([]*model.BoardMember{}, nil)
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("user", utils.Anything).Return(nil)
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("user", utils.Anything, utils.Anything).Return(nil)
|
||||
|
||||
err := th.App.ImportArchive(r, opts)
|
||||
require.NoError(t, err, "import archive should not fail")
|
||||
})
|
||||
|
||||
t.Run("import board archive", func(t *testing.T) {
|
||||
r := bytes.NewReader([]byte(boardArchive))
|
||||
opts := model.ImportArchiveOptions{
|
||||
TeamID: "test-team",
|
||||
ModifiedBy: "f1tydgc697fcbp8ampr6881jea",
|
||||
}
|
||||
|
||||
bm1 := &model.BoardMember{
|
||||
BoardID: board.ID,
|
||||
UserID: "f1tydgc697fcbp8ampr6881jea",
|
||||
}
|
||||
|
||||
bm2 := &model.BoardMember{
|
||||
BoardID: board.ID,
|
||||
UserID: "hxxzooc3ff8cubsgtcmpn8733e",
|
||||
}
|
||||
|
||||
bm3 := &model.BoardMember{
|
||||
BoardID: board.ID,
|
||||
UserID: "nto73edn5ir6ifimo5a53y1dwa",
|
||||
}
|
||||
|
||||
user1 := &model.User{
|
||||
ID: "f1tydgc697fcbp8ampr6881jea",
|
||||
}
|
||||
|
||||
user2 := &model.User{
|
||||
ID: "hxxzooc3ff8cubsgtcmpn8733e",
|
||||
}
|
||||
|
||||
user3 := &model.User{
|
||||
ID: "nto73edn5ir6ifimo5a53y1dwa",
|
||||
}
|
||||
|
||||
th.Store.EXPECT().CreateBoardsAndBlocks(gomock.AssignableToTypeOf(&model.BoardsAndBlocks{}), "f1tydgc697fcbp8ampr6881jea").Return(babs, nil)
|
||||
th.Store.EXPECT().GetMembersForBoard(board.ID).AnyTimes().Return([]*model.BoardMember{bm1, bm2, bm3}, nil)
|
||||
th.Store.EXPECT().GetUserCategoryBoards("f1tydgc697fcbp8ampr6881jea", "test-team").Return([]model.CategoryBoards{}, nil)
|
||||
th.Store.EXPECT().GetUserCategoryBoards("f1tydgc697fcbp8ampr6881jea", "test-team").Return([]model.CategoryBoards{
|
||||
{
|
||||
Category: model.Category{
|
||||
ID: "boards_category_id",
|
||||
Name: "Boards",
|
||||
Type: model.CategoryTypeSystem,
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
th.Store.EXPECT().CreateCategory(utils.Anything).Return(nil)
|
||||
th.Store.EXPECT().GetCategory(utils.Anything).Return(&model.Category{
|
||||
ID: "boards_category_id",
|
||||
Name: "Boards",
|
||||
}, nil)
|
||||
th.Store.EXPECT().GetMembersForUser("f1tydgc697fcbp8ampr6881jea").Return([]*model.BoardMember{}, nil)
|
||||
th.Store.EXPECT().GetBoardsForUserAndTeam("f1tydgc697fcbp8ampr6881jea", "test-team", false).Return([]*model.Board{}, nil)
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("f1tydgc697fcbp8ampr6881jea", utils.Anything, utils.Anything).Return(nil)
|
||||
th.Store.EXPECT().GetBoard(board.ID).AnyTimes().Return(board, nil)
|
||||
th.Store.EXPECT().GetMemberForBoard(board.ID, "f1tydgc697fcbp8ampr6881jea").AnyTimes().Return(bm1, nil)
|
||||
th.Store.EXPECT().GetMemberForBoard(board.ID, "hxxzooc3ff8cubsgtcmpn8733e").AnyTimes().Return(bm2, nil)
|
||||
th.Store.EXPECT().GetMemberForBoard(board.ID, "nto73edn5ir6ifimo5a53y1dwa").AnyTimes().Return(bm3, nil)
|
||||
th.Store.EXPECT().GetUserByID("f1tydgc697fcbp8ampr6881jea").AnyTimes().Return(user1, nil)
|
||||
th.Store.EXPECT().GetUserByID("hxxzooc3ff8cubsgtcmpn8733e").AnyTimes().Return(user2, nil)
|
||||
th.Store.EXPECT().GetUserByID("nto73edn5ir6ifimo5a53y1dwa").AnyTimes().Return(user3, nil)
|
||||
|
||||
boardID, err := th.App.ImportBoardJSONL(r, opts)
|
||||
require.Equal(t, board.ID, boardID, "Board ID should be same")
|
||||
require.NoError(t, err, "import archive should not fail")
|
||||
})
|
||||
}
|
||||
|
||||
//nolint:lll
|
||||
@ -78,3 +154,12 @@ const asana = `{"version":1,"date":1614714686842}
|
||||
{"type":"block","data":{"id":"db1dd596-0999-4741-8b05-72ca8e438e31","fields":{"icon":"","properties":{"3bdcbaeb-bc78-4884-8531-a0323b74676a":"deaab476-c690-48df-828f-725b064dc476"},"contentOrder":[]},"createAt":1614714686841,"updateAt":1614714686841,"deleteAt":0,"schema":1,"parentId":"d14b9df9-1f31-4732-8a64-92bc7162cd28","rootId":"d14b9df9-1f31-4732-8a64-92bc7162cd28","modifiedBy":"","type":"card","title":"[EXAMPLE TASK] Approve campaign copy"}}
|
||||
{"type":"block","data":{"id":"16861c05-f31f-46af-8429-80a87b5aa93a","fields":{"icon":"","properties":{"3bdcbaeb-bc78-4884-8531-a0323b74676a":"2138305a-3157-461c-8bbe-f19ebb55846d"},"contentOrder":[]},"createAt":1614714686841,"updateAt":1614714686841,"deleteAt":0,"schema":1,"parentId":"d14b9df9-1f31-4732-8a64-92bc7162cd28","rootId":"d14b9df9-1f31-4732-8a64-92bc7162cd28","modifiedBy":"","type":"card","title":"[EXAMPLE TASK] Send out updated attendee list"}}
|
||||
`
|
||||
|
||||
//nolint:lll
|
||||
const boardArchive = `{"type":"board","data":{"id":"bfoi6yy6pa3yzika53spj7pq9ee","teamId":"wsmqbtwb5jb35jb3mtp85c8a9h","channelId":"","createdBy":"nto73edn5ir6ifimo5a53y1dwa","modifiedBy":"nto73edn5ir6ifimo5a53y1dwa","type":"P","minimumRole":"","title":"Custom","description":"","icon":"","showDescription":false,"isTemplate":false,"templateVersion":0,"properties":{},"cardProperties":[{"id":"aonihehbifijmx56aqzu3cc7w1r","name":"Status","options":[],"type":"select"},{"id":"aohjkzt769rxhtcz1o9xcoce5to","name":"Person","options":[],"type":"person"}],"createAt":1672750481591,"updateAt":1672750481591,"deleteAt":0}}
|
||||
{"type":"block","data":{"id":"ckpc3b1dp3pbw7bqntfryy9jbzo","parentId":"bjaqxtbyqz3bu7pgyddpgpms74a","createdBy":"nto73edn5ir6ifimo5a53y1dwa","modifiedBy":"nto73edn5ir6ifimo5a53y1dwa","schema":1,"type":"card","title":"Test","fields":{"contentOrder":[],"icon":"","isTemplate":false,"properties":{"aohjkzt769rxhtcz1o9xcoce5to":"hxxzooc3ff8cubsgtcmpn8733e"}},"createAt":1672750481612,"updateAt":1672845003530,"deleteAt":0,"boardId":"bfoi6yy6pa3yzika53spj7pq9ee"}}
|
||||
{"type":"block","data":{"id":"v7tdajwpm47r3u8duedk89bhxar","parentId":"bpypang3a3errqstj1agx9kuqay","createdBy":"nto73edn5ir6ifimo5a53y1dwa","modifiedBy":"nto73edn5ir6ifimo5a53y1dwa","schema":1,"type":"view","title":"Board view","fields":{"cardOrder":["crsyw7tbr3pnjznok6ppngmmyya","c5titiemp4pgaxbs4jksgybbj4y"],"collapsedOptionIds":[],"columnCalculations":{},"columnWidths":{},"defaultTemplateId":"","filter":{"filters":[],"operation":"and"},"hiddenOptionIds":[],"kanbanCalculations":{},"sortOptions":[],"viewType":"board","visibleOptionIds":[],"visiblePropertyIds":["aohjkzt769rxhtcz1o9xcoce5to"]},"createAt":1672750481626,"updateAt":1672750481626,"deleteAt":0,"boardId":"bfoi6yy6pa3yzika53spj7pq9ee"}}
|
||||
{"type":"boardMember","data":{"boardId":"bfoi6yy6pa3yzika53spj7pq9ee","userId":"f1tydgc697fcbp8ampr6881jea","roles":"","minimumRole":"","schemeAdmin":false,"schemeEditor":false,"schemeCommenter":false,"schemeViewer":true,"synthetic":false}}
|
||||
{"type":"boardMember","data":{"boardId":"bfoi6yy6pa3yzika53spj7pq9ee","userId":"hxxzooc3ff8cubsgtcmpn8733e","roles":"","minimumRole":"","schemeAdmin":false,"schemeEditor":false,"schemeCommenter":false,"schemeViewer":true,"synthetic":false}}
|
||||
{"type":"boardMember","data":{"boardId":"bfoi6yy6pa3yzika53spj7pq9ee","userId":"nto73edn5ir6ifimo5a53y1dwa","roles":"","minimumRole":"","schemeAdmin":true,"schemeEditor":false,"schemeCommenter":false,"schemeViewer":false,"synthetic":false}}
|
||||
`
|
||||
|
@ -19,7 +19,7 @@ func (a *App) GetTeamBoardsInsights(userID string, teamID string, opts *mmModel.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return a.store.GetTeamBoardsInsights(teamID, userID, opts.StartUnixMilli, opts.Page*opts.PerPage, opts.PerPage, boardIDs)
|
||||
return a.store.GetTeamBoardsInsights(teamID, opts.StartUnixMilli, opts.Page*opts.PerPage, opts.PerPage, boardIDs)
|
||||
}
|
||||
|
||||
func (a *App) GetUserBoardsInsights(userID string, teamID string, opts *mmModel.InsightsOpts) (*model.BoardInsightsList, error) {
|
||||
|
@ -51,7 +51,7 @@ func TestGetTeamAndUserBoardsInsights(t *testing.T) {
|
||||
th.Store.EXPECT().GetUserByID("user-id").Return(fakeUser, nil).AnyTimes()
|
||||
th.Store.EXPECT().GetBoardsForUserAndTeam("user-id", "team-id", true).Return(mockInsightsBoards, nil).AnyTimes()
|
||||
th.Store.EXPECT().
|
||||
GetTeamBoardsInsights("team-id", "user-id", int64(0), 0, 10, []string{"mock-user-workspace-id"}).
|
||||
GetTeamBoardsInsights("team-id", int64(0), 0, 10, []string{"mock-user-workspace-id"}).
|
||||
Return(mockTeamInsightsList, nil)
|
||||
results, err := th.App.GetTeamBoardsInsights("user-id", "team-id", &mmModel.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 10})
|
||||
require.NoError(t, err)
|
||||
@ -74,7 +74,7 @@ func TestGetTeamAndUserBoardsInsights(t *testing.T) {
|
||||
th.Store.EXPECT().GetUserByID("user-id").Return(fakeUser, nil).AnyTimes()
|
||||
th.Store.EXPECT().GetBoardsForUserAndTeam("user-id", "team-id", true).Return(mockInsightsBoards, nil).AnyTimes()
|
||||
th.Store.EXPECT().
|
||||
GetTeamBoardsInsights("team-id", "user-id", int64(0), 0, 10, []string{"mock-user-workspace-id"}).
|
||||
GetTeamBoardsInsights("team-id", int64(0), 0, 10, []string{"mock-user-workspace-id"}).
|
||||
Return(nil, insightError{"board-insight-error"})
|
||||
_, err := th.App.GetTeamBoardsInsights("user-id", "team-id", &mmModel.InsightsOpts{StartUnixMilli: 0, Page: 0, PerPage: 10})
|
||||
require.Error(t, err)
|
||||
|
@ -70,7 +70,7 @@ func TestPrepareOnboardingTour(t *testing.T) {
|
||||
{
|
||||
Category: model.Category{ID: "boards_category_id", Name: "Boards"},
|
||||
},
|
||||
}, nil).Times(1)
|
||||
}, nil).Times(2)
|
||||
|
||||
th.Store.EXPECT().CreateCategory(utils.Anything).Return(nil).Times(1)
|
||||
th.Store.EXPECT().GetCategory(utils.Anything).Return(&model.Category{
|
||||
@ -78,7 +78,7 @@ func TestPrepareOnboardingTour(t *testing.T) {
|
||||
Name: "Boards",
|
||||
}, nil)
|
||||
th.Store.EXPECT().GetBoardsForUserAndTeam("user_id_1", teamID, false).Return([]*model.Board{}, nil)
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("user_id_1", map[string]string{"board_id_2": "boards_category_id"}).Return(nil)
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("user_id_1", "boards_category_id", []string{"board_id_2"}).Return(nil)
|
||||
|
||||
teamID, boardID, err := th.App.PrepareOnboardingTour(userID, teamID)
|
||||
assert.NoError(t, err)
|
||||
@ -120,8 +120,8 @@ func TestCreateWelcomeBoard(t *testing.T) {
|
||||
{
|
||||
Category: model.Category{ID: "boards_category_id", Name: "Boards"},
|
||||
},
|
||||
}, nil).Times(2)
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("user_id_1", map[string]string{"board_id_1": "boards_category_id"}).Return(nil)
|
||||
}, nil).Times(3)
|
||||
th.Store.EXPECT().AddUpdateCategoryBoard("user_id_1", "boards_category_id", []string{"board_id_1"}).Return(nil)
|
||||
|
||||
boardID, err := th.App.createWelcomeBoard(userID, teamID)
|
||||
assert.Nil(t, err)
|
||||
|
@ -7,5 +7,6 @@ import (
|
||||
// DefaultTemplatesArchive is an embedded archive file containing the default
|
||||
// templates to be imported to team 0.
|
||||
// This archive is generated with `make templates-archive`
|
||||
//
|
||||
//go:embed templates.boardarchive
|
||||
var DefaultTemplatesArchive []byte
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/mattermost/focalboard/server/api"
|
||||
"github.com/mattermost/focalboard/server/model"
|
||||
|
||||
mmModel "github.com/mattermost/mattermost-server/v6/model"
|
||||
)
|
||||
|
||||
@ -986,3 +987,76 @@ func (c *Client) GetStatistics() (*model.BoardsStatistics, *Response) {
|
||||
|
||||
return stats, BuildResponse(r)
|
||||
}
|
||||
|
||||
func (c *Client) GetBoardsForCompliance(teamID string, page, perPage int) (*model.BoardsComplianceResponse, *Response) {
|
||||
query := fmt.Sprintf("?team_id=%s&page=%d&per_page=%d", teamID, page, perPage)
|
||||
r, err := c.DoAPIGet("/admin/boards"+query, "")
|
||||
if err != nil {
|
||||
return nil, BuildErrorResponse(r, err)
|
||||
}
|
||||
defer closeBody(r)
|
||||
|
||||
var res *model.BoardsComplianceResponse
|
||||
err = json.NewDecoder(r.Body).Decode(&res)
|
||||
if err != nil {
|
||||
return nil, BuildErrorResponse(r, err)
|
||||
}
|
||||
|
||||
return res, BuildResponse(r)
|
||||
}
|
||||
|
||||
func (c *Client) GetBoardsComplianceHistory(
|
||||
modifiedSince int64, includeDeleted bool, teamID string, page, perPage int) (*model.BoardsComplianceHistoryResponse, *Response) {
|
||||
query := fmt.Sprintf("?modified_since=%d&include_deleted=%t&team_id=%s&page=%d&per_page=%d",
|
||||
modifiedSince, includeDeleted, teamID, page, perPage)
|
||||
r, err := c.DoAPIGet("/admin/boards_history"+query, "")
|
||||
if err != nil {
|
||||
return nil, BuildErrorResponse(r, err)
|
||||
}
|
||||
defer closeBody(r)
|
||||
|
||||
var res *model.BoardsComplianceHistoryResponse
|
||||
err = json.NewDecoder(r.Body).Decode(&res)
|
||||
if err != nil {
|
||||
return nil, BuildErrorResponse(r, err)
|
||||
}
|
||||
|
||||
return res, BuildResponse(r)
|
||||
}
|
||||
|
||||
func (c *Client) GetBlocksComplianceHistory(
|
||||
modifiedSince int64, includeDeleted bool, teamID, boardID string, page, perPage int) (*model.BlocksComplianceHistoryResponse, *Response) {
|
||||
query := fmt.Sprintf("?modified_since=%d&include_deleted=%t&team_id=%s&board_id=%s&page=%d&per_page=%d",
|
||||
modifiedSince, includeDeleted, teamID, boardID, page, perPage)
|
||||
r, err := c.DoAPIGet("/admin/blocks_history"+query, "")
|
||||
if err != nil {
|
||||
return nil, BuildErrorResponse(r, err)
|
||||
}
|
||||
defer closeBody(r)
|
||||
|
||||
var res *model.BlocksComplianceHistoryResponse
|
||||
err = json.NewDecoder(r.Body).Decode(&res)
|
||||
if err != nil {
|
||||
return nil, BuildErrorResponse(r, err)
|
||||
}
|
||||
|
||||
return res, BuildResponse(r)
|
||||
}
|
||||
|
||||
func (c *Client) HideBoard(teamID, categoryID, boardID string) *Response {
|
||||
r, err := c.DoAPIPut(c.GetTeamRoute(teamID)+"/categories/"+categoryID+"/boards/"+boardID+"/hide", "")
|
||||
if err != nil {
|
||||
return BuildErrorResponse(r, err)
|
||||
}
|
||||
|
||||
return BuildResponse(r)
|
||||
}
|
||||
|
||||
func (c *Client) UnhideBoard(teamID, categoryID, boardID string) *Response {
|
||||
r, err := c.DoAPIPut(c.GetTeamRoute(teamID)+"/categories/"+categoryID+"/boards/"+boardID+"/unhide", "")
|
||||
if err != nil {
|
||||
return BuildErrorResponse(r, err)
|
||||
}
|
||||
|
||||
return BuildResponse(r)
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
module github.com/mattermost/focalboard/server
|
||||
|
||||
go 1.18
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/Masterminds/squirrel v1.5.3
|
||||
@ -9,8 +9,8 @@ require (
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/krolaw/zipstream v0.0.0-20180621105154-0a2661891f94
|
||||
github.com/lib/pq v1.10.7
|
||||
github.com/mattermost/mattermost-plugin-api v0.0.29-0.20220801143717-73008cfda2fb
|
||||
github.com/mattermost/mattermost-server/v6 v6.0.0-20221214122404-8d90c7042f93
|
||||
github.com/mattermost/mattermost-plugin-api v0.1.1
|
||||
github.com/mattermost/mattermost-server/v6 v6.0.0-20230116174708-240304ad0728
|
||||
github.com/mattermost/morph v1.0.5-0.20221115094356-4c18a75b1f5e
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible
|
||||
github.com/mgdelacroix/foundation v0.0.0-20220812143423-0bfc18f73538
|
||||
@ -23,14 +23,14 @@ require (
|
||||
github.com/spf13/viper v1.10.1
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/wiggin77/merror v1.0.4
|
||||
golang.org/x/crypto v0.2.0
|
||||
golang.org/x/crypto v0.5.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/blang/semver v3.5.1+incompatible // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||
github.com/dyatlov/go-opengraph/opengraph v0.0.0-20220524092352-606d7b1e5f8a // indirect
|
||||
@ -38,20 +38,20 @@ require (
|
||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
|
||||
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||
github.com/golang-migrate/migrate/v4 v4.15.2 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/graph-gophers/graphql-go v1.4.0 // indirect
|
||||
github.com/hashicorp/go-hclog v1.3.1 // indirect
|
||||
github.com/hashicorp/go-plugin v1.4.6 // indirect
|
||||
github.com/graph-gophers/graphql-go v1.5.1-0.20230110080634-edea822f558a // indirect
|
||||
github.com/hashicorp/go-hclog v1.4.0 // indirect
|
||||
github.com/hashicorp/go-plugin v1.4.8 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/jmoiron/sqlx v1.3.5 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/klauspost/compress v1.15.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.1 // indirect
|
||||
github.com/klauspost/compress v1.15.14 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.3 // indirect
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
@ -60,24 +60,23 @@ require (
|
||||
github.com/mattermost/logr/v2 v2.0.15 // indirect
|
||||
github.com/mattermost/squirrel v0.2.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/minio/minio-go/v7 v7.0.43 // indirect
|
||||
github.com/minio/minio-go/v7 v7.0.45 // indirect
|
||||
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pborman/uuid v1.2.1 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/philhofer/fwd v1.1.1 // indirect
|
||||
github.com/philhofer/fwd v1.1.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.33.0 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20220927061507-ef77025ab5aa // indirect
|
||||
github.com/rs/xid v1.4.0 // indirect
|
||||
github.com/segmentio/backo-go v1.0.1 // indirect
|
||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||
@ -87,36 +86,36 @@ require (
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
github.com/subosito/gotenv v1.2.0 // indirect
|
||||
github.com/tidwall/gjson v1.14.3 // indirect
|
||||
github.com/tidwall/gjson v1.14.4 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tinylib/msgp v1.1.6 // indirect
|
||||
github.com/tinylib/msgp v1.1.8 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
github.com/wiggin77/srslog v1.0.1 // indirect
|
||||
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
|
||||
github.com/yuin/goldmark v1.5.3 // indirect
|
||||
golang.org/x/mod v0.7.0 // indirect
|
||||
golang.org/x/net v0.2.0 // indirect
|
||||
golang.org/x/net v0.5.0 // indirect
|
||||
golang.org/x/sync v0.1.0 // indirect
|
||||
golang.org/x/sys v0.2.0 // indirect
|
||||
golang.org/x/text v0.4.0 // indirect
|
||||
golang.org/x/tools v0.3.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1 // indirect
|
||||
google.golang.org/grpc v1.50.1 // indirect
|
||||
golang.org/x/sys v0.4.0 // indirect
|
||||
golang.org/x/text v0.6.0 // indirect
|
||||
golang.org/x/tools v0.5.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230104163317-caabf589fcbf // indirect
|
||||
google.golang.org/grpc v1.51.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/uint128 v1.2.0 // indirect
|
||||
modernc.org/cc/v3 v3.36.0 // indirect
|
||||
modernc.org/ccgo/v3 v3.16.6 // indirect
|
||||
modernc.org/libc v1.16.7 // indirect
|
||||
modernc.org/mathutil v1.4.1 // indirect
|
||||
modernc.org/memory v1.1.1 // indirect
|
||||
modernc.org/opt v0.1.1 // indirect
|
||||
modernc.org/sqlite v1.18.0 // indirect
|
||||
modernc.org/strutil v1.1.1 // indirect
|
||||
modernc.org/token v1.0.0 // indirect
|
||||
modernc.org/cc/v3 v3.40.0 // indirect
|
||||
modernc.org/ccgo/v3 v3.16.13 // indirect
|
||||
modernc.org/libc v1.22.2 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.5.0 // indirect
|
||||
modernc.org/opt v0.1.3 // indirect
|
||||
modernc.org/sqlite v1.20.1 // indirect
|
||||
modernc.org/strutil v1.1.3 // indirect
|
||||
modernc.org/token v1.1.0 // indirect
|
||||
)
|
||||
|
261
server/go.sum
261
server/go.sum
@ -85,8 +85,6 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/ClickHouse/clickhouse-go v1.4.3/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
|
||||
github.com/Masterminds/squirrel v1.5.2 h1:UiOEi2ZX4RCSkpiNDQN5kro/XIBpSRk9iTqdIRPzUXE=
|
||||
github.com/Masterminds/squirrel v1.5.2/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
|
||||
github.com/Masterminds/squirrel v1.5.3 h1:YPpoceAcxuzIljlr5iWpNKaql7hLeG1KLSrhvdHpkZc=
|
||||
github.com/Masterminds/squirrel v1.5.3/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
|
||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||
@ -203,6 +201,7 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
|
||||
github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M=
|
||||
github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
|
||||
@ -406,7 +405,6 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.m
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ=
|
||||
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws=
|
||||
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
@ -427,8 +425,7 @@ github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiD
|
||||
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw=
|
||||
github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA=
|
||||
@ -487,6 +484,7 @@ github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
|
||||
@ -655,6 +653,7 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/graph-gophers/graphql-go v1.4.0 h1:JE9wveRTSXwJyjdRd6bOQ7Ob5bewTUQ58Jv4OiVdpdE=
|
||||
github.com/graph-gophers/graphql-go v1.4.0/go.mod h1:YtmJZDLbF1YYNrlNAuiO5zAStUWc3XZT07iGsVqe1Os=
|
||||
github.com/graph-gophers/graphql-go v1.5.1-0.20230110080634-edea822f558a/go.mod h1:YtmJZDLbF1YYNrlNAuiO5zAStUWc3XZT07iGsVqe1Os=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
@ -672,18 +671,18 @@ github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-hclog v1.2.1 h1:YQsLlGDJgwhXFpucSPyVbCBviQtjlHv3jLTlp8YmtEw=
|
||||
github.com/hashicorp/go-hclog v1.2.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-hclog v1.3.1 h1:vDwF1DFNZhntP4DAjuTpOw3uEgMUpXh1pB5fW9DqHpo=
|
||||
github.com/hashicorp/go-hclog v1.3.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-plugin v1.4.4 h1:NVdrSdFRt3SkZtNckJ6tog7gbpRrcbOjQi/rgF7JYWQ=
|
||||
github.com/hashicorp/go-plugin v1.4.4/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s=
|
||||
github.com/hashicorp/go-plugin v1.4.6 h1:MDV3UrKQBM3du3G7MApDGvOsMYy3JQJ4exhSoKBAeVA=
|
||||
github.com/hashicorp/go-plugin v1.4.6/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s=
|
||||
github.com/hashicorp/go-plugin v1.4.8/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
@ -698,8 +697,7 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 h1:xixZ2bWeofWV68J+x6AzmKuVM/JWCQwkWm6GW/MUR6I=
|
||||
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||
@ -806,14 +804,14 @@ github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdY
|
||||
github.com/klauspost/compress v1.13.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
||||
github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/compress v1.15.6 h1:6D9PcO8QWu0JyaQ2zUMmu16T1T+zjjEpP91guRsvDfY=
|
||||
github.com/klauspost/compress v1.15.6/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
||||
github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM=
|
||||
github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
|
||||
github.com/klauspost/compress v1.15.14/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
|
||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.0.13 h1:1XxvOiqXZ8SULZUKim/wncr3wZ38H4yCuVDvKdK9OGs=
|
||||
github.com/klauspost/cpuid/v2 v2.0.13/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||
github.com/klauspost/cpuid/v2 v2.2.1 h1:U33DW0aiEj633gHYw3LoDNfkDiYnE5Q8M/TKJn2f2jI=
|
||||
github.com/klauspost/cpuid/v2 v2.2.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
@ -843,8 +841,7 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs=
|
||||
github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
|
||||
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo=
|
||||
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||
@ -871,12 +868,15 @@ github.com/mattermost/logr/v2 v2.0.15 h1:+WNbGcsc3dBao65eXlceB6dTILNJRIrvubnsTl3
|
||||
github.com/mattermost/logr/v2 v2.0.15/go.mod h1:mpPp935r5dIkFDo2y9Q87cQWhFR/4xXpNh0k/y8Hmwg=
|
||||
github.com/mattermost/mattermost-plugin-api v0.0.29-0.20220801143717-73008cfda2fb h1:q1qXKVv59rA2gcQ7lVLc5OlWBmfsR3i8mdGD5EZesyk=
|
||||
github.com/mattermost/mattermost-plugin-api v0.0.29-0.20220801143717-73008cfda2fb/go.mod h1:PIeo40t9VTA4Wu1FwjzH7QmcgC3SRyk/ohCwJw4/oSo=
|
||||
github.com/mattermost/mattermost-plugin-api v0.1.1/go.mod h1:9yZhtg0bBj3kqSTjXnjYBMZoTsWbe3ajdFMdl9/Jz34=
|
||||
github.com/mattermost/mattermost-server/v6 v6.0.0-20220802151854-f07c31c5d933 h1:h7EibO8cwWeK8dLhC/A5tKGbkYSuJKZ0+2EXW7jDHoA=
|
||||
github.com/mattermost/mattermost-server/v6 v6.0.0-20220802151854-f07c31c5d933/go.mod h1:otnBnKY9Y0eNkUKeD161de+BUBlESwANTnrkPT/392Y=
|
||||
github.com/mattermost/mattermost-server/v6 v6.0.0-20221130200243-06e964b86b0d h1:CKJXDUCkRrfy1U9sZHOpvACOtkthV5iWt2boHUK720I=
|
||||
github.com/mattermost/mattermost-server/v6 v6.0.0-20221130200243-06e964b86b0d/go.mod h1:U3gSM0I15WSMHPpDEU30mmc4JrbSDk+8F1+MFLOHWD0=
|
||||
github.com/mattermost/mattermost-server/v6 v6.0.0-20221214122404-8d90c7042f93 h1:mGN2D6KhjKosQdZ+BHzmWxsA/tRK9FiR+nUd38nSZQY=
|
||||
github.com/mattermost/mattermost-server/v6 v6.0.0-20221214122404-8d90c7042f93/go.mod h1:U3gSM0I15WSMHPpDEU30mmc4JrbSDk+8F1+MFLOHWD0=
|
||||
github.com/mattermost/mattermost-server/v6 v6.0.0-20230116174708-240304ad0728 h1:fegj7GaXjiVH+/j1DsPtkobczafvUJynfFSwNeqIA84=
|
||||
github.com/mattermost/mattermost-server/v6 v6.0.0-20230116174708-240304ad0728/go.mod h1:FPN2+SAU9ndEpMFcjClvdillSpvS2eQ+i1qiSgAUxPI=
|
||||
github.com/mattermost/morph v0.0.0-20220401091636-39f834798da8 h1:gwliVjCTqAC01mSCNqa5nJ/4MmGq50vrjsottIhQ4d8=
|
||||
github.com/mattermost/morph v0.0.0-20220401091636-39f834798da8/go.mod h1:jxM3g1bx+k2Thz7jofcHguBS8TZn5Pc+o5MGmORObhw=
|
||||
github.com/mattermost/morph v1.0.5-0.20221115094356-4c18a75b1f5e h1:VfNz+fvJ3DxOlALM22Eea8ONp5jHrybKBCcCtDPVlss=
|
||||
@ -889,8 +889,8 @@ github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
@ -900,16 +900,16 @@ github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
|
||||
github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
|
||||
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
|
||||
@ -925,15 +925,14 @@ github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3N
|
||||
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||
github.com/minio/minio-go/v7 v7.0.28 h1:VMr3K5qGIEt+/KW3poopRh8mzi5RwuCjmrmstK196Fg=
|
||||
github.com/minio/minio-go/v7 v7.0.28/go.mod h1:x81+AX5gHSfCSqw7jxRKHvxUXMlE5uKX0Vb75Xk5yYg=
|
||||
github.com/minio/minio-go/v7 v7.0.43 h1:14Q4lwblqTdlAmba05oq5xL0VBLHi06zS4yLnIkz6hI=
|
||||
github.com/minio/minio-go/v7 v7.0.43/go.mod h1:nCrRzjoSUQh8hgKKtu3Y708OLvRLtuASMg2/nvmbarw=
|
||||
github.com/minio/minio-go/v7 v7.0.45/go.mod h1:nCrRzjoSUQh8hgKKtu3Y708OLvRLtuASMg2/nvmbarw=
|
||||
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
|
||||
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
|
||||
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
|
||||
@ -1048,6 +1047,7 @@ github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ=
|
||||
github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
||||
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
|
||||
github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY=
|
||||
github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
@ -1111,6 +1111,7 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20220927061507-ef77025ab5aa/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
@ -1125,8 +1126,7 @@ github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY=
|
||||
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||
github.com/rudderlabs/analytics-go v3.3.2+incompatible h1:bDajEJTYhfHjNYxbQFMA/2dHlOjyeSgxS7GPIdMZ52Q=
|
||||
github.com/rudderlabs/analytics-go v3.3.2+incompatible/go.mod h1:LF8/ty9kUX4PTY3l5c97K3nZZaX5Hwsvt+NBaRL/f30=
|
||||
github.com/rudderlabs/analytics-go v3.3.3+incompatible h1:OG0XlKoXfr539e2t1dXtTB+Gr89uFW+OUNQBVhHIIBY=
|
||||
github.com/rudderlabs/analytics-go v3.3.3+incompatible/go.mod h1:LF8/ty9kUX4PTY3l5c97K3nZZaX5Hwsvt+NBaRL/f30=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
@ -1140,8 +1140,7 @@ github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
||||
github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
|
||||
github.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3 h1:ZuhckGJ10ulaKkdvJtiAqsLTiPrLaXSdnVgXJKJkTxE=
|
||||
github.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3/go.mod h1:9/Rh6yILuLysoQnZ2oNooD2g7aBnvM7r/fNVxRNWfBc=
|
||||
github.com/segmentio/backo-go v1.0.1 h1:68RQccglxZeyURy93ASB/2kc9QudzgIDexJ927N++y4=
|
||||
github.com/segmentio/backo-go v1.0.1/go.mod h1:9/Rh6yILuLysoQnZ2oNooD2g7aBnvM7r/fNVxRNWfBc=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
|
||||
@ -1180,8 +1179,8 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
@ -1224,8 +1223,8 @@ github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
@ -1236,8 +1235,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
@ -1246,17 +1245,18 @@ github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
|
||||
github.com/tidwall/gjson v1.14.1 h1:iymTbGkQBhveq21bEvAQ81I0LEBork8BFe1CUZXdyuo=
|
||||
github.com/tidwall/gjson v1.14.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw=
|
||||
github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tinylib/msgp v1.1.6 h1:i+SbKraHhnrf9M5MYmvQhFnbLhAXSDWF8WWsuyRdocw=
|
||||
github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw=
|
||||
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
@ -1281,8 +1281,7 @@ github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||
github.com/wiggin77/merror v1.0.2/go.mod h1:uQTcIU0Z6jRK4OwqganPYerzQxSFJ4GSHM3aurxxQpg=
|
||||
github.com/wiggin77/merror v1.0.3 h1:8+ZHV+aSnJoYghE3EUThl15C6rvF2TYRSvOSBjdmNR8=
|
||||
github.com/wiggin77/merror v1.0.3/go.mod h1:H2ETSu7/bPE0Ymf4bEwdUoo73OOEkdClnoRisfw0Nm0=
|
||||
github.com/wiggin77/merror v1.0.4 h1:XxFLEevmQQfgJW2AxhapuMG7C1fQqfbim/XyUmYv/ZM=
|
||||
github.com/wiggin77/merror v1.0.4/go.mod h1:H2ETSu7/bPE0Ymf4bEwdUoo73OOEkdClnoRisfw0Nm0=
|
||||
github.com/wiggin77/srslog v1.0.1 h1:gA2XjSMy3DrRdX9UqLuDtuVAAshb8bE1NhX1YK0Qe+8=
|
||||
github.com/wiggin77/srslog v1.0.1/go.mod h1:fehkyYDq1QfuYn60TDPu9YdY2bB85VUW2mvN1WynEls=
|
||||
@ -1307,6 +1306,7 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.12 h1:6hffw6vALvEDqJ19dOJvJKOoAOKe4NDaTqvd2sktGN0=
|
||||
github.com/yuin/goldmark v1.4.12/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.5.3/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
|
||||
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
|
||||
@ -1362,7 +1362,7 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
@ -1402,9 +1402,9 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.2.0 h1:BRXPfhNivWL5Yq0BGQ39a2sW6t44aODpfxkWjYdzewE=
|
||||
golang.org/x/crypto v0.2.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@ -1453,8 +1453,7 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
|
||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@ -1529,7 +1528,10 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su
|
||||
golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220614195744-fb05da6f9022 h1:0qjDla5xICC2suMtyRH/QqX3B1btXTfNsIt/i4LFgO0=
|
||||
golang.org/x/net v0.0.0-20220614195744-fb05da6f9022/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||
golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@ -1566,6 +1568,7 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8=
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -1680,7 +1683,6 @@ golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20210818153620-00dd8d7831e7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@ -1694,20 +1696,25 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220317061510-51cd9980dadf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220614162138-6c1b26c55098 h1:PgOr27OhUx2IRqGJ2RxAWI4dJQ7bi9cSrB82uzFzfUA=
|
||||
golang.org/x/sys v0.0.0-20220614162138-6c1b26c55098/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@ -1716,9 +1723,11 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@ -1812,14 +1821,17 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY=
|
||||
golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
|
||||
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
|
||||
golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k=
|
||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f h1:uF6paiQQebLeSXkrTqHqz0MXhXXS1KgF41eUdBNvxK0=
|
||||
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
|
||||
gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
|
||||
gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0=
|
||||
@ -1948,9 +1960,9 @@ google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ6
|
||||
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20220111164026-67b88f271998/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
|
||||
google.golang.org/genproto v0.0.0-20220614165028-45ed7f3ff16e h1:ubR4JUtqN3ffdFjpKylv8scWk/mZstGmzXbgYSkuMl0=
|
||||
google.golang.org/genproto v0.0.0-20220614165028-45ed7f3ff16e/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||
google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1 h1:jCw9YRd2s40X9Vxi4zKsPRvSPlHWNqadVkpbMsCPzPQ=
|
||||
google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=
|
||||
google.golang.org/genproto v0.0.0-20230104163317-caabf589fcbf/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
|
||||
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
@ -1987,9 +1999,9 @@ google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K
|
||||
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||
google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
|
||||
google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8=
|
||||
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY=
|
||||
google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
|
||||
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
@ -2004,8 +2016,7 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
@ -2023,8 +2034,7 @@ gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKW
|
||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.66.6 h1:LATuAqN/shcYAOkv3wl2L4rkaKqkcgTBQjOyYDvcPKI=
|
||||
gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
@ -2113,80 +2123,18 @@ lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
|
||||
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
modernc.org/b v1.0.0/go.mod h1:uZWcZfRj1BpYzfN9JTerzlNUnnPsV9O2ZA8JsRcubNg=
|
||||
modernc.org/cc/v3 v3.32.4/go.mod h1:0R6jl1aZlIl2avnYfbfHBS1QB6/f+16mihBObaBC878=
|
||||
modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.8/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.20/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.22/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.24 h1:vlCqjhVwX15t1uwlMPpOpNRC7JTjMZ9lT9DYHKQTFuA=
|
||||
modernc.org/cc/v3 v3.35.24/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
|
||||
modernc.org/cc/v3 v3.36.0 h1:0kmRkTmqNidmu3c7BNDSdVHCxXCkWLmWmCIVX4LUboo=
|
||||
modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
|
||||
modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
|
||||
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
|
||||
modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc=
|
||||
modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw=
|
||||
modernc.org/ccgo/v3 v3.9.2/go.mod h1:gnJpy6NIVqkETT+L5zPsQFj7L2kkhfPMzOghRNv/CFo=
|
||||
modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60=
|
||||
modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw=
|
||||
modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI=
|
||||
modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag=
|
||||
modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw=
|
||||
modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ=
|
||||
modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c=
|
||||
modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo=
|
||||
modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg=
|
||||
modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I=
|
||||
modernc.org/ccgo/v3 v3.12.18/go.mod h1:jvg/xVdWWmZACSgOiAhpWpwHWylbJaSzayCqNOJKIhs=
|
||||
modernc.org/ccgo/v3 v3.12.20/go.mod h1:aKEdssiu7gVgSy/jjMastnv/q6wWGRbszbheXgWRHc8=
|
||||
modernc.org/ccgo/v3 v3.12.21/go.mod h1:ydgg2tEprnyMn159ZO/N4pLBqpL7NOkJ88GT5zNU2dE=
|
||||
modernc.org/ccgo/v3 v3.12.22/go.mod h1:nyDVFMmMWhMsgQw+5JH6B6o4MnZ+UQNw1pp52XYFPRk=
|
||||
modernc.org/ccgo/v3 v3.12.25/go.mod h1:UaLyWI26TwyIT4+ZFNjkyTbsPsY3plAEB6E7L/vZV3w=
|
||||
modernc.org/ccgo/v3 v3.12.29/go.mod h1:FXVjG7YLf9FetsS2OOYcwNhcdOLGt8S9bQ48+OP75cE=
|
||||
modernc.org/ccgo/v3 v3.12.36/go.mod h1:uP3/Fiezp/Ga8onfvMLpREq+KUjUmYMxXPO8tETHtA8=
|
||||
modernc.org/ccgo/v3 v3.12.38/go.mod h1:93O0G7baRST1vNj4wnZ49b1kLxt0xCW5Hsa2qRaZPqc=
|
||||
modernc.org/ccgo/v3 v3.12.43/go.mod h1:k+DqGXd3o7W+inNujK15S5ZYuPoWYLpF5PYougCmthU=
|
||||
modernc.org/ccgo/v3 v3.12.46/go.mod h1:UZe6EvMSqOxaJ4sznY7b23/k13R8XNlyWsO5bAmSgOE=
|
||||
modernc.org/ccgo/v3 v3.12.47/go.mod h1:m8d6p0zNps187fhBwzY/ii6gxfjob1VxWb919Nk1HUk=
|
||||
modernc.org/ccgo/v3 v3.12.50/go.mod h1:bu9YIwtg+HXQxBhsRDE+cJjQRuINuT9PUK4orOco/JI=
|
||||
modernc.org/ccgo/v3 v3.12.51/go.mod h1:gaIIlx4YpmGO2bLye04/yeblmvWEmE4BBBls4aJXFiE=
|
||||
modernc.org/ccgo/v3 v3.12.53/go.mod h1:8xWGGTFkdFEWBEsUmi+DBjwu/WLy3SSOrqEmKUjMeEg=
|
||||
modernc.org/ccgo/v3 v3.12.54/go.mod h1:yANKFTm9llTFVX1FqNKHE0aMcQb1fuPJx6p8AcUx+74=
|
||||
modernc.org/ccgo/v3 v3.12.55/go.mod h1:rsXiIyJi9psOwiBkplOaHye5L4MOOaCjHg1Fxkj7IeU=
|
||||
modernc.org/ccgo/v3 v3.12.56/go.mod h1:ljeFks3faDseCkr60JMpeDb2GSO3TKAmrzm7q9YOcMU=
|
||||
modernc.org/ccgo/v3 v3.12.57/go.mod h1:hNSF4DNVgBl8wYHpMvPqQWDQx8luqxDnNGCMM4NFNMc=
|
||||
modernc.org/ccgo/v3 v3.12.60/go.mod h1:k/Nn0zdO1xHVWjPYVshDeWKqbRWIfif5dtsIOCUVMqM=
|
||||
modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/EhQ=
|
||||
modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84=
|
||||
modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ=
|
||||
modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY=
|
||||
modernc.org/ccgo/v3 v3.12.84/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w=
|
||||
modernc.org/ccgo/v3 v3.12.86/go.mod h1:dN7S26DLTgVSni1PVA3KxxHTcykyDurf3OgUzNqTSrU=
|
||||
modernc.org/ccgo/v3 v3.12.88/go.mod h1:0MFzUHIuSIthpVZyMWiFYMwjiFnhrN5MkvBrUwON+ZM=
|
||||
modernc.org/ccgo/v3 v3.12.90/go.mod h1:obhSc3CdivCRpYZmrvO88TXlW0NvoSVvdh/ccRjJYko=
|
||||
modernc.org/ccgo/v3 v3.12.92/go.mod h1:5yDdN7ti9KWPi5bRVWPl8UNhpEAtCjuEE7ayQnzzqHA=
|
||||
modernc.org/ccgo/v3 v3.12.95/go.mod h1:ZcLyvtocXYi8uF+9Ebm3G8EF8HNY5hGomBqthDp4eC8=
|
||||
modernc.org/ccgo/v3 v3.13.1/go.mod h1:aBYVOUfIlcSnrsRVU8VRS35y2DIfpgkmVkYZ0tpIXi4=
|
||||
modernc.org/ccgo/v3 v3.15.9/go.mod h1:md59wBwDT2LznX/OTCPoVS6KIsdRgY8xqQwBV+hkTH0=
|
||||
modernc.org/ccgo/v3 v3.15.10/go.mod h1:wQKxoFn0ynxMuCLfFD09c8XPUCc8obfchoVR9Cn0fI8=
|
||||
modernc.org/ccgo/v3 v3.15.12/go.mod h1:VFePOWoCd8uDGRJpq/zfJ29D0EVzMSyID8LCMWYbX6I=
|
||||
modernc.org/ccgo/v3 v3.15.14/go.mod h1:144Sz2iBCKogb9OKwsu7hQEub3EVgOlyI8wMUPGKUXQ=
|
||||
modernc.org/ccgo/v3 v3.15.15/go.mod h1:z5qltXjU4PJl0pE5nhYQCvA9DhPHiWsl5GWl89+NSYE=
|
||||
modernc.org/ccgo/v3 v3.15.16/go.mod h1:XbKRMeMWMdq712Tr5ECgATYMrzJ+g9zAZEj2ktzBe24=
|
||||
modernc.org/ccgo/v3 v3.15.17 h1:svaDk4rfh7XQPBwkqzjKK8bta/vK4VVL3JP6ZLbcr0w=
|
||||
modernc.org/ccgo/v3 v3.15.17/go.mod h1:bofnFkpRFf5gLY+mBZIyTW6FEcp26xi2lgOFk2Rlvs0=
|
||||
modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
|
||||
modernc.org/ccgo/v3 v3.16.6 h1:3l18poV+iUemQ98O3X5OMr97LOqlzis+ytivU4NqGhA=
|
||||
modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
|
||||
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
|
||||
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
|
||||
modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
|
||||
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
|
||||
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
|
||||
@ -2200,100 +2148,47 @@ modernc.org/internal v1.0.0/go.mod h1:VUD/+JAkhCpvkUitlEOnhpVxCgsBI90oTzSCRcqQVS
|
||||
modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA=
|
||||
modernc.org/libc v1.7.13-0.20210308123627-12f642a52bb8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
|
||||
modernc.org/libc v1.9.5/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
|
||||
modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
|
||||
modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q=
|
||||
modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg=
|
||||
modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M=
|
||||
modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU=
|
||||
modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE=
|
||||
modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso=
|
||||
modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8=
|
||||
modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8=
|
||||
modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I=
|
||||
modernc.org/libc v1.11.24/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk=
|
||||
modernc.org/libc v1.11.26/go.mod h1:SFjnYi9OSd2W7f4ct622o/PAYqk7KHv6GS8NZULIjKY=
|
||||
modernc.org/libc v1.11.27/go.mod h1:zmWm6kcFXt/jpzeCgfvUNswM0qke8qVwxqZrnddlDiE=
|
||||
modernc.org/libc v1.11.28/go.mod h1:Ii4V0fTFcbq3qrv3CNn+OGHAvzqMBvC7dBNyC4vHZlg=
|
||||
modernc.org/libc v1.11.31/go.mod h1:FpBncUkEAtopRNJj8aRo29qUiyx5AvAlAxzlx9GNaVM=
|
||||
modernc.org/libc v1.11.34/go.mod h1:+Tzc4hnb1iaX/SKAutJmfzES6awxfU1BPvrrJO0pYLg=
|
||||
modernc.org/libc v1.11.37/go.mod h1:dCQebOwoO1046yTrfUE5nX1f3YpGZQKNcITUYWlrAWo=
|
||||
modernc.org/libc v1.11.39/go.mod h1:mV8lJMo2S5A31uD0k1cMu7vrJbSA3J3waQJxpV4iqx8=
|
||||
modernc.org/libc v1.11.42/go.mod h1:yzrLDU+sSjLE+D4bIhS7q1L5UwXDOw99PLSX0BlZvSQ=
|
||||
modernc.org/libc v1.11.44/go.mod h1:KFq33jsma7F5WXiYelU8quMJasCCTnHK0mkri4yPHgA=
|
||||
modernc.org/libc v1.11.45/go.mod h1:Y192orvfVQQYFzCNsn+Xt0Hxt4DiO4USpLNXBlXg/tM=
|
||||
modernc.org/libc v1.11.47/go.mod h1:tPkE4PzCTW27E6AIKIR5IwHAQKCAtudEIeAV1/SiyBg=
|
||||
modernc.org/libc v1.11.49/go.mod h1:9JrJuK5WTtoTWIFQ7QjX2Mb/bagYdZdscI3xrvHbXjE=
|
||||
modernc.org/libc v1.11.51/go.mod h1:R9I8u9TS+meaWLdbfQhq2kFknTW0O3aw3kEMqDDxMaM=
|
||||
modernc.org/libc v1.11.53/go.mod h1:5ip5vWYPAoMulkQ5XlSJTy12Sz5U6blOQiYasilVPsU=
|
||||
modernc.org/libc v1.11.54/go.mod h1:S/FVnskbzVUrjfBqlGFIPA5m7UwB3n9fojHhCNfSsnw=
|
||||
modernc.org/libc v1.11.55/go.mod h1:j2A5YBRm6HjNkoSs/fzZrSxCuwWqcMYTDPLNx0URn3M=
|
||||
modernc.org/libc v1.11.56/go.mod h1:pakHkg5JdMLt2OgRadpPOTnyRXm/uzu+Yyg/LSLdi18=
|
||||
modernc.org/libc v1.11.58/go.mod h1:ns94Rxv0OWyoQrDqMFfWwka2BcaF6/61CqJRK9LP7S8=
|
||||
modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw=
|
||||
modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0=
|
||||
modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI=
|
||||
modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE=
|
||||
modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY=
|
||||
modernc.org/libc v1.11.88/go.mod h1:h3oIVe8dxmTcchcFuCcJ4nAWaoiwzKCdv82MM0oiIdQ=
|
||||
modernc.org/libc v1.11.90/go.mod h1:ynK5sbjsU77AP+nn61+k+wxUGRx9rOFcIqWYYMaDZ4c=
|
||||
modernc.org/libc v1.11.98/go.mod h1:ynK5sbjsU77AP+nn61+k+wxUGRx9rOFcIqWYYMaDZ4c=
|
||||
modernc.org/libc v1.11.99/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI=
|
||||
modernc.org/libc v1.11.101/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI=
|
||||
modernc.org/libc v1.11.104/go.mod h1:2MH3DaF/gCU8i/UBiVE1VFRos4o523M7zipmwH8SIgQ=
|
||||
modernc.org/libc v1.12.0/go.mod h1:2MH3DaF/gCU8i/UBiVE1VFRos4o523M7zipmwH8SIgQ=
|
||||
modernc.org/libc v1.14.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk=
|
||||
modernc.org/libc v1.14.2/go.mod h1:MX1GBLnRLNdvmK9azU9LCxZ5lMyhrbEMK8rG3X/Fe34=
|
||||
modernc.org/libc v1.14.3/go.mod h1:GPIvQVOVPizzlqyRX3l756/3ppsAgg1QgPxjr5Q4agQ=
|
||||
modernc.org/libc v1.14.6/go.mod h1:2PJHINagVxO4QW/5OQdRrvMYo+bm5ClpUFfyXCYl9ak=
|
||||
modernc.org/libc v1.14.7/go.mod h1:f8xfWXW8LW41qb4X5+huVQo5dcfPlq7Cbny2TDheMv0=
|
||||
modernc.org/libc v1.14.8/go.mod h1:9+JCLb1MWSY23smyOpIPbd5ED+rSS/ieiDWUpdyO3mo=
|
||||
modernc.org/libc v1.14.10/go.mod h1:y1MtIWhwpJFpLYm6grAThtuXJKEsY6xkdZmXbRngIdo=
|
||||
modernc.org/libc v1.14.11/go.mod h1:l5/Mz/GrZwOqzwRHA3abgSCnSeJzzTl+Ify0bAwKbAw=
|
||||
modernc.org/libc v1.14.12 h1:pUBZTYoISfbb4pCf4PECENpbvwDBxeKc+/dS9LyOWFM=
|
||||
modernc.org/libc v1.14.12/go.mod h1:fJdoe23MHu2ruPQkFPPqCpToDi5cckzsbmkI6Ez0LqQ=
|
||||
modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A=
|
||||
modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU=
|
||||
modernc.org/libc v1.16.7 h1:qzQtHhsZNpVPpeCu+aMIQldXeV1P0vRhSqCL0nOIJOA=
|
||||
modernc.org/libc v1.16.7/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU=
|
||||
modernc.org/libc v1.22.2/go.mod h1:uvQavJ1pZ0hIoC/jfqNoMLURIMhKzINIWypNM17puug=
|
||||
modernc.org/lldb v1.0.0/go.mod h1:jcRvJGWfCGodDZz8BPwiKMJxGJngQ/5DrRapkQnLob8=
|
||||
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
|
||||
modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
|
||||
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc=
|
||||
modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM=
|
||||
modernc.org/memory v1.0.6/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
|
||||
modernc.org/memory v1.0.7 h1:UE3cxTRFa5tfUibAV7Jqq8P7zRY0OlJg+yWVIIaluEE=
|
||||
modernc.org/memory v1.0.7/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
|
||||
modernc.org/memory v1.1.1 h1:bDOL0DIDLQv7bWhP3gMvIrnoFw+Eo6F7a2QK9HPDiFU=
|
||||
modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
|
||||
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A=
|
||||
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/ql v1.0.0/go.mod h1:xGVyrLIatPcO2C1JvI/Co8c0sr6y91HKFNy4pt9JXEY=
|
||||
modernc.org/sortutil v1.1.0/go.mod h1:ZyL98OQHJgH9IEfN71VsamvJgrtRX9Dj2gX+vH86L1k=
|
||||
modernc.org/sqlite v1.10.6/go.mod h1:Z9FEjUtZP4qFEg6/SiADg9XCER7aYy9a/j7Pg9P7CPs=
|
||||
modernc.org/sqlite v1.14.3/go.mod h1:xMpicS1i2MJ4C8+Ap0vYBqTwYfpFvdnPE6brbFOtV2Y=
|
||||
modernc.org/sqlite v1.15.3 h1:3C4AWicF7S5vUUFJuBi7Ws8eWlPjqyo/c4Z1UGYBbyg=
|
||||
modernc.org/sqlite v1.15.3/go.mod h1:J7GAPbk8Txp0DJnT8TGwpUqJW0Z1cK2YpzjoXaZRU8k=
|
||||
modernc.org/sqlite v1.18.0 h1:ef66qJSgKeyLyrF4kQ2RHw/Ue3V89fyFNbGL073aDjI=
|
||||
modernc.org/sqlite v1.18.0/go.mod h1:B9fRWZacNxJBHoCJZQr1R54zhVn3fjfl0aszflrTSxY=
|
||||
modernc.org/sqlite v1.20.1/go.mod h1:fODt+bFmc/j8LcoCbMSkAuKuGmhxjG45KGc25N2705M=
|
||||
modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
|
||||
modernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs=
|
||||
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
|
||||
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
|
||||
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
|
||||
modernc.org/tcl v1.5.2/go.mod h1:pmJYOLgpiys3oI4AeAafkcUfE+TKKilminxNyU/+Zlo=
|
||||
modernc.org/tcl v1.9.2/go.mod h1:aw7OnlIoiuJgu1gwbTZtrKnGpDqH9wyH++jZcxdqNsg=
|
||||
modernc.org/tcl v1.11.2 h1:mXpsx3AZqJt83uDiFu9UYQVBjNjaWKGCF1YDSlpCL6Y=
|
||||
modernc.org/tcl v1.11.2/go.mod h1:BRzgpajcGdS2qTxniOx9c/dcxjlbA7p12eJNmiriQYo=
|
||||
modernc.org/tcl v1.13.1 h1:npxzTwFTZYM8ghWicVIX1cRWzj7Nd8i6AqqX2p+IYao=
|
||||
modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw=
|
||||
modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk=
|
||||
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
modernc.org/z v1.0.1-0.20210308123920-1f282aa71362/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA=
|
||||
modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA=
|
||||
modernc.org/z v1.2.20/go.mod h1:zU9FiF4PbHdOTUxw+IF8j7ArBMRPsHgq10uVPt6xTzo=
|
||||
modernc.org/z v1.3.2 h1:4GWBVMa48UDC7KQ9tnaggN/yTlXg+CdCX9bhgHPQ9AM=
|
||||
modernc.org/z v1.3.2/go.mod h1:PEU2oK2OEA1CfzDTd+8E908qEXhC9s0MfyKp5LZsd+k=
|
||||
modernc.org/z v1.5.1 h1:RTNHdsrOpeoSeOF4FbzTo8gBYByaJ5xT7NgZ9ZqRiJM=
|
||||
modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8=
|
||||
modernc.org/zappy v1.0.0/go.mod h1:hHe+oGahLVII/aTTyWK/b53VDHMAGCBYYeZ9sn83HC4=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
|
@ -1,6 +1,6 @@
|
||||
module github.com/mattermost/focalboard/server
|
||||
|
||||
go 1.18
|
||||
go 1.19
|
||||
|
||||
require github.com/golang/mock v1.6.0
|
||||
|
||||
|
@ -13,10 +13,6 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
testTeamID = "team-id"
|
||||
)
|
||||
|
||||
func TestGetBoards(t *testing.T) {
|
||||
t.Run("a non authenticated client should be rejected", func(t *testing.T) {
|
||||
th := SetupTestHelper(t).InitBasic()
|
||||
@ -2082,8 +2078,8 @@ func TestDuplicateBoard(t *testing.T) {
|
||||
|
||||
var duplicateBoardCategoryID string
|
||||
for _, categoryBoard := range userCategoryBoards {
|
||||
for _, boardID := range categoryBoard.BoardIDs {
|
||||
if boardID == duplicateBoard.ID {
|
||||
for _, boardMetadata := range categoryBoard.BoardMetadata {
|
||||
if boardMetadata.BoardID == duplicateBoard.ID {
|
||||
duplicateBoardCategoryID = categoryBoard.Category.ID
|
||||
}
|
||||
}
|
||||
|
@ -269,9 +269,7 @@ func TestGetCard(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
//
|
||||
// Helpers.
|
||||
//
|
||||
func reverse(src []string) []string {
|
||||
out := make([]string, 0, len(src))
|
||||
for i := len(src) - 1; i >= 0; i-- {
|
||||
|
@ -29,6 +29,7 @@ const (
|
||||
user1Username = "user1"
|
||||
user2Username = "user2"
|
||||
password = "Pa$$word"
|
||||
testTeamID = "team-id"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -457,6 +458,16 @@ func (th *TestHelper) CreateBoard(teamID string, boardType model.BoardType) *mod
|
||||
return board
|
||||
}
|
||||
|
||||
func (th *TestHelper) CreateBoards(teamID string, boardType model.BoardType, count int) []*model.Board {
|
||||
boards := make([]*model.Board, 0, count)
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
board := th.CreateBoard(teamID, boardType)
|
||||
boards = append(boards, board)
|
||||
}
|
||||
return boards
|
||||
}
|
||||
|
||||
func (th *TestHelper) CreateCategory(category model.Category) *model.Category {
|
||||
cat, resp := th.Client.CreateCategory(category)
|
||||
th.CheckOK(resp)
|
||||
|
360
server/integrationtests/compliance_test.go
Normal file
360
server/integrationtests/compliance_test.go
Normal file
@ -0,0 +1,360 @@
|
||||
package integrationtests
|
||||
|
||||
import (
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/focalboard/server/model"
|
||||
"github.com/mattermost/focalboard/server/utils"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
OneHour int64 = 360000
|
||||
OneDay int64 = OneHour * 24
|
||||
OneYear int64 = OneDay * 365
|
||||
)
|
||||
|
||||
func setupTestHelperForCompliance(t *testing.T, complianceLicense bool) (*TestHelper, Clients) {
|
||||
os.Setenv("FOCALBOARD_UNIT_TESTING_COMPLIANCE", strconv.FormatBool(complianceLicense))
|
||||
|
||||
th := SetupTestHelperPluginMode(t)
|
||||
clients := setupClients(th)
|
||||
|
||||
th.Client = clients.TeamMember
|
||||
th.Client2 = clients.TeamMember
|
||||
|
||||
return th, clients
|
||||
}
|
||||
|
||||
func TestGetBoardsForCompliance(t *testing.T) {
|
||||
t.Run("missing Features.Compliance license should fail", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, false)
|
||||
defer th.TearDown()
|
||||
|
||||
_ = th.CreateBoards(testTeamID, model.BoardTypeOpen, 2)
|
||||
|
||||
bcr, resp := clients.Admin.GetBoardsForCompliance(testTeamID, 0, 0)
|
||||
|
||||
th.CheckNotImplemented(resp)
|
||||
require.Nil(t, bcr)
|
||||
})
|
||||
|
||||
t.Run("a non authenticated user should be rejected", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
_ = th.CreateBoards(testTeamID, model.BoardTypeOpen, 2)
|
||||
th.Logout(th.Client)
|
||||
|
||||
bcr, resp := clients.Anon.GetBoardsForCompliance(testTeamID, 0, 0)
|
||||
|
||||
th.CheckUnauthorized(resp)
|
||||
require.Nil(t, bcr)
|
||||
})
|
||||
|
||||
t.Run("a user without manage_system permission should be rejected", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
_ = th.CreateBoards(testTeamID, model.BoardTypeOpen, 2)
|
||||
|
||||
bcr, resp := clients.TeamMember.GetBoardsForCompliance(testTeamID, 0, 0)
|
||||
|
||||
th.CheckUnauthorized(resp)
|
||||
require.Nil(t, bcr)
|
||||
})
|
||||
|
||||
t.Run("good call", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
const count = 10
|
||||
_ = th.CreateBoards(testTeamID, model.BoardTypeOpen, count)
|
||||
|
||||
bcr, resp := clients.Admin.GetBoardsForCompliance(testTeamID, 0, 0)
|
||||
th.CheckOK(resp)
|
||||
require.False(t, bcr.HasNext)
|
||||
require.Len(t, bcr.Results, count)
|
||||
})
|
||||
|
||||
t.Run("pagination", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
const count = 20
|
||||
const perPage = 3
|
||||
_ = th.CreateBoards(testTeamID, model.BoardTypeOpen, count)
|
||||
|
||||
boards := make([]*model.Board, 0, count)
|
||||
page := 0
|
||||
for {
|
||||
bcr, resp := clients.Admin.GetBoardsForCompliance(testTeamID, page, perPage)
|
||||
page++
|
||||
th.CheckOK(resp)
|
||||
boards = append(boards, bcr.Results...)
|
||||
if !bcr.HasNext {
|
||||
break
|
||||
}
|
||||
}
|
||||
require.Len(t, boards, count)
|
||||
require.Equal(t, int(math.Floor((count/perPage)+1)), page)
|
||||
})
|
||||
|
||||
t.Run("invalid teamID", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
_ = th.CreateBoards(testTeamID, model.BoardTypeOpen, 2)
|
||||
|
||||
bcr, resp := clients.Admin.GetBoardsForCompliance(utils.NewID(utils.IDTypeTeam), 0, 0)
|
||||
|
||||
th.CheckBadRequest(resp)
|
||||
require.Nil(t, bcr)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetBoardsComplianceHistory(t *testing.T) {
|
||||
t.Run("missing Features.Compliance license should fail", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, false)
|
||||
defer th.TearDown()
|
||||
|
||||
_ = th.CreateBoards(testTeamID, model.BoardTypeOpen, 2)
|
||||
|
||||
bchr, resp := clients.Admin.GetBoardsComplianceHistory(utils.GetMillis()-OneDay, true, testTeamID, 0, 0)
|
||||
|
||||
th.CheckNotImplemented(resp)
|
||||
require.Nil(t, bchr)
|
||||
})
|
||||
|
||||
t.Run("a non authenticated user should be rejected", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
_ = th.CreateBoards(testTeamID, model.BoardTypeOpen, 2)
|
||||
th.Logout(th.Client)
|
||||
|
||||
bchr, resp := clients.Anon.GetBoardsComplianceHistory(utils.GetMillis()-OneDay, true, testTeamID, 0, 0)
|
||||
|
||||
th.CheckUnauthorized(resp)
|
||||
require.Nil(t, bchr)
|
||||
})
|
||||
|
||||
t.Run("a user without manage_system permission should be rejected", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
_ = th.CreateBoards(testTeamID, model.BoardTypeOpen, 2)
|
||||
|
||||
bchr, resp := clients.TeamMember.GetBoardsComplianceHistory(utils.GetMillis()-OneDay, true, testTeamID, 0, 0)
|
||||
|
||||
th.CheckUnauthorized(resp)
|
||||
require.Nil(t, bchr)
|
||||
})
|
||||
|
||||
t.Run("good call, exclude deleted", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
const count = 10
|
||||
boards := th.CreateBoards(testTeamID, model.BoardTypeOpen, count)
|
||||
|
||||
deleted, resp := th.Client.DeleteBoard(boards[0].ID)
|
||||
th.CheckOK(resp)
|
||||
require.True(t, deleted)
|
||||
|
||||
deleted, resp = th.Client.DeleteBoard(boards[1].ID)
|
||||
th.CheckOK(resp)
|
||||
require.True(t, deleted)
|
||||
|
||||
bchr, resp := clients.Admin.GetBoardsComplianceHistory(utils.GetMillis()-OneDay, false, testTeamID, 0, 0)
|
||||
th.CheckOK(resp)
|
||||
require.False(t, bchr.HasNext)
|
||||
require.Len(t, bchr.Results, count-2) // two boards deleted
|
||||
})
|
||||
|
||||
t.Run("good call, include deleted", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
const count = 10
|
||||
boards := th.CreateBoards(testTeamID, model.BoardTypeOpen, count)
|
||||
|
||||
deleted, resp := th.Client.DeleteBoard(boards[0].ID)
|
||||
th.CheckOK(resp)
|
||||
require.True(t, deleted)
|
||||
|
||||
deleted, resp = th.Client.DeleteBoard(boards[1].ID)
|
||||
th.CheckOK(resp)
|
||||
require.True(t, deleted)
|
||||
|
||||
bchr, resp := clients.Admin.GetBoardsComplianceHistory(utils.GetMillis()-OneDay, true, testTeamID, 0, 0)
|
||||
th.CheckOK(resp)
|
||||
require.False(t, bchr.HasNext)
|
||||
require.Len(t, bchr.Results, count+2) // both deleted boards have 2 history records each
|
||||
})
|
||||
|
||||
t.Run("pagination", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
const count = 20
|
||||
const perPage = 3
|
||||
_ = th.CreateBoards(testTeamID, model.BoardTypeOpen, count)
|
||||
|
||||
boardHistory := make([]*model.BoardHistory, 0, count)
|
||||
page := 0
|
||||
for {
|
||||
bchr, resp := clients.Admin.GetBoardsComplianceHistory(utils.GetMillis()-OneDay, true, testTeamID, page, perPage)
|
||||
page++
|
||||
th.CheckOK(resp)
|
||||
boardHistory = append(boardHistory, bchr.Results...)
|
||||
if !bchr.HasNext {
|
||||
break
|
||||
}
|
||||
}
|
||||
require.Len(t, boardHistory, count)
|
||||
require.Equal(t, int(math.Floor((count/perPage)+1)), page)
|
||||
})
|
||||
|
||||
t.Run("invalid teamID", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
_ = th.CreateBoards(testTeamID, model.BoardTypeOpen, 2)
|
||||
|
||||
bchr, resp := clients.Admin.GetBoardsComplianceHistory(utils.GetMillis()-OneDay, true, utils.NewID(utils.IDTypeTeam), 0, 0)
|
||||
|
||||
th.CheckBadRequest(resp)
|
||||
require.Nil(t, bchr)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetBlocksComplianceHistory(t *testing.T) {
|
||||
t.Run("missing Features.Compliance license should fail", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, false)
|
||||
defer th.TearDown()
|
||||
|
||||
board, _ := th.CreateBoardAndCards(testTeamID, model.BoardTypeOpen, 2)
|
||||
|
||||
bchr, resp := clients.Admin.GetBlocksComplianceHistory(utils.GetMillis()-OneDay, true, testTeamID, board.ID, 0, 0)
|
||||
|
||||
th.CheckNotImplemented(resp)
|
||||
require.Nil(t, bchr)
|
||||
})
|
||||
|
||||
t.Run("a non authenticated user should be rejected", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
board, _ := th.CreateBoardAndCards(testTeamID, model.BoardTypeOpen, 2)
|
||||
|
||||
bchr, resp := clients.Anon.GetBlocksComplianceHistory(utils.GetMillis()-OneDay, true, testTeamID, board.ID, 0, 0)
|
||||
|
||||
th.CheckUnauthorized(resp)
|
||||
require.Nil(t, bchr)
|
||||
})
|
||||
|
||||
t.Run("a user without manage_system permission should be rejected", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
board, _ := th.CreateBoardAndCards(testTeamID, model.BoardTypeOpen, 2)
|
||||
|
||||
bchr, resp := clients.TeamMember.GetBlocksComplianceHistory(utils.GetMillis()-OneDay, true, testTeamID, board.ID, 0, 0)
|
||||
|
||||
th.CheckUnauthorized(resp)
|
||||
require.Nil(t, bchr)
|
||||
})
|
||||
|
||||
t.Run("good call, exclude deleted", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
const count = 10
|
||||
board, cards := th.CreateBoardAndCards(testTeamID, model.BoardTypeOpen, count)
|
||||
|
||||
deleted, resp := th.Client.DeleteBlock(board.ID, cards[0].ID, true)
|
||||
th.CheckOK(resp)
|
||||
require.True(t, deleted)
|
||||
|
||||
deleted, resp = th.Client.DeleteBlock(board.ID, cards[1].ID, true)
|
||||
th.CheckOK(resp)
|
||||
require.True(t, deleted)
|
||||
|
||||
bchr, resp := clients.Admin.GetBlocksComplianceHistory(utils.GetMillis()-OneDay, false, testTeamID, board.ID, 0, 0)
|
||||
th.CheckOK(resp)
|
||||
require.False(t, bchr.HasNext)
|
||||
require.Len(t, bchr.Results, count-2) // 2 blocks deleted
|
||||
})
|
||||
|
||||
t.Run("good call, include deleted", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
const count = 10
|
||||
board, cards := th.CreateBoardAndCards(testTeamID, model.BoardTypeOpen, count)
|
||||
|
||||
deleted, resp := th.Client.DeleteBlock(board.ID, cards[0].ID, true)
|
||||
th.CheckOK(resp)
|
||||
require.True(t, deleted)
|
||||
|
||||
deleted, resp = th.Client.DeleteBlock(board.ID, cards[1].ID, true)
|
||||
th.CheckOK(resp)
|
||||
require.True(t, deleted)
|
||||
|
||||
bchr, resp := clients.Admin.GetBlocksComplianceHistory(utils.GetMillis()-OneDay, true, testTeamID, board.ID, 0, 0)
|
||||
th.CheckOK(resp)
|
||||
require.False(t, bchr.HasNext)
|
||||
require.Len(t, bchr.Results, count+2) // both deleted boards have 2 history records each
|
||||
})
|
||||
|
||||
t.Run("pagination", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
const count = 20
|
||||
const perPage = 3
|
||||
board, _ := th.CreateBoardAndCards(testTeamID, model.BoardTypeOpen, count)
|
||||
|
||||
blockHistory := make([]*model.BlockHistory, 0, count)
|
||||
page := 0
|
||||
for {
|
||||
bchr, resp := clients.Admin.GetBlocksComplianceHistory(utils.GetMillis()-OneDay, true, testTeamID, board.ID, page, perPage)
|
||||
page++
|
||||
th.CheckOK(resp)
|
||||
blockHistory = append(blockHistory, bchr.Results...)
|
||||
if !bchr.HasNext {
|
||||
break
|
||||
}
|
||||
}
|
||||
require.Len(t, blockHistory, count)
|
||||
require.Equal(t, int(math.Floor((count/perPage)+1)), page)
|
||||
})
|
||||
|
||||
t.Run("invalid teamID", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
board, _ := th.CreateBoardAndCards(testTeamID, model.BoardTypeOpen, 2)
|
||||
|
||||
bchr, resp := clients.Admin.GetBlocksComplianceHistory(utils.GetMillis()-OneDay, true, utils.NewID(utils.IDTypeTeam), board.ID, 0, 0)
|
||||
|
||||
th.CheckBadRequest(resp)
|
||||
require.Nil(t, bchr)
|
||||
})
|
||||
|
||||
t.Run("invalid boardID", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
_, _ = th.CreateBoardAndCards(testTeamID, model.BoardTypeOpen, 2)
|
||||
|
||||
bchr, resp := clients.Admin.GetBlocksComplianceHistory(utils.GetMillis()-OneDay, true, testTeamID, utils.NewID(utils.IDTypeBoard), 0, 0)
|
||||
|
||||
th.CheckBadRequest(resp)
|
||||
require.Nil(t, bchr)
|
||||
})
|
||||
}
|
@ -2964,7 +2964,7 @@ func TestPermissionsClientConfig(t *testing.T) {
|
||||
func TestPermissionsGetCategories(t *testing.T) {
|
||||
ttCases := []TestCase{
|
||||
{"/teams/test-team/categories", methodGet, "", userAnon, http.StatusUnauthorized, 1},
|
||||
{"/teams/test-team/categories", methodGet, "", userNoTeamMember, http.StatusOK, 1},
|
||||
{"/teams/test-team/categories", methodGet, "", userNoTeamMember, http.StatusForbidden, 1},
|
||||
{"/teams/test-team/categories", methodGet, "", userTeamMember, http.StatusOK, 1},
|
||||
{"/teams/test-team/categories", methodGet, "", userViewer, http.StatusOK, 1},
|
||||
{"/teams/test-team/categories", methodGet, "", userCommenter, http.StatusOK, 1},
|
||||
@ -2985,6 +2985,8 @@ func TestPermissionsGetCategories(t *testing.T) {
|
||||
defer th.TearDown()
|
||||
clients := setupLocalClients(th)
|
||||
testData := setupData(t, th)
|
||||
ttCases[1].expectedStatusCode = http.StatusOK
|
||||
ttCases[1].totalResults = 1
|
||||
runTestCases(t, ttCases, testData, clients)
|
||||
})
|
||||
}
|
||||
@ -3003,7 +3005,7 @@ func TestPermissionsCreateCategory(t *testing.T) {
|
||||
|
||||
return []TestCase{
|
||||
{"/teams/test-team/categories", methodPost, category(""), userAnon, http.StatusUnauthorized, 0},
|
||||
{"/teams/test-team/categories", methodPost, category(userNoTeamMemberID), userNoTeamMember, http.StatusOK, 1},
|
||||
{"/teams/test-team/categories", methodPost, category(userNoTeamMemberID), userNoTeamMember, http.StatusForbidden, 0},
|
||||
{"/teams/test-team/categories", methodPost, category(userTeamMemberID), userTeamMember, http.StatusOK, 1},
|
||||
{"/teams/test-team/categories", methodPost, category(userViewerID), userViewer, http.StatusOK, 1},
|
||||
{"/teams/test-team/categories", methodPost, category(userCommenterID), userCommenter, http.StatusOK, 1},
|
||||
@ -3044,6 +3046,8 @@ func TestPermissionsCreateCategory(t *testing.T) {
|
||||
clients := setupLocalClients(th)
|
||||
testData := setupData(t, th)
|
||||
ttCases := ttCasesF()
|
||||
ttCases[1].expectedStatusCode = http.StatusOK
|
||||
ttCases[1].totalResults = 1
|
||||
runTestCases(t, ttCases, testData, clients)
|
||||
})
|
||||
}
|
||||
@ -3064,7 +3068,7 @@ func TestPermissionsUpdateCategory(t *testing.T) {
|
||||
|
||||
return []TestCase{
|
||||
{"/teams/test-team/categories/any", methodPut, category("", "any"), userAnonID, http.StatusUnauthorized, 0},
|
||||
{"/teams/test-team/categories/" + extraData["noTeamMember"], methodPut, category(userNoTeamMemberID, extraData["noTeamMember"]), userNoTeamMember, http.StatusOK, 1},
|
||||
{"/teams/test-team/categories/" + extraData["noTeamMember"], methodPut, category(userNoTeamMemberID, extraData["noTeamMember"]), userNoTeamMember, http.StatusForbidden, 0},
|
||||
{"/teams/test-team/categories/" + extraData["teamMember"], methodPut, category(userTeamMemberID, extraData["teamMember"]), userTeamMember, http.StatusOK, 1},
|
||||
{"/teams/test-team/categories/" + extraData["viewer"], methodPut, category(userViewerID, extraData["viewer"]), userViewer, http.StatusOK, 1},
|
||||
{"/teams/test-team/categories/" + extraData["commenter"], methodPut, category(userCommenterID, extraData["commenter"]), userCommenter, http.StatusOK, 1},
|
||||
@ -3148,6 +3152,8 @@ func TestPermissionsUpdateCategory(t *testing.T) {
|
||||
testData := setupData(t, th)
|
||||
extraData := extraSetup(t, th)
|
||||
ttCases := ttCasesF(extraData)
|
||||
ttCases[1].expectedStatusCode = http.StatusOK
|
||||
ttCases[1].totalResults = 1
|
||||
runTestCases(t, ttCases, testData, clients)
|
||||
})
|
||||
}
|
||||
@ -3156,7 +3162,7 @@ func TestPermissionsDeleteCategory(t *testing.T) {
|
||||
ttCasesF := func(extraData map[string]string) []TestCase {
|
||||
return []TestCase{
|
||||
{"/teams/other-team/categories/any", methodDelete, "", userAnon, http.StatusUnauthorized, 0},
|
||||
{"/teams/other-team/categories/" + extraData["noTeamMember"], methodDelete, "", userNoTeamMember, http.StatusBadRequest, 0},
|
||||
{"/teams/other-team/categories/" + extraData["noTeamMember"], methodDelete, "", userNoTeamMember, http.StatusForbidden, 0},
|
||||
{"/teams/other-team/categories/" + extraData["teamMember"], methodDelete, "", userTeamMember, http.StatusBadRequest, 0},
|
||||
{"/teams/other-team/categories/" + extraData["viewer"], methodDelete, "", userViewer, http.StatusBadRequest, 0},
|
||||
{"/teams/other-team/categories/" + extraData["commenter"], methodDelete, "", userCommenter, http.StatusBadRequest, 0},
|
||||
@ -3165,7 +3171,7 @@ func TestPermissionsDeleteCategory(t *testing.T) {
|
||||
{"/teams/other-team/categories/" + extraData["guest"], methodDelete, "", userGuest, http.StatusBadRequest, 0},
|
||||
|
||||
{"/teams/test-team/categories/any", methodDelete, "", userAnon, http.StatusUnauthorized, 0},
|
||||
{"/teams/test-team/categories/" + extraData["noTeamMember"], methodDelete, "", userNoTeamMember, http.StatusOK, 1},
|
||||
{"/teams/test-team/categories/" + extraData["noTeamMember"], methodDelete, "", userNoTeamMember, http.StatusForbidden, 0},
|
||||
{"/teams/test-team/categories/" + extraData["teamMember"], methodDelete, "", userTeamMember, http.StatusOK, 1},
|
||||
{"/teams/test-team/categories/" + extraData["viewer"], methodDelete, "", userViewer, http.StatusOK, 1},
|
||||
{"/teams/test-team/categories/" + extraData["commenter"], methodDelete, "", userCommenter, http.StatusOK, 1},
|
||||
@ -3231,6 +3237,10 @@ func TestPermissionsDeleteCategory(t *testing.T) {
|
||||
testData := setupData(t, th)
|
||||
extraData := extraSetup(t, th)
|
||||
ttCases := ttCasesF(extraData)
|
||||
ttCases[1].expectedStatusCode = http.StatusBadRequest
|
||||
ttCases[1].totalResults = 0
|
||||
ttCases[9].expectedStatusCode = http.StatusOK
|
||||
ttCases[9].totalResults = 1
|
||||
runTestCases(t, ttCases, testData, clients)
|
||||
})
|
||||
}
|
||||
@ -3239,7 +3249,7 @@ func TestPermissionsUpdateCategoryBoard(t *testing.T) {
|
||||
ttCasesF := func(testData TestData, extraData map[string]string) []TestCase {
|
||||
return []TestCase{
|
||||
{"/teams/test-team/categories/any/boards/any", methodPost, "", userAnon, http.StatusUnauthorized, 0},
|
||||
{"/teams/test-team/categories/" + extraData["noTeamMember"] + "/boards/" + testData.publicBoard.ID, methodPost, "", userNoTeamMember, http.StatusOK, 0},
|
||||
{"/teams/test-team/categories/" + extraData["noTeamMember"] + "/boards/" + testData.publicBoard.ID, methodPost, "", userNoTeamMember, http.StatusForbidden, 0},
|
||||
{"/teams/test-team/categories/" + extraData["teamMember"] + "/boards/" + testData.publicBoard.ID, methodPost, "", userTeamMember, http.StatusOK, 0},
|
||||
{"/teams/test-team/categories/" + extraData["viewer"] + "/boards/" + testData.publicBoard.ID, methodPost, "", userViewer, http.StatusOK, 0},
|
||||
{"/teams/test-team/categories/" + extraData["commenter"] + "/boards/" + testData.publicBoard.ID, methodPost, "", userCommenter, http.StatusOK, 0},
|
||||
@ -3305,6 +3315,8 @@ func TestPermissionsUpdateCategoryBoard(t *testing.T) {
|
||||
testData := setupData(t, th)
|
||||
extraData := extraSetup(t, th)
|
||||
ttCases := ttCasesF(testData, extraData)
|
||||
ttCases[1].expectedStatusCode = http.StatusOK
|
||||
ttCases[1].totalResults = 0
|
||||
runTestCases(t, ttCases, testData, clients)
|
||||
})
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ package integrationtests
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/mattermost/focalboard/server/model"
|
||||
@ -89,7 +91,7 @@ func (s *PluginTestStore) GetTeam(id string) (*model.Team, error) {
|
||||
return s.baseTeam, nil
|
||||
case "other-team":
|
||||
return s.otherTeam, nil
|
||||
case "test-team":
|
||||
case "test-team", testTeamID:
|
||||
return s.testTeam, nil
|
||||
case "empty-team":
|
||||
return s.emptyTeam, nil
|
||||
@ -293,3 +295,27 @@ func (s *PluginTestStore) SearchBoardsForUser(term string, field model.BoardSear
|
||||
}
|
||||
return resultBoards, nil
|
||||
}
|
||||
|
||||
func (s *PluginTestStore) GetLicense() *mmModel.License {
|
||||
license := s.Store.GetLicense()
|
||||
|
||||
if license == nil {
|
||||
license = &mmModel.License{
|
||||
Id: mmModel.NewId(),
|
||||
StartsAt: mmModel.GetMillis() - 2629746000, // 1 month
|
||||
ExpiresAt: mmModel.GetMillis() + 2629746000, //
|
||||
IssuedAt: mmModel.GetMillis() - 2629746000,
|
||||
Features: &mmModel.Features{},
|
||||
}
|
||||
license.Features.SetDefaults()
|
||||
}
|
||||
|
||||
complianceLicense := os.Getenv("FOCALBOARD_UNIT_TESTING_COMPLIANCE")
|
||||
if complianceLicense != "" {
|
||||
if val, err := strconv.ParseBool(complianceLicense); err == nil {
|
||||
license.Features.Compliance = mmModel.NewBool(val)
|
||||
}
|
||||
}
|
||||
|
||||
return license
|
||||
}
|
||||
|
@ -18,8 +18,8 @@ func TestSidebar(t *testing.T) {
|
||||
categoryBoards := th.GetUserCategoryBoards("team-id")
|
||||
require.Equal(t, 1, len(categoryBoards))
|
||||
require.Equal(t, "Boards", categoryBoards[0].Name)
|
||||
require.Equal(t, 1, len(categoryBoards[0].BoardIDs))
|
||||
require.Equal(t, board.ID, categoryBoards[0].BoardIDs[0])
|
||||
require.Equal(t, 1, len(categoryBoards[0].BoardMetadata))
|
||||
require.Equal(t, board.ID, categoryBoards[0].BoardMetadata[0].BoardID)
|
||||
|
||||
// create a new category, a new board
|
||||
// and move that board into the new category
|
||||
@ -39,8 +39,8 @@ func TestSidebar(t *testing.T) {
|
||||
// the newly created category should be the first one array
|
||||
// as new categories end up on top in LHS
|
||||
require.Equal(t, "Category 2", categoryBoards[0].Name)
|
||||
require.Equal(t, 1, len(categoryBoards[0].BoardIDs))
|
||||
require.Equal(t, board2.ID, categoryBoards[0].BoardIDs[0])
|
||||
require.Equal(t, 1, len(categoryBoards[0].BoardMetadata))
|
||||
require.Equal(t, board2.ID, categoryBoards[0].BoardMetadata[0].BoardID)
|
||||
|
||||
// now we'll delete the custom category we created, "Category 2"
|
||||
// and all it's boards should get moved to the Boards category
|
||||
@ -48,7 +48,51 @@ func TestSidebar(t *testing.T) {
|
||||
categoryBoards = th.GetUserCategoryBoards("team-id")
|
||||
require.Equal(t, 1, len(categoryBoards))
|
||||
require.Equal(t, "Boards", categoryBoards[0].Name)
|
||||
require.Equal(t, 2, len(categoryBoards[0].BoardIDs))
|
||||
require.Contains(t, categoryBoards[0].BoardIDs, board.ID)
|
||||
require.Contains(t, categoryBoards[0].BoardIDs, board2.ID)
|
||||
require.Equal(t, 2, len(categoryBoards[0].BoardMetadata))
|
||||
require.Contains(t, categoryBoards[0].BoardMetadata, model.CategoryBoardMetadata{BoardID: board.ID, Hidden: false})
|
||||
require.Contains(t, categoryBoards[0].BoardMetadata, model.CategoryBoardMetadata{BoardID: board2.ID, Hidden: false})
|
||||
}
|
||||
|
||||
func TestHideUnhideBoard(t *testing.T) {
|
||||
th := SetupTestHelperWithToken(t).Start()
|
||||
defer th.TearDown()
|
||||
|
||||
// we'll create a new board.
|
||||
// The board should end up in a default "Boards" category
|
||||
th.CreateBoard("team-id", "O")
|
||||
|
||||
// the created board should not be hidden
|
||||
categoryBoards := th.GetUserCategoryBoards("team-id")
|
||||
require.Equal(t, 1, len(categoryBoards))
|
||||
require.Equal(t, "Boards", categoryBoards[0].Name)
|
||||
require.Equal(t, 1, len(categoryBoards[0].BoardMetadata))
|
||||
require.False(t, categoryBoards[0].BoardMetadata[0].Hidden)
|
||||
|
||||
// now we'll hide the board
|
||||
response := th.Client.HideBoard("team-id", categoryBoards[0].ID, categoryBoards[0].BoardMetadata[0].BoardID)
|
||||
th.CheckOK(response)
|
||||
|
||||
// verifying if the board has been marked as hidden
|
||||
categoryBoards = th.GetUserCategoryBoards("team-id")
|
||||
require.True(t, categoryBoards[0].BoardMetadata[0].Hidden)
|
||||
|
||||
// trying to hide the already hidden board.This should have no effect
|
||||
response = th.Client.HideBoard("team-id", categoryBoards[0].ID, categoryBoards[0].BoardMetadata[0].BoardID)
|
||||
th.CheckOK(response)
|
||||
categoryBoards = th.GetUserCategoryBoards("team-id")
|
||||
require.True(t, categoryBoards[0].BoardMetadata[0].Hidden)
|
||||
|
||||
// now we'll unhide the board
|
||||
response = th.Client.UnhideBoard("team-id", categoryBoards[0].ID, categoryBoards[0].BoardMetadata[0].BoardID)
|
||||
th.CheckOK(response)
|
||||
|
||||
// verifying
|
||||
categoryBoards = th.GetUserCategoryBoards("team-id")
|
||||
require.False(t, categoryBoards[0].BoardMetadata[0].Hidden)
|
||||
|
||||
// trying to unhide the already visible board.This should have no effect
|
||||
response = th.Client.UnhideBoard("team-id", categoryBoards[0].ID, categoryBoards[0].BoardMetadata[0].BoardID)
|
||||
th.CheckOK(response)
|
||||
categoryBoards = th.GetUserCategoryBoards("team-id")
|
||||
require.False(t, categoryBoards[0].BoardMetadata[0].Hidden)
|
||||
}
|
||||
|
57
server/integrationtests/work_template_test.go
Normal file
57
server/integrationtests/work_template_test.go
Normal file
@ -0,0 +1,57 @@
|
||||
package integrationtests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// This test is there to guarantee that the board templates needed for
|
||||
// the work template are present in the default templates.
|
||||
// If this fails, you might need to sync with the channels team.
|
||||
func TestGetTemplatesForWorkTemplate(t *testing.T) {
|
||||
// map[name]trackingTemplateId
|
||||
knownInWorkTemplates := map[string]string{
|
||||
"Company Goals & OKRs": "7ba22ccfdfac391d63dea5c4b8cde0de",
|
||||
"Competitive Analysis": "06f4bff367a7c2126fab2380c9dec23c",
|
||||
"Content Calendar": "c75fbd659d2258b5183af2236d176ab4",
|
||||
"Meeting Agenda ": "54fcf9c610f0ac5e4c522c0657c90602",
|
||||
"Personal Goals ": "7f32dc8d2ae008cfe56554e9363505cc",
|
||||
"Personal Tasls ": "dfb70c146a4584b8a21837477c7b5431",
|
||||
"Project Tasks ": "a4ec399ab4f2088b1051c3cdf1dde4c3",
|
||||
"Roadmap ": "b728c6ca730e2cfc229741c5a4712b65",
|
||||
"Sales Pipeline CRM": "ecc250bb7dff0fe02247f1110f097544",
|
||||
"Sprint Planner ": "99b74e26d2f5d0a9b346d43c0a7bfb09",
|
||||
"Team Retrospective": "e4f03181c4ced8edd4d53d33d569a086",
|
||||
"User Research Sessions": "6c345c7f50f6833f78b7d0f08ce450a3",
|
||||
}
|
||||
th := SetupTestHelper(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
err := th.Server.App().InitTemplates()
|
||||
require.NoError(t, err, "InitTemplates should not fail")
|
||||
|
||||
rBoards, resp := th.Client.GetTemplatesForTeam("0")
|
||||
th.CheckOK(resp)
|
||||
require.NotNil(t, rBoards)
|
||||
|
||||
trackingTemplateIDs := []string{}
|
||||
for _, board := range rBoards {
|
||||
property, _ := board.GetPropertyString("trackingTemplateId")
|
||||
if property != "" {
|
||||
trackingTemplateIDs = append(trackingTemplateIDs, property)
|
||||
}
|
||||
}
|
||||
|
||||
// make sure all known templates are in trackingTemplateIds
|
||||
for name, ttID := range knownInWorkTemplates {
|
||||
found := false
|
||||
for _, trackingTemplateID := range trackingTemplateIDs {
|
||||
if trackingTemplateID == ttID {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
require.True(t, found, "trackingTemplateId %s for %s not found", ttID, name)
|
||||
}
|
||||
}
|
@ -122,7 +122,7 @@ func main() {
|
||||
if pDBConfig != nil && len(*pDBConfig) > 0 {
|
||||
config.DBConfigString = *pDBConfig
|
||||
// Don't echo, as the confix string may contain passwords
|
||||
logger.Info("DBConfigString overriden from commandline")
|
||||
logger.Info("DBConfigString overridden from commandline")
|
||||
}
|
||||
|
||||
if pPort != nil && *pPort > 0 && *pPort != config.Port {
|
||||
@ -166,6 +166,7 @@ func main() {
|
||||
}
|
||||
|
||||
// StartServer starts the server
|
||||
//
|
||||
//export StartServer
|
||||
func StartServer(webPath *C.char, filesPath *C.char, port int, singleUserToken, dbConfigString, configFilePath *C.char) {
|
||||
startServer(
|
||||
@ -179,6 +180,7 @@ func StartServer(webPath *C.char, filesPath *C.char, port int, singleUserToken,
|
||||
}
|
||||
|
||||
// StopServer stops the server
|
||||
//
|
||||
//export StopServer
|
||||
func StopServer() {
|
||||
stopServer()
|
||||
|
@ -199,6 +199,14 @@ type QueryBoardHistoryOptions struct {
|
||||
Descending bool // if true then the records are sorted by insert_at in descending order
|
||||
}
|
||||
|
||||
// QueryBlockHistoryOptions are query options that can be passed to GetBlockHistory.
|
||||
type QueryBlockHistoryChildOptions struct {
|
||||
BeforeUpdateAt int64 // if non-zero then filter for records with update_at less than BeforeUpdateAt
|
||||
AfterUpdateAt int64 // if non-zero then filter for records with update_at greater than AfterUpdateAt
|
||||
Page int // page number to select when paginating
|
||||
PerPage int // number of blocks per page (default=-1, meaning unlimited)
|
||||
}
|
||||
|
||||
func StampModificationMetadata(userID string, blocks []*Block, auditRec *audit.Record) {
|
||||
if userID == SingleUser {
|
||||
userID = ""
|
||||
|
@ -14,13 +14,16 @@ import (
|
||||
type BlockType string
|
||||
|
||||
const (
|
||||
TypeUnknown = "unknown"
|
||||
TypeBoard = "board"
|
||||
TypeCard = "card"
|
||||
TypeView = "view"
|
||||
TypeText = "text"
|
||||
TypeComment = "comment"
|
||||
TypeImage = "image"
|
||||
TypeUnknown = "unknown"
|
||||
TypeBoard = "board"
|
||||
TypeCard = "card"
|
||||
TypeView = "view"
|
||||
TypeText = "text"
|
||||
TypeCheckbox = "checkbox"
|
||||
TypeComment = "comment"
|
||||
TypeImage = "image"
|
||||
TypeAttachment = "attachment"
|
||||
TypeDivider = "divider"
|
||||
)
|
||||
|
||||
func (bt BlockType) String() string {
|
||||
@ -38,10 +41,16 @@ func BlockTypeFromString(s string) (BlockType, error) {
|
||||
return TypeView, nil
|
||||
case "text":
|
||||
return TypeText, nil
|
||||
case "checkbox":
|
||||
return TypeCheckbox, nil
|
||||
case "comment":
|
||||
return TypeComment, nil
|
||||
case "image":
|
||||
return TypeImage, nil
|
||||
case "attachment":
|
||||
return TypeAttachment, nil
|
||||
case "divider":
|
||||
return TypeDivider, nil
|
||||
}
|
||||
return TypeUnknown, ErrInvalidBlockType{s}
|
||||
}
|
||||
@ -55,8 +64,10 @@ func BlockType2IDType(blockType BlockType) utils.IDType {
|
||||
return utils.IDTypeCard
|
||||
case TypeView:
|
||||
return utils.IDTypeView
|
||||
case TypeText, TypeComment:
|
||||
case TypeText, TypeCheckbox, TypeComment, TypeDivider:
|
||||
return utils.IDTypeBlock
|
||||
case TypeImage, TypeAttachment:
|
||||
return utils.IDTypeAttachment
|
||||
}
|
||||
return utils.IDTypeNone
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ type CategoryBoards struct {
|
||||
|
||||
// The IDs of boards in this category
|
||||
// required: true
|
||||
BoardIDs []string `json:"boardIDs"`
|
||||
BoardMetadata []CategoryBoardMetadata `json:"boardMetadata"`
|
||||
|
||||
// The relative sort order of this board in its category
|
||||
// required: true
|
||||
@ -19,4 +19,10 @@ type CategoryBoards struct {
|
||||
type BoardCategoryWebsocketData struct {
|
||||
BoardID string `json:"boardID"`
|
||||
CategoryID string `json:"categoryID"`
|
||||
Hidden bool `json:"hidden"`
|
||||
}
|
||||
|
||||
type CategoryBoardMetadata struct {
|
||||
BoardID string `json:"boardID"`
|
||||
Hidden bool `json:"hidden"`
|
||||
}
|
||||
|
88
server/model/compliance.go
Normal file
88
server/model/compliance.go
Normal file
@ -0,0 +1,88 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
package model
|
||||
|
||||
// BaordsComplianceResponse is the response body to a request for boards.
|
||||
// swagger:model
|
||||
type BoardsComplianceResponse struct {
|
||||
// True if there is a next page for pagination
|
||||
// required: true
|
||||
HasNext bool `json:"hasNext"`
|
||||
|
||||
// The array of board records.
|
||||
// required: true
|
||||
Results []*Board `json:"results"`
|
||||
}
|
||||
|
||||
// BoardsComplianceHistoryResponse is the response body to a request for boards history.
|
||||
// swagger:model
|
||||
type BoardsComplianceHistoryResponse struct {
|
||||
// True if there is a next page for pagination
|
||||
// required: true
|
||||
HasNext bool `json:"hasNext"`
|
||||
|
||||
// The array of BoardHistory records.
|
||||
// required: true
|
||||
Results []*BoardHistory `json:"results"`
|
||||
}
|
||||
|
||||
// BlocksComplianceHistoryResponse is the response body to a request for blocks history.
|
||||
// swagger:model
|
||||
type BlocksComplianceHistoryResponse struct {
|
||||
// True if there is a next page for pagination
|
||||
// required: true
|
||||
HasNext bool `json:"hasNext"`
|
||||
|
||||
// The array of BlockHistory records.
|
||||
// required: true
|
||||
Results []*BlockHistory `json:"results"`
|
||||
}
|
||||
|
||||
// BoardHistory provides information about the history of a board.
|
||||
// swagger:model
|
||||
type BoardHistory struct {
|
||||
ID string `json:"id"`
|
||||
TeamID string `json:"teamId"`
|
||||
IsDeleted bool `json:"isDeleted"`
|
||||
DescendantLastUpdateAt int64 `json:"descendantLastUpdateAt"`
|
||||
DescendantFirstUpdateAt int64 `json:"descendantFirstUpdateAt"`
|
||||
CreatedBy string `json:"createdBy"`
|
||||
LastModifiedBy string `json:"lastModifiedBy"`
|
||||
}
|
||||
|
||||
// BlockHistory provides information about the history of a block.
|
||||
// swagger:model
|
||||
type BlockHistory struct {
|
||||
ID string `json:"id"`
|
||||
TeamID string `json:"teamId"`
|
||||
BoardID string `json:"boardId"`
|
||||
Type string `json:"type"`
|
||||
IsDeleted bool `json:"isDeleted"`
|
||||
LastUpdateAt int64 `json:"lastUpdateAt"`
|
||||
FirstUpdateAt int64 `json:"firstUpdateAt"`
|
||||
CreatedBy string `json:"createdBy"`
|
||||
LastModifiedBy string `json:"lastModifiedBy"`
|
||||
}
|
||||
|
||||
type QueryBoardsForComplianceOptions struct {
|
||||
TeamID string // if not empty then filter for specific team, otherwise all teams are included
|
||||
Page int // page number to select when paginating
|
||||
PerPage int // number of blocks per page (default=60)
|
||||
}
|
||||
|
||||
type QueryBoardsComplianceHistoryOptions struct {
|
||||
ModifiedSince int64 // if non-zero then filter for records with update_at greater than ModifiedSince
|
||||
IncludeDeleted bool // if true then deleted blocks are included
|
||||
TeamID string // if not empty then filter for specific team, otherwise all teams are included
|
||||
Page int // page number to select when paginating
|
||||
PerPage int // number of blocks per page (default=60)
|
||||
}
|
||||
|
||||
type QueryBlocksComplianceHistoryOptions struct {
|
||||
ModifiedSince int64 // if non-zero then filter for records with update_at greater than ModifiedSince
|
||||
IncludeDeleted bool // if true then deleted blocks are included
|
||||
TeamID string // if not empty then filter for specific team, otherwise all teams are included
|
||||
BoardID string // if not empty then filter for specific board, otherwise all boards are included
|
||||
Page int // page number to select when paginating
|
||||
PerPage int // number of blocks per page (default=60)
|
||||
}
|
@ -199,6 +199,21 @@ func (mr *MockServicesAPIMockRecorder) GetDirectChannel(arg0, arg1 interface{})
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDirectChannel", reflect.TypeOf((*MockServicesAPI)(nil).GetDirectChannel), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetDirectChannelOrCreate mocks base method.
|
||||
func (m *MockServicesAPI) GetDirectChannelOrCreate(arg0, arg1 string) (*model.Channel, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetDirectChannelOrCreate", arg0, arg1)
|
||||
ret0, _ := ret[0].(*model.Channel)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetDirectChannelOrCreate indicates an expected call of GetDirectChannelOrCreate.
|
||||
func (mr *MockServicesAPIMockRecorder) GetDirectChannelOrCreate(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDirectChannelOrCreate", reflect.TypeOf((*MockServicesAPI)(nil).GetDirectChannelOrCreate), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetFileInfo mocks base method.
|
||||
func (m *MockServicesAPI) GetFileInfo(arg0 string) (*model.FileInfo, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
50
server/model/mocks/propValueResolverMock.go
Normal file
50
server/model/mocks/propValueResolverMock.go
Normal file
@ -0,0 +1,50 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/mattermost/focalboard/server/model (interfaces: PropValueResolver)
|
||||
|
||||
// Package mocks is a generated GoMock package.
|
||||
package mocks
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
model "github.com/mattermost/focalboard/server/model"
|
||||
)
|
||||
|
||||
// MockPropValueResolver is a mock of PropValueResolver interface.
|
||||
type MockPropValueResolver struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockPropValueResolverMockRecorder
|
||||
}
|
||||
|
||||
// MockPropValueResolverMockRecorder is the mock recorder for MockPropValueResolver.
|
||||
type MockPropValueResolverMockRecorder struct {
|
||||
mock *MockPropValueResolver
|
||||
}
|
||||
|
||||
// NewMockPropValueResolver creates a new mock instance.
|
||||
func NewMockPropValueResolver(ctrl *gomock.Controller) *MockPropValueResolver {
|
||||
mock := &MockPropValueResolver{ctrl: ctrl}
|
||||
mock.recorder = &MockPropValueResolverMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockPropValueResolver) EXPECT() *MockPropValueResolverMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// GetUserByID mocks base method.
|
||||
func (m *MockPropValueResolver) GetUserByID(arg0 string) (*model.User, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetUserByID", arg0)
|
||||
ret0, _ := ret[0].(*model.User)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetUserByID indicates an expected call of GetUserByID.
|
||||
func (mr *MockPropValueResolverMockRecorder) GetUserByID(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserByID", reflect.TypeOf((*MockPropValueResolver)(nil).GetUserByID), arg0)
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
//go:generate mockgen -destination=mocks/propValueResolverMock.go -package mocks . PropValueResolver
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
@ -98,6 +100,32 @@ func (pd PropDef) GetValue(v interface{}, resolver PropValueResolver) (string, e
|
||||
}
|
||||
return userID, nil
|
||||
|
||||
case "multiPerson":
|
||||
// v is a slice of user IDs
|
||||
userIDs, ok := v.([]interface{})
|
||||
if !ok {
|
||||
return "", fmt.Errorf("multiPerson property type: %w", ErrInvalidPropertyValueType)
|
||||
}
|
||||
if resolver != nil {
|
||||
usernames := make([]string, len(userIDs))
|
||||
|
||||
for i, userIDInterface := range userIDs {
|
||||
userID := userIDInterface.(string)
|
||||
|
||||
user, err := resolver.GetUserByID(userID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if user == nil {
|
||||
usernames[i] = userID
|
||||
} else {
|
||||
usernames[i] = user.Username
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(usernames, ", "), nil
|
||||
}
|
||||
|
||||
case "multiSelect":
|
||||
// v is a slice of strings containing option ids
|
||||
ms, ok := v.([]interface{})
|
||||
|
@ -12,6 +12,24 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type MockResolver struct{}
|
||||
|
||||
func (r MockResolver) GetUserByID(userID string) (*User, error) {
|
||||
if userID == "user_id_1" {
|
||||
return &User{
|
||||
ID: "user_id_1",
|
||||
Username: "username_1",
|
||||
}, nil
|
||||
} else if userID == "user_id_2" {
|
||||
return &User{
|
||||
ID: "user_id_2",
|
||||
Username: "username_2",
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func Test_parsePropertySchema(t *testing.T) {
|
||||
board := &Board{
|
||||
ID: utils.NewID(utils.IDTypeBoard),
|
||||
@ -44,6 +62,33 @@ func Test_parsePropertySchema(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_GetValue(t *testing.T) {
|
||||
resolver := MockResolver{}
|
||||
|
||||
propDef := PropDef{
|
||||
Type: "multiPerson",
|
||||
}
|
||||
|
||||
value, err := propDef.GetValue([]interface{}{"user_id_1", "user_id_2"}, resolver)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "username_1, username_2", value)
|
||||
|
||||
// trying with only user
|
||||
value, err = propDef.GetValue([]interface{}{"user_id_1"}, resolver)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "username_1", value)
|
||||
|
||||
// trying with unknown user
|
||||
value, err = propDef.GetValue([]interface{}{"user_id_1", "user_id_unknown"}, resolver)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "username_1, user_id_unknown", value)
|
||||
|
||||
// trying with multiple unknown users
|
||||
value, err = propDef.GetValue([]interface{}{"michael_scott", "jim_halpert"}, resolver)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "michael_scott, jim_halpert", value)
|
||||
}
|
||||
|
||||
const (
|
||||
cardPropertiesExample = `[
|
||||
{
|
||||
|
@ -30,6 +30,7 @@ var FocalboardBot = &mm_model.Bot{
|
||||
type ServicesAPI interface {
|
||||
// Channels service
|
||||
GetDirectChannel(userID1, userID2 string) (*mm_model.Channel, error)
|
||||
GetDirectChannelOrCreate(userID1, userID2 string) (*mm_model.Channel, error)
|
||||
GetChannelByID(channelID string) (*mm_model.Channel, error)
|
||||
GetChannelMember(channelID string, userID string) (*mm_model.ChannelMember, error)
|
||||
GetChannelsForTeamForUser(teamID string, userID string, includeDeleted bool) (mm_model.ChannelList, error)
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
// It should be maintained in chronological order with most current
|
||||
// release at the front of the list.
|
||||
var versions = []string{
|
||||
"7.9.0",
|
||||
"7.8.0",
|
||||
"7.7.0",
|
||||
"7.6.0",
|
||||
@ -53,7 +54,7 @@ var (
|
||||
|
||||
// LogServerInfo logs information about the server instance.
|
||||
func LogServerInfo(logger mlog.LoggerIFace) {
|
||||
logger.Info("FocalBoard Server",
|
||||
logger.Info("Focalboard server",
|
||||
mlog.String("version", CurrentVersion),
|
||||
mlog.String("edition", Edition),
|
||||
mlog.String("build_number", BuildNumber),
|
||||
|
@ -377,7 +377,7 @@ func (s *Server) UpdateAppConfig() {
|
||||
// Local server
|
||||
|
||||
func (s *Server) startLocalModeServer() error {
|
||||
s.localModeServer = &http.Server{
|
||||
s.localModeServer = &http.Server{ //nolint:gosec
|
||||
Handler: s.localRouter,
|
||||
ConnContext: api.SetContextConn,
|
||||
}
|
||||
|
@ -46,12 +46,14 @@ func NewAudit(options ...mlog.Option) (*Audit, error) {
|
||||
|
||||
// Configure provides a new configuration for this audit service.
|
||||
// Zero or more sources of config can be provided:
|
||||
// cfgFile - path to file containing JSON
|
||||
// cfgEscaped - JSON string probably from ENV var
|
||||
//
|
||||
// cfgFile - path to file containing JSON
|
||||
// cfgEscaped - JSON string probably from ENV var
|
||||
//
|
||||
// For each case JSON containing log targets is provided. Target name collisions are resolved
|
||||
// using the following precedence:
|
||||
// cfgFile > cfgEscaped
|
||||
//
|
||||
// cfgFile > cfgEscaped
|
||||
func (a *Audit) Configure(cfgFile string, cfgEscaped string) error {
|
||||
return a.auditLogger.Configure(cfgFile, cfgEscaped, nil)
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ type Service struct {
|
||||
// NewMetricsServer factory method to create a new prometheus server.
|
||||
func NewMetricsServer(address string, metricsService *Metrics, logger mlog.LoggerIFace) *Service {
|
||||
return &Service{
|
||||
&http.Server{
|
||||
&http.Server{ //nolint:gosec
|
||||
Addr: address,
|
||||
Handler: promhttp.HandlerFor(metricsService.registry, promhttp.HandlerOpts{
|
||||
ErrorLog: logger.StdLogger(mlog.LvlError),
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
|
||||
type AppAPI interface {
|
||||
GetBlockHistory(blockID string, opts model.QueryBlockHistoryOptions) ([]*model.Block, error)
|
||||
GetSubTree2(boardID, blockID string, opts model.QuerySubtreeOptions) ([]*model.Block, error)
|
||||
GetBlockHistoryNewestChildren(parentID string, opts model.QueryBlockHistoryChildOptions) ([]*model.Block, bool, error)
|
||||
GetBoardAndCardByID(blockID string) (board *model.Board, card *model.Block, err error)
|
||||
|
||||
GetUserByID(userID string) (*model.User, error)
|
||||
|
@ -149,10 +149,10 @@ func (dg *diffGenerator) generateDiffsForCard(card *model.Block, schema model.Pr
|
||||
}
|
||||
|
||||
// fetch all card content blocks that were updated after last notify
|
||||
opts := model.QuerySubtreeOptions{
|
||||
opts := model.QueryBlockHistoryChildOptions{
|
||||
AfterUpdateAt: dg.lastNotifyAt,
|
||||
}
|
||||
blocks, err := dg.store.GetSubTree2(card.BoardID, card.ID, opts)
|
||||
blocks, _, err := dg.store.GetBlockHistoryNewestChildren(card.ID, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get subtree for card %s: %w", card.ID, err)
|
||||
}
|
||||
|
@ -246,7 +246,7 @@ func appendCommentChanges(fields []*mm_model.SlackAttachmentField, cardDiff *Dif
|
||||
msg = child.NewBlock.Title
|
||||
}
|
||||
|
||||
if child.NewBlock == nil && child.OldBlock != nil {
|
||||
if (child.NewBlock == nil || child.NewBlock.DeleteAt != 0) && child.OldBlock != nil {
|
||||
// deleted comment
|
||||
format = "~~`%s`~~"
|
||||
msg = stripNewlines(child.OldBlock.Title)
|
||||
@ -266,36 +266,73 @@ func appendCommentChanges(fields []*mm_model.SlackAttachmentField, cardDiff *Dif
|
||||
|
||||
func appendContentChanges(fields []*mm_model.SlackAttachmentField, cardDiff *Diff, logger mlog.LoggerIFace) []*mm_model.SlackAttachmentField {
|
||||
for _, child := range cardDiff.Diffs {
|
||||
if child.BlockType != model.TypeComment {
|
||||
var newTitle, oldTitle string
|
||||
if child.OldBlock != nil {
|
||||
oldTitle = child.OldBlock.Title
|
||||
}
|
||||
if child.NewBlock != nil {
|
||||
newTitle = child.NewBlock.Title
|
||||
}
|
||||
var opAdd, opDelete bool
|
||||
var opString string
|
||||
|
||||
// only strip newlines when modifying or deleting
|
||||
if child.OldBlock != nil && child.NewBlock == nil {
|
||||
newTitle = stripNewlines(newTitle)
|
||||
switch {
|
||||
case child.OldBlock == nil && child.NewBlock != nil:
|
||||
opAdd = true
|
||||
opString = "added" // TODO: localize when i18n added to server
|
||||
case child.NewBlock == nil || child.NewBlock.DeleteAt != 0:
|
||||
opDelete = true
|
||||
opString = "deleted"
|
||||
default:
|
||||
opString = "modified"
|
||||
}
|
||||
|
||||
var newTitle, oldTitle string
|
||||
if child.OldBlock != nil {
|
||||
oldTitle = child.OldBlock.Title
|
||||
}
|
||||
if child.NewBlock != nil {
|
||||
newTitle = child.NewBlock.Title
|
||||
}
|
||||
|
||||
switch child.BlockType {
|
||||
case model.TypeDivider, model.TypeComment:
|
||||
// do nothing
|
||||
continue
|
||||
case model.TypeImage:
|
||||
if newTitle == "" {
|
||||
newTitle = "An image was " + opString + "." // TODO: localize when i18n added to server
|
||||
}
|
||||
oldTitle = ""
|
||||
case model.TypeAttachment:
|
||||
if newTitle == "" {
|
||||
newTitle = "A file attachment was " + opString + "." // TODO: localize when i18n added to server
|
||||
}
|
||||
oldTitle = ""
|
||||
default:
|
||||
if !opAdd {
|
||||
if opDelete {
|
||||
newTitle = ""
|
||||
}
|
||||
// only strip newlines when modifying or deleting
|
||||
oldTitle = stripNewlines(oldTitle)
|
||||
newTitle = stripNewlines(newTitle)
|
||||
}
|
||||
|
||||
if newTitle == oldTitle {
|
||||
continue
|
||||
}
|
||||
|
||||
markdown := generateMarkdownDiff(oldTitle, newTitle, logger)
|
||||
if markdown == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
fields = append(fields, &mm_model.SlackAttachmentField{
|
||||
Short: false,
|
||||
Title: "Description",
|
||||
Value: markdown,
|
||||
})
|
||||
}
|
||||
|
||||
logger.Debug("appendContentChanges",
|
||||
mlog.String("type", string(child.BlockType)),
|
||||
mlog.String("opString", opString),
|
||||
mlog.String("oldTitle", oldTitle),
|
||||
mlog.String("newTitle", newTitle),
|
||||
)
|
||||
|
||||
markdown := generateMarkdownDiff(oldTitle, newTitle, logger)
|
||||
if markdown == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
fields = append(fields, &mm_model.SlackAttachmentField{
|
||||
Short: false,
|
||||
Title: "Description",
|
||||
Value: markdown,
|
||||
})
|
||||
}
|
||||
return fields
|
||||
}
|
||||
|
@ -200,7 +200,7 @@ func (n *notifier) notifySubscribers(hint *model.NotificationHint) error {
|
||||
}
|
||||
|
||||
opts := DiffConvOpts{
|
||||
Language: "en", // TODO: use correct language with i18n available on server.
|
||||
Language: "en", // TODO: use correct language when i18n is available on server.
|
||||
MakeCardLink: func(block *model.Block, board *model.Board, card *model.Block) string {
|
||||
return fmt.Sprintf("[%s](%s)", block.Title, utils.MakeCardLink(n.serverRoot, board.TeamID, board.ID, card.ID))
|
||||
},
|
||||
|
@ -5,6 +5,8 @@ package notifysubscriptions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/focalboard/server/model"
|
||||
@ -73,6 +75,16 @@ func (b *Backend) Name() string {
|
||||
}
|
||||
|
||||
func (b *Backend) getBlockUpdateFreq(blockType model.BlockType) time.Duration {
|
||||
// check for env variable override
|
||||
sFreq := os.Getenv("MM_BOARDS_NOTIFY_FREQ_SECONDS")
|
||||
if sFreq != "" && sFreq != "0" {
|
||||
if freq, err := strconv.ParseInt(sFreq, 10, 64); err != nil {
|
||||
b.logger.Error("Environment variable MM_BOARDS_NOTIFY_FREQ_SECONDS invalid (ignoring)", mlog.Err(err))
|
||||
} else {
|
||||
return time.Second * time.Duration(freq)
|
||||
}
|
||||
}
|
||||
|
||||
switch blockType {
|
||||
case model.TypeCard:
|
||||
return time.Second * time.Duration(b.notifyFreqCardSeconds)
|
||||
|
@ -8,9 +8,9 @@ import (
|
||||
)
|
||||
|
||||
type servicesAPI interface {
|
||||
// GetDirectChannel gets a direct message channel.
|
||||
// If the channel does not exist it will create it.
|
||||
GetDirectChannel(userID1, userID2 string) (*mm_model.Channel, error)
|
||||
// GetDirectChannelOrCreate gets a direct message channel,
|
||||
// or creates one if it does not already exist
|
||||
GetDirectChannelOrCreate(userID1, userID2 string) (*mm_model.Channel, error)
|
||||
|
||||
// CreatePost creates a post.
|
||||
CreatePost(post *mm_model.Post) (*mm_model.Post, error)
|
||||
|
@ -53,7 +53,7 @@ func (pd *PluginDelivery) getDirectChannelID(teamID string, subscriberID string,
|
||||
return "", fmt.Errorf("cannot find user: %w", err)
|
||||
}
|
||||
channel, err := pd.getDirectChannel(teamID, user.Id, botID)
|
||||
if err != nil {
|
||||
if err != nil || channel == nil {
|
||||
return "", fmt.Errorf("cannot get direct channel: %w", err)
|
||||
}
|
||||
return channel.Id, nil
|
||||
@ -70,5 +70,5 @@ func (pd *PluginDelivery) getDirectChannel(teamID string, userID string, botID s
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot add bot to team %s: %w", teamID, err)
|
||||
}
|
||||
return pd.api.GetDirectChannel(userID, botID)
|
||||
return pd.api.GetDirectChannelOrCreate(userID, botID)
|
||||
}
|
||||
|
@ -101,6 +101,10 @@ func (m servicesAPIMock) GetDirectChannel(userID1, userID2 string) (*mm_model.Ch
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m servicesAPIMock) GetDirectChannelOrCreate(userID1, userID2 string) (*mm_model.Channel, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m servicesAPIMock) CreatePost(post *mm_model.Post) (*mm_model.Post, error) {
|
||||
return post, nil
|
||||
}
|
||||
|
@ -80,8 +80,9 @@ type storeMetadata struct {
|
||||
}
|
||||
|
||||
var blacklistedStoreMethodNames = map[string]bool{
|
||||
"Shutdown": true,
|
||||
"DBType": true,
|
||||
"Shutdown": true,
|
||||
"DBType": true,
|
||||
"DBVersion": true,
|
||||
}
|
||||
|
||||
func extractMethodMetadata(method *ast.Field, src []byte) methodData {
|
||||
|
@ -599,7 +599,7 @@ func (s *MattermostAuthLayer) GetLicense() *mmModel.License {
|
||||
return s.servicesAPI.GetLicense()
|
||||
}
|
||||
|
||||
func boardFields(prefix string) []string {
|
||||
func boardFields(prefix string) []string { //nolint:unparam
|
||||
fields := []string{
|
||||
"id",
|
||||
"team_id",
|
||||
@ -1114,10 +1114,12 @@ func (s *MattermostAuthLayer) GetMembersForBoard(boardID string) ([]*model.Board
|
||||
From(s.tablePrefix + "boards AS B").
|
||||
Join("ChannelMembers AS CM ON B.channel_id=CM.channelId").
|
||||
Join("Users as U on CM.userID = U.id").
|
||||
LeftJoin("Bots as bo on U.id = bo.UserID").
|
||||
Where(sq.Eq{"B.id": boardID}).
|
||||
Where(sq.NotEq{"B.channel_id": ""}).
|
||||
// Filter out guests as they don't have synthetic membership
|
||||
Where(sq.NotEq{"U.roles": "system_guest"})
|
||||
Where(sq.NotEq{"U.roles": "system_guest"}).
|
||||
Where(sq.Eq{"bo.UserId IS NOT NULL": false})
|
||||
|
||||
rows, err := query.Query()
|
||||
if err != nil {
|
||||
|
@ -37,17 +37,17 @@ func (m *MockStore) EXPECT() *MockStoreMockRecorder {
|
||||
}
|
||||
|
||||
// AddUpdateCategoryBoard mocks base method.
|
||||
func (m *MockStore) AddUpdateCategoryBoard(arg0 string, arg1 map[string]string) error {
|
||||
func (m *MockStore) AddUpdateCategoryBoard(arg0, arg1 string, arg2 []string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "AddUpdateCategoryBoard", arg0, arg1)
|
||||
ret := m.ctrl.Call(m, "AddUpdateCategoryBoard", arg0, arg1, arg2)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// AddUpdateCategoryBoard indicates an expected call of AddUpdateCategoryBoard.
|
||||
func (mr *MockStoreMockRecorder) AddUpdateCategoryBoard(arg0, arg1 interface{}) *gomock.Call {
|
||||
func (mr *MockStoreMockRecorder) AddUpdateCategoryBoard(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddUpdateCategoryBoard", reflect.TypeOf((*MockStore)(nil).AddUpdateCategoryBoard), arg0, arg1)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddUpdateCategoryBoard", reflect.TypeOf((*MockStore)(nil).AddUpdateCategoryBoard), arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// CanSeeUser mocks base method.
|
||||
@ -457,6 +457,22 @@ func (mr *MockStoreMockRecorder) GetBlockHistoryDescendants(arg0, arg1 interface
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlockHistoryDescendants", reflect.TypeOf((*MockStore)(nil).GetBlockHistoryDescendants), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetBlockHistoryNewestChildren mocks base method.
|
||||
func (m *MockStore) GetBlockHistoryNewestChildren(arg0 string, arg1 model.QueryBlockHistoryChildOptions) ([]*model.Block, bool, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetBlockHistoryNewestChildren", arg0, arg1)
|
||||
ret0, _ := ret[0].([]*model.Block)
|
||||
ret1, _ := ret[1].(bool)
|
||||
ret2, _ := ret[2].(error)
|
||||
return ret0, ret1, ret2
|
||||
}
|
||||
|
||||
// GetBlockHistoryNewestChildren indicates an expected call of GetBlockHistoryNewestChildren.
|
||||
func (mr *MockStoreMockRecorder) GetBlockHistoryNewestChildren(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlockHistoryNewestChildren", reflect.TypeOf((*MockStore)(nil).GetBlockHistoryNewestChildren), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetBlocks mocks base method.
|
||||
func (m *MockStore) GetBlocks(arg0 model.QueryBlocksOptions) ([]*model.Block, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@ -487,6 +503,22 @@ func (mr *MockStoreMockRecorder) GetBlocksByIDs(arg0 interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlocksByIDs", reflect.TypeOf((*MockStore)(nil).GetBlocksByIDs), arg0)
|
||||
}
|
||||
|
||||
// GetBlocksComplianceHistory mocks base method.
|
||||
func (m *MockStore) GetBlocksComplianceHistory(arg0 model.QueryBlocksComplianceHistoryOptions) ([]*model.BlockHistory, bool, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetBlocksComplianceHistory", arg0)
|
||||
ret0, _ := ret[0].([]*model.BlockHistory)
|
||||
ret1, _ := ret[1].(bool)
|
||||
ret2, _ := ret[2].(error)
|
||||
return ret0, ret1, ret2
|
||||
}
|
||||
|
||||
// GetBlocksComplianceHistory indicates an expected call of GetBlocksComplianceHistory.
|
||||
func (mr *MockStoreMockRecorder) GetBlocksComplianceHistory(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlocksComplianceHistory", reflect.TypeOf((*MockStore)(nil).GetBlocksComplianceHistory), arg0)
|
||||
}
|
||||
|
||||
// GetBlocksForBoard mocks base method.
|
||||
func (m *MockStore) GetBlocksForBoard(arg0 string) ([]*model.Block, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@ -639,6 +671,38 @@ func (mr *MockStoreMockRecorder) GetBoardMemberHistory(arg0, arg1, arg2 interfac
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBoardMemberHistory", reflect.TypeOf((*MockStore)(nil).GetBoardMemberHistory), arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// GetBoardsComplianceHistory mocks base method.
|
||||
func (m *MockStore) GetBoardsComplianceHistory(arg0 model.QueryBoardsComplianceHistoryOptions) ([]*model.BoardHistory, bool, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetBoardsComplianceHistory", arg0)
|
||||
ret0, _ := ret[0].([]*model.BoardHistory)
|
||||
ret1, _ := ret[1].(bool)
|
||||
ret2, _ := ret[2].(error)
|
||||
return ret0, ret1, ret2
|
||||
}
|
||||
|
||||
// GetBoardsComplianceHistory indicates an expected call of GetBoardsComplianceHistory.
|
||||
func (mr *MockStoreMockRecorder) GetBoardsComplianceHistory(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBoardsComplianceHistory", reflect.TypeOf((*MockStore)(nil).GetBoardsComplianceHistory), arg0)
|
||||
}
|
||||
|
||||
// GetBoardsForCompliance mocks base method.
|
||||
func (m *MockStore) GetBoardsForCompliance(arg0 model.QueryBoardsForComplianceOptions) ([]*model.Board, bool, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetBoardsForCompliance", arg0)
|
||||
ret0, _ := ret[0].([]*model.Board)
|
||||
ret1, _ := ret[1].(bool)
|
||||
ret2, _ := ret[2].(error)
|
||||
return ret0, ret1, ret2
|
||||
}
|
||||
|
||||
// GetBoardsForCompliance indicates an expected call of GetBoardsForCompliance.
|
||||
func (mr *MockStoreMockRecorder) GetBoardsForCompliance(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBoardsForCompliance", reflect.TypeOf((*MockStore)(nil).GetBoardsForCompliance), arg0)
|
||||
}
|
||||
|
||||
// GetBoardsForUserAndTeam mocks base method.
|
||||
func (m *MockStore) GetBoardsForUserAndTeam(arg0, arg1 string, arg2 bool) ([]*model.Board, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@ -999,18 +1063,18 @@ func (mr *MockStoreMockRecorder) GetTeam(arg0 interface{}) *gomock.Call {
|
||||
}
|
||||
|
||||
// GetTeamBoardsInsights mocks base method.
|
||||
func (m *MockStore) GetTeamBoardsInsights(arg0, arg1 string, arg2 int64, arg3, arg4 int, arg5 []string) (*model.BoardInsightsList, error) {
|
||||
func (m *MockStore) GetTeamBoardsInsights(arg0 string, arg1 int64, arg2, arg3 int, arg4 []string) (*model.BoardInsightsList, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetTeamBoardsInsights", arg0, arg1, arg2, arg3, arg4, arg5)
|
||||
ret := m.ctrl.Call(m, "GetTeamBoardsInsights", arg0, arg1, arg2, arg3, arg4)
|
||||
ret0, _ := ret[0].(*model.BoardInsightsList)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetTeamBoardsInsights indicates an expected call of GetTeamBoardsInsights.
|
||||
func (mr *MockStoreMockRecorder) GetTeamBoardsInsights(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call {
|
||||
func (mr *MockStoreMockRecorder) GetTeamBoardsInsights(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTeamBoardsInsights", reflect.TypeOf((*MockStore)(nil).GetTeamBoardsInsights), arg0, arg1, arg2, arg3, arg4, arg5)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTeamBoardsInsights", reflect.TypeOf((*MockStore)(nil).GetTeamBoardsInsights), arg0, arg1, arg2, arg3, arg4)
|
||||
}
|
||||
|
||||
// GetTeamCount mocks base method.
|
||||
@ -1545,6 +1609,20 @@ func (mr *MockStoreMockRecorder) SendMessage(arg0, arg1, arg2 interface{}) *gomo
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendMessage", reflect.TypeOf((*MockStore)(nil).SendMessage), arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// SetBoardVisibility mocks base method.
|
||||
func (m *MockStore) SetBoardVisibility(arg0, arg1, arg2 string, arg3 bool) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SetBoardVisibility", arg0, arg1, arg2, arg3)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// SetBoardVisibility indicates an expected call of SetBoardVisibility.
|
||||
func (mr *MockStoreMockRecorder) SetBoardVisibility(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetBoardVisibility", reflect.TypeOf((*MockStore)(nil).SetBoardVisibility), arg0, arg1, arg2, arg3)
|
||||
}
|
||||
|
||||
// SetSystemSetting mocks base method.
|
||||
func (m *MockStore) SetSystemSetting(arg0, arg1 string) error {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/mattermost/focalboard/server/utils"
|
||||
|
||||
@ -42,27 +43,31 @@ func (s *SQLStore) timestampToCharField(name string, as string) string {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SQLStore) blockFields() []string {
|
||||
func (s *SQLStore) blockFields(tableAlias string) []string {
|
||||
if tableAlias != "" && !strings.HasSuffix(tableAlias, ".") {
|
||||
tableAlias += "."
|
||||
}
|
||||
|
||||
return []string{
|
||||
"id",
|
||||
"parent_id",
|
||||
"created_by",
|
||||
"modified_by",
|
||||
s.escapeField("schema"),
|
||||
"type",
|
||||
"title",
|
||||
"COALESCE(fields, '{}')",
|
||||
s.timestampToCharField("insert_at", "insertAt"),
|
||||
"create_at",
|
||||
"update_at",
|
||||
"delete_at",
|
||||
"COALESCE(board_id, '0')",
|
||||
tableAlias + "id",
|
||||
tableAlias + "parent_id",
|
||||
tableAlias + "created_by",
|
||||
tableAlias + "modified_by",
|
||||
tableAlias + s.escapeField("schema"),
|
||||
tableAlias + "type",
|
||||
tableAlias + "title",
|
||||
"COALESCE(" + tableAlias + "fields, '{}')",
|
||||
s.timestampToCharField(tableAlias+"insert_at", "insertAt"),
|
||||
tableAlias + "create_at",
|
||||
tableAlias + "update_at",
|
||||
tableAlias + "delete_at",
|
||||
"COALESCE(" + tableAlias + "board_id, '0')",
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SQLStore) getBlocks(db sq.BaseRunner, opts model.QueryBlocksOptions) ([]*model.Block, error) {
|
||||
query := s.getQueryBuilder(db).
|
||||
Select(s.blockFields()...).
|
||||
Select(s.blockFields("")...).
|
||||
From(s.tablePrefix + "blocks")
|
||||
|
||||
if opts.BoardID != "" {
|
||||
@ -115,7 +120,7 @@ func (s *SQLStore) getBlocksWithParent(db sq.BaseRunner, boardID, parentID strin
|
||||
|
||||
func (s *SQLStore) getBlocksByIDs(db sq.BaseRunner, ids []string) ([]*model.Block, error) {
|
||||
query := s.getQueryBuilder(db).
|
||||
Select(s.blockFields()...).
|
||||
Select(s.blockFields("")...).
|
||||
From(s.tablePrefix + "blocks").
|
||||
Where(sq.Eq{"id": ids})
|
||||
|
||||
@ -150,7 +155,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) {
|
||||
query := s.getQueryBuilder(db).
|
||||
Select(s.blockFields()...).
|
||||
Select(s.blockFields("")...).
|
||||
From(s.tablePrefix + "blocks").
|
||||
Where(sq.Or{sq.Eq{"id": blockID}, sq.Eq{"parent_id": blockID}}).
|
||||
Where(sq.Eq{"board_id": boardID}).
|
||||
@ -550,7 +555,7 @@ func (s *SQLStore) getBoardCount(db sq.BaseRunner) (int64, error) {
|
||||
|
||||
func (s *SQLStore) getBlock(db sq.BaseRunner, blockID string) (*model.Block, error) {
|
||||
query := s.getQueryBuilder(db).
|
||||
Select(s.blockFields()...).
|
||||
Select(s.blockFields("")...).
|
||||
From(s.tablePrefix + "blocks").
|
||||
Where(sq.Eq{"id": blockID})
|
||||
|
||||
@ -580,7 +585,7 @@ func (s *SQLStore) getBlockHistory(db sq.BaseRunner, blockID string, opts model.
|
||||
}
|
||||
|
||||
query := s.getQueryBuilder(db).
|
||||
Select(s.blockFields()...).
|
||||
Select(s.blockFields("")...).
|
||||
From(s.tablePrefix + "blocks_history").
|
||||
Where(sq.Eq{"id": blockID}).
|
||||
OrderBy("insert_at " + order + ", update_at" + order)
|
||||
@ -614,7 +619,7 @@ func (s *SQLStore) getBlockHistoryDescendants(db sq.BaseRunner, boardID string,
|
||||
}
|
||||
|
||||
query := s.getQueryBuilder(db).
|
||||
Select(s.blockFields()...).
|
||||
Select(s.blockFields("")...).
|
||||
From(s.tablePrefix + "blocks_history").
|
||||
Where(sq.Eq{"board_id": boardID}).
|
||||
OrderBy("insert_at " + order + ", update_at" + order)
|
||||
@ -641,6 +646,83 @@ func (s *SQLStore) getBlockHistoryDescendants(db sq.BaseRunner, boardID string,
|
||||
return s.blocksFromRows(rows)
|
||||
}
|
||||
|
||||
// getBlockHistoryNewestChildren returns the newest (latest) version child blocks for the
|
||||
// specified parent from the blocks_history table. This includes any deleted children.
|
||||
func (s *SQLStore) getBlockHistoryNewestChildren(db sq.BaseRunner, parentID string, opts model.QueryBlockHistoryChildOptions) ([]*model.Block, bool, error) {
|
||||
// as we're joining 2 queries, we need to avoid numbered
|
||||
// placeholders until the join is done, so we use the default
|
||||
// question mark placeholder here
|
||||
builder := s.getQueryBuilder(db).PlaceholderFormat(sq.Question)
|
||||
|
||||
sub := builder.
|
||||
Select("bh2.id", "MAX(bh2.insert_at) AS max_insert_at").
|
||||
From(s.tablePrefix + "blocks_history AS bh2").
|
||||
Where(sq.Eq{"bh2.parent_id": parentID}).
|
||||
GroupBy("bh2.id")
|
||||
|
||||
if opts.AfterUpdateAt != 0 {
|
||||
sub = sub.Where(sq.Gt{"bh2.update_at": opts.AfterUpdateAt})
|
||||
}
|
||||
|
||||
if opts.BeforeUpdateAt != 0 {
|
||||
sub = sub.Where(sq.Lt{"bh2.update_at": opts.BeforeUpdateAt})
|
||||
}
|
||||
|
||||
subQuery, subArgs, err := sub.ToSql()
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("getBlockHistoryNewestChildren unable to generate subquery: %w", err)
|
||||
}
|
||||
|
||||
query := s.getQueryBuilder(db).
|
||||
Select(s.blockFields("bh")...).
|
||||
From(s.tablePrefix+"blocks_history AS bh").
|
||||
InnerJoin("("+subQuery+") AS sub ON bh.id=sub.id AND bh.insert_at=sub.max_insert_at", subArgs...)
|
||||
|
||||
if opts.Page != 0 {
|
||||
query = query.Offset(uint64(opts.Page * opts.PerPage))
|
||||
}
|
||||
|
||||
if opts.PerPage > 0 {
|
||||
// limit+1 to detect if more records available
|
||||
query = query.Limit(uint64(opts.PerPage + 1))
|
||||
}
|
||||
|
||||
sql, args, err := query.ToSql()
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("getBlockHistoryNewestChildren unable to generate sql: %w", err)
|
||||
}
|
||||
|
||||
// if we're using postgres or sqlite, we need to replace the
|
||||
// question mark placeholder with the numbered dollar one, now
|
||||
// that the full query is built
|
||||
if s.dbType == model.PostgresDBType || s.dbType == model.SqliteDBType {
|
||||
var rErr error
|
||||
sql, rErr = sq.Dollar.ReplacePlaceholders(sql)
|
||||
if rErr != nil {
|
||||
return nil, false, fmt.Errorf("getBlockHistoryNewestChildren unable to replace sql placeholders: %w", rErr)
|
||||
}
|
||||
}
|
||||
|
||||
rows, err := db.Query(sql, args...)
|
||||
if err != nil {
|
||||
s.logger.Error(`getBlockHistoryNewestChildren ERROR`, mlog.Err(err))
|
||||
return nil, false, err
|
||||
}
|
||||
defer s.CloseRows(rows)
|
||||
|
||||
blocks, err := s.blocksFromRows(rows)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
hasMore := false
|
||||
if opts.PerPage > 0 && len(blocks) > opts.PerPage {
|
||||
blocks = blocks[:opts.PerPage]
|
||||
hasMore = true
|
||||
}
|
||||
return blocks, hasMore, nil
|
||||
}
|
||||
|
||||
// getBoardAndCardByID returns the first parent of type `card` and first parent of type `board` for the block specified by ID.
|
||||
// `board` and/or `card` may return nil without error if the block does not belong to a board or card.
|
||||
func (s *SQLStore) getBoardAndCardByID(db sq.BaseRunner, blockID string) (board *model.Board, card *model.Block, err error) {
|
||||
|
@ -17,41 +17,31 @@ import (
|
||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||
)
|
||||
|
||||
func boardFields(prefix string) []string {
|
||||
fields := []string{
|
||||
"id",
|
||||
"team_id",
|
||||
"COALESCE(channel_id, '')",
|
||||
"COALESCE(created_by, '')",
|
||||
"modified_by",
|
||||
"type",
|
||||
"minimum_role",
|
||||
"title",
|
||||
"description",
|
||||
"icon",
|
||||
"show_description",
|
||||
"is_template",
|
||||
"template_version",
|
||||
"COALESCE(properties, '{}')",
|
||||
"COALESCE(card_properties, '[]')",
|
||||
"create_at",
|
||||
"update_at",
|
||||
"delete_at",
|
||||
func boardFields(tableAlias string) []string {
|
||||
if tableAlias != "" && !strings.HasSuffix(tableAlias, ".") {
|
||||
tableAlias += "."
|
||||
}
|
||||
|
||||
if prefix == "" {
|
||||
return fields
|
||||
return []string{
|
||||
tableAlias + "id",
|
||||
tableAlias + "team_id",
|
||||
"COALESCE(" + tableAlias + "channel_id, '')",
|
||||
"COALESCE(" + tableAlias + "created_by, '')",
|
||||
tableAlias + "modified_by",
|
||||
tableAlias + "type",
|
||||
tableAlias + "minimum_role",
|
||||
tableAlias + "title",
|
||||
tableAlias + "description",
|
||||
tableAlias + "icon",
|
||||
tableAlias + "show_description",
|
||||
tableAlias + "is_template",
|
||||
tableAlias + "template_version",
|
||||
"COALESCE(" + tableAlias + "properties, '{}')",
|
||||
"COALESCE(" + tableAlias + "card_properties, '[]')",
|
||||
tableAlias + "create_at",
|
||||
tableAlias + "update_at",
|
||||
tableAlias + "delete_at",
|
||||
}
|
||||
|
||||
prefixedFields := make([]string, len(fields))
|
||||
for i, field := range fields {
|
||||
if strings.HasPrefix(field, "COALESCE(") {
|
||||
prefixedFields[i] = strings.Replace(field, "COALESCE(", "COALESCE("+prefix, 1)
|
||||
} else {
|
||||
prefixedFields[i] = prefix + field
|
||||
}
|
||||
}
|
||||
return prefixedFields
|
||||
}
|
||||
|
||||
func boardHistoryFields() []string {
|
||||
|
@ -14,7 +14,7 @@ import (
|
||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||
)
|
||||
|
||||
func (s *SQLStore) getTeamBoardsInsights(db sq.BaseRunner, teamID string, userID string, since int64, offset int, limit int, boardIDs []string) (*model.BoardInsightsList, error) {
|
||||
func (s *SQLStore) getTeamBoardsInsights(db sq.BaseRunner, teamID string, since int64, offset int, limit int, boardIDs []string) (*model.BoardInsightsList, error) {
|
||||
boardsHistoryQuery := s.getQueryBuilder(db).
|
||||
Select("boards.id, boards.icon, boards.title, count(boards_history.id) as count, boards_history.modified_by, boards.created_by").
|
||||
From(s.tablePrefix + "boards_history as boards_history").
|
||||
|
@ -19,14 +19,14 @@ func (s *SQLStore) getUserCategoryBoards(db sq.BaseRunner, userID, teamID string
|
||||
|
||||
userCategoryBoards := []model.CategoryBoards{}
|
||||
for _, category := range categories {
|
||||
boardIDs, err := s.getCategoryBoardAttributes(db, category.ID)
|
||||
boardMetadata, err := s.getCategoryBoardAttributes(db, category.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userCategoryBoard := model.CategoryBoards{
|
||||
Category: category,
|
||||
BoardIDs: boardIDs,
|
||||
Category: category,
|
||||
BoardMetadata: boardMetadata,
|
||||
}
|
||||
|
||||
userCategoryBoards = append(userCategoryBoards, userCategoryBoard)
|
||||
@ -35,13 +35,12 @@ func (s *SQLStore) getUserCategoryBoards(db sq.BaseRunner, userID, teamID string
|
||||
return userCategoryBoards, nil
|
||||
}
|
||||
|
||||
func (s *SQLStore) getCategoryBoardAttributes(db sq.BaseRunner, categoryID string) ([]string, error) {
|
||||
func (s *SQLStore) getCategoryBoardAttributes(db sq.BaseRunner, categoryID string) ([]model.CategoryBoardMetadata, error) {
|
||||
query := s.getQueryBuilder(db).
|
||||
Select("board_id").
|
||||
Select("board_id, COALESCE(hidden, false)").
|
||||
From(s.tablePrefix + "category_boards").
|
||||
Where(sq.Eq{
|
||||
"category_id": categoryID,
|
||||
"delete_at": 0,
|
||||
}).
|
||||
OrderBy("sort_order")
|
||||
|
||||
@ -54,21 +53,17 @@ func (s *SQLStore) getCategoryBoardAttributes(db sq.BaseRunner, categoryID strin
|
||||
return s.categoryBoardsFromRows(rows)
|
||||
}
|
||||
|
||||
func (s *SQLStore) addUpdateCategoryBoard(db sq.BaseRunner, userID string, boardCategoryMapping map[string]string) error {
|
||||
boardIDs := []string{}
|
||||
for boardID := range boardCategoryMapping {
|
||||
boardIDs = append(boardIDs, boardID)
|
||||
}
|
||||
func (s *SQLStore) addUpdateCategoryBoard(db sq.BaseRunner, userID, categoryID string, boardIDsParam []string) error {
|
||||
// we need to de-duplicate this array as Postgres failes to
|
||||
// handle upsert if there are multiple incoming rows
|
||||
// that conflict the same existing row.
|
||||
// For example, having the entry "1" in DB and trying to upsert "1" and "1" will fail
|
||||
// as there are multiple duplicates of the same "1".
|
||||
//
|
||||
// Source: https://stackoverflow.com/questions/42994373/postgresql-on-conflict-cannot-affect-row-a-second-time
|
||||
boardIDs := utils.DedupeStringArr(boardIDsParam)
|
||||
|
||||
if err := s.deleteUserCategoryBoards(db, userID, boardIDs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.addUserCategoryBoard(db, userID, boardCategoryMapping)
|
||||
}
|
||||
|
||||
func (s *SQLStore) addUserCategoryBoard(db sq.BaseRunner, userID string, boardCategoryMapping map[string]string) error {
|
||||
if len(boardCategoryMapping) == 0 {
|
||||
if len(boardIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -81,73 +76,62 @@ func (s *SQLStore) addUserCategoryBoard(db sq.BaseRunner, userID string, boardCa
|
||||
"board_id",
|
||||
"create_at",
|
||||
"update_at",
|
||||
"delete_at",
|
||||
"sort_order",
|
||||
"hidden",
|
||||
)
|
||||
|
||||
now := utils.GetMillis()
|
||||
for boardID, categoryID := range boardCategoryMapping {
|
||||
query = query.
|
||||
Values(
|
||||
utils.NewID(utils.IDTypeNone),
|
||||
userID,
|
||||
categoryID,
|
||||
boardID,
|
||||
now,
|
||||
now,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
for _, boardID := range boardIDs {
|
||||
query = query.Values(
|
||||
utils.NewID(utils.IDTypeNone),
|
||||
userID,
|
||||
categoryID,
|
||||
boardID,
|
||||
now,
|
||||
now,
|
||||
0,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
if s.dbType == model.MysqlDBType {
|
||||
query = query.Suffix(
|
||||
"ON DUPLICATE KEY UPDATE category_id = ?",
|
||||
categoryID,
|
||||
)
|
||||
} else {
|
||||
query = query.Suffix(
|
||||
`ON CONFLICT (user_id, board_id)
|
||||
DO UPDATE SET category_id = EXCLUDED.category_id, update_at = EXCLUDED.update_at`,
|
||||
)
|
||||
}
|
||||
|
||||
if _, err := query.Exec(); err != nil {
|
||||
s.logger.Error("addUserCategoryBoard error", mlog.Err(err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SQLStore) deleteUserCategoryBoards(db sq.BaseRunner, userID string, boardIDs []string) error {
|
||||
if len(boardIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err := s.getQueryBuilder(db).
|
||||
Update(s.tablePrefix+"category_boards").
|
||||
Set("delete_at", utils.GetMillis()).
|
||||
Where(sq.Eq{
|
||||
"user_id": userID,
|
||||
"board_id": boardIDs,
|
||||
"delete_at": 0,
|
||||
}).Exec()
|
||||
|
||||
if err != nil {
|
||||
s.logger.Error(
|
||||
"deleteUserCategoryBoards delete error",
|
||||
mlog.String("userID", userID),
|
||||
mlog.Array("boardID", boardIDs),
|
||||
mlog.Err(err),
|
||||
return fmt.Errorf(
|
||||
"store addUpdateCategoryBoard: failed to upsert user-board-category userID: %s, categoryID: %s, board_count: %d, error: %w",
|
||||
userID, categoryID, len(boardIDs), err,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SQLStore) categoryBoardsFromRows(rows *sql.Rows) ([]string, error) {
|
||||
blocks := []string{}
|
||||
func (s *SQLStore) categoryBoardsFromRows(rows *sql.Rows) ([]model.CategoryBoardMetadata, error) {
|
||||
metadata := []model.CategoryBoardMetadata{}
|
||||
|
||||
for rows.Next() {
|
||||
boardID := ""
|
||||
if err := rows.Scan(&boardID); err != nil {
|
||||
datum := model.CategoryBoardMetadata{}
|
||||
err := rows.Scan(&datum.BoardID, &datum.Hidden)
|
||||
|
||||
if err != nil {
|
||||
s.logger.Error("categoryBoardsFromRows row scan error", mlog.Err(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blocks = append(blocks, boardID)
|
||||
metadata = append(metadata, datum)
|
||||
}
|
||||
|
||||
return blocks, nil
|
||||
return metadata, nil
|
||||
}
|
||||
|
||||
func (s *SQLStore) reorderCategoryBoards(db sq.BaseRunner, categoryID string, newBoardsOrder []string) ([]string, error) {
|
||||
@ -166,7 +150,6 @@ func (s *SQLStore) reorderCategoryBoards(db sq.BaseRunner, categoryID string, ne
|
||||
Set("sort_order", updateCase).
|
||||
Where(sq.Eq{
|
||||
"category_id": categoryID,
|
||||
"delete_at": 0,
|
||||
})
|
||||
|
||||
if _, err := query.Exec(); err != nil {
|
||||
@ -181,3 +164,28 @@ func (s *SQLStore) reorderCategoryBoards(db sq.BaseRunner, categoryID string, ne
|
||||
|
||||
return newBoardsOrder, nil
|
||||
}
|
||||
|
||||
func (s *SQLStore) setBoardVisibility(db sq.BaseRunner, userID, categoryID, boardID string, visible bool) error {
|
||||
query := s.getQueryBuilder(db).
|
||||
Update(s.tablePrefix+"category_boards").
|
||||
Set("hidden", !visible).
|
||||
Where(sq.Eq{
|
||||
"user_id": userID,
|
||||
"category_id": categoryID,
|
||||
"board_id": boardID,
|
||||
})
|
||||
|
||||
if _, err := query.Exec(); err != nil {
|
||||
s.logger.Error(
|
||||
"SQLStore setBoardVisibility: failed to update board visibility",
|
||||
mlog.String("user_id", userID),
|
||||
mlog.String("board_id", boardID),
|
||||
mlog.Bool("visible", visible),
|
||||
mlog.Err(err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
241
server/services/store/sqlstore/compliance.go
Normal file
241
server/services/store/sqlstore/compliance.go
Normal file
@ -0,0 +1,241 @@
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
"github.com/mattermost/focalboard/server/model"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||
)
|
||||
|
||||
func (s *SQLStore) getBoardsForCompliance(db sq.BaseRunner, opts model.QueryBoardsForComplianceOptions) ([]*model.Board, bool, error) {
|
||||
query := s.getQueryBuilder(db).
|
||||
Select(boardFields("b.")...).
|
||||
From(s.tablePrefix + "boards as b")
|
||||
|
||||
if opts.TeamID != "" {
|
||||
query = query.Where(sq.Eq{"b.team_id": opts.TeamID})
|
||||
}
|
||||
|
||||
if opts.Page != 0 {
|
||||
query = query.Offset(uint64(opts.Page * opts.PerPage))
|
||||
}
|
||||
|
||||
if opts.PerPage > 0 {
|
||||
// N+1 to check if there's a next page for pagination
|
||||
query = query.Limit(uint64(opts.PerPage) + 1)
|
||||
}
|
||||
|
||||
rows, err := query.Query()
|
||||
if err != nil {
|
||||
s.logger.Error(`GetBoardsForCompliance ERROR`, mlog.Err(err))
|
||||
return nil, false, err
|
||||
}
|
||||
defer s.CloseRows(rows)
|
||||
|
||||
boards, err := s.boardsFromRows(rows)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
var hasMore bool
|
||||
if opts.PerPage > 0 && len(boards) > opts.PerPage {
|
||||
boards = boards[0:opts.PerPage]
|
||||
hasMore = true
|
||||
}
|
||||
return boards, hasMore, nil
|
||||
}
|
||||
|
||||
func (s *SQLStore) getBoardsComplianceHistory(db sq.BaseRunner, opts model.QueryBoardsComplianceHistoryOptions) ([]*model.BoardHistory, bool, error) {
|
||||
queryDescendentLastUpdate := s.getQueryBuilder(db).
|
||||
Select("MAX(blk1.update_at)").
|
||||
From(s.tablePrefix + "blocks_history as blk1").
|
||||
Where("blk1.board_id=bh.id")
|
||||
|
||||
if !opts.IncludeDeleted {
|
||||
queryDescendentLastUpdate.Where(sq.Eq{"blk1.delete_at": 0})
|
||||
}
|
||||
|
||||
sqlDescendentLastUpdate, _, _ := queryDescendentLastUpdate.ToSql()
|
||||
|
||||
queryDescendentFirstUpdate := s.getQueryBuilder(db).
|
||||
Select("MIN(blk2.update_at)").
|
||||
From(s.tablePrefix + "blocks_history as blk2").
|
||||
Where("blk2.board_id=bh.id")
|
||||
|
||||
if !opts.IncludeDeleted {
|
||||
queryDescendentFirstUpdate.Where(sq.Eq{"blk2.delete_at": 0})
|
||||
}
|
||||
|
||||
sqlDescendentFirstUpdate, _, _ := queryDescendentFirstUpdate.ToSql()
|
||||
|
||||
query := s.getQueryBuilder(db).
|
||||
Select(
|
||||
"bh.id",
|
||||
"bh.team_id",
|
||||
"CASE WHEN bh.delete_at=0 THEN false ELSE true END AS isDeleted",
|
||||
"COALESCE(("+sqlDescendentLastUpdate+"),0) as decendentLastUpdateAt",
|
||||
"COALESCE(("+sqlDescendentFirstUpdate+"),0) as decendentFirstUpdateAt",
|
||||
"bh.created_by",
|
||||
"bh.modified_by",
|
||||
).
|
||||
From(s.tablePrefix + "boards_history as bh")
|
||||
|
||||
if !opts.IncludeDeleted {
|
||||
// filtering out deleted boards; join with boards table to ensure no history
|
||||
// for deleted boards are returned. Deleted boards won't exist in boards table.
|
||||
query = query.Join(s.tablePrefix + "boards as b ON b.id=bh.id")
|
||||
}
|
||||
|
||||
query = query.Where(sq.Gt{"bh.update_at": opts.ModifiedSince}).
|
||||
GroupBy("bh.id", "bh.team_id", "bh.delete_at", "bh.created_by", "bh.modified_by").
|
||||
OrderBy("decendentLastUpdateAt desc", "bh.id")
|
||||
|
||||
if opts.TeamID != "" {
|
||||
query = query.Where(sq.Eq{"bh.team_id": opts.TeamID})
|
||||
}
|
||||
|
||||
if opts.Page != 0 {
|
||||
query = query.Offset(uint64(opts.Page * opts.PerPage))
|
||||
}
|
||||
|
||||
if opts.PerPage > 0 {
|
||||
// N+1 to check if there's a next page for pagination
|
||||
query = query.Limit(uint64(opts.PerPage) + 1)
|
||||
}
|
||||
|
||||
rows, err := query.Query()
|
||||
if err != nil {
|
||||
s.logger.Error(`GetBoardsComplianceHistory ERROR`, mlog.Err(err))
|
||||
return nil, false, err
|
||||
}
|
||||
defer s.CloseRows(rows)
|
||||
|
||||
history, err := s.boardsHistoryFromRows(rows)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
var hasMore bool
|
||||
if opts.PerPage > 0 && len(history) > opts.PerPage {
|
||||
history = history[0:opts.PerPage]
|
||||
hasMore = true
|
||||
}
|
||||
return history, hasMore, nil
|
||||
}
|
||||
|
||||
func (s *SQLStore) getBlocksComplianceHistory(db sq.BaseRunner, opts model.QueryBlocksComplianceHistoryOptions) ([]*model.BlockHistory, bool, error) {
|
||||
query := s.getQueryBuilder(db).
|
||||
Select(
|
||||
"bh.id",
|
||||
"brd.team_id",
|
||||
"bh.board_id",
|
||||
"bh.type",
|
||||
"CASE WHEN bh.delete_at=0 THEN false ELSE true END AS isDeleted",
|
||||
"max(bh.update_at) as lastUpdateAt",
|
||||
"min(bh.update_at) as firstUpdateAt",
|
||||
"bh.created_by",
|
||||
"bh.modified_by",
|
||||
).
|
||||
From(s.tablePrefix + "blocks_history as bh").
|
||||
Join(s.tablePrefix + "boards_history as brd on brd.id=bh.board_id")
|
||||
|
||||
if !opts.IncludeDeleted {
|
||||
// filtering out deleted blocks; join with blocks table to ensure no history
|
||||
// for deleted blocks are returned. Deleted blocks won't exist in blocks table.
|
||||
query = query.Join(s.tablePrefix + "blocks as b ON b.id=bh.id")
|
||||
}
|
||||
|
||||
query = query.Where(sq.Gt{"bh.update_at": opts.ModifiedSince}).
|
||||
GroupBy("bh.id", "brd.team_id", "bh.board_id", "bh.type", "bh.delete_at", "bh.created_by", "bh.modified_by").
|
||||
OrderBy("lastUpdateAt desc", "bh.id")
|
||||
|
||||
if opts.TeamID != "" {
|
||||
query = query.Where(sq.Eq{"brd.team_id": opts.TeamID})
|
||||
}
|
||||
|
||||
if opts.BoardID != "" {
|
||||
query = query.Where(sq.Eq{"bh.board_id": opts.BoardID})
|
||||
}
|
||||
|
||||
if opts.Page != 0 {
|
||||
query = query.Offset(uint64(opts.Page * opts.PerPage))
|
||||
}
|
||||
|
||||
if opts.PerPage > 0 {
|
||||
// N+1 to check if there's a next page for pagination
|
||||
query = query.Limit(uint64(opts.PerPage) + 1)
|
||||
}
|
||||
|
||||
rows, err := query.Query()
|
||||
if err != nil {
|
||||
s.logger.Error(`GetBlocksComplianceHistory ERROR`, mlog.Err(err))
|
||||
return nil, false, err
|
||||
}
|
||||
defer s.CloseRows(rows)
|
||||
|
||||
history, err := s.blocksHistoryFromRows(rows)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
var hasMore bool
|
||||
if opts.PerPage > 0 && len(history) > opts.PerPage {
|
||||
history = history[0:opts.PerPage]
|
||||
hasMore = true
|
||||
}
|
||||
return history, hasMore, nil
|
||||
}
|
||||
|
||||
func (s *SQLStore) boardsHistoryFromRows(rows *sql.Rows) ([]*model.BoardHistory, error) {
|
||||
history := []*model.BoardHistory{}
|
||||
|
||||
for rows.Next() {
|
||||
boardHistory := &model.BoardHistory{}
|
||||
|
||||
err := rows.Scan(
|
||||
&boardHistory.ID,
|
||||
&boardHistory.TeamID,
|
||||
&boardHistory.IsDeleted,
|
||||
&boardHistory.DescendantLastUpdateAt,
|
||||
&boardHistory.DescendantFirstUpdateAt,
|
||||
&boardHistory.CreatedBy,
|
||||
&boardHistory.LastModifiedBy,
|
||||
)
|
||||
if err != nil {
|
||||
s.logger.Error("boardsHistoryFromRows scan error", mlog.Err(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
history = append(history, boardHistory)
|
||||
}
|
||||
return history, nil
|
||||
}
|
||||
|
||||
func (s *SQLStore) blocksHistoryFromRows(rows *sql.Rows) ([]*model.BlockHistory, error) {
|
||||
history := []*model.BlockHistory{}
|
||||
|
||||
for rows.Next() {
|
||||
blockHistory := &model.BlockHistory{}
|
||||
|
||||
err := rows.Scan(
|
||||
&blockHistory.ID,
|
||||
&blockHistory.TeamID,
|
||||
&blockHistory.BoardID,
|
||||
&blockHistory.Type,
|
||||
&blockHistory.IsDeleted,
|
||||
&blockHistory.LastUpdateAt,
|
||||
&blockHistory.FirstUpdateAt,
|
||||
&blockHistory.CreatedBy,
|
||||
&blockHistory.LastModifiedBy,
|
||||
)
|
||||
if err != nil {
|
||||
s.logger.Error("blocksHistoryFromRows scan error", mlog.Err(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
history = append(history, blockHistory)
|
||||
}
|
||||
return history, nil
|
||||
}
|
@ -295,13 +295,14 @@ func (s *SQLStore) ensureMigrationsAppliedUpToVersion(engine *morph.Morph, drive
|
||||
|
||||
func (s *SQLStore) GetTemplateHelperFuncs() template.FuncMap {
|
||||
funcs := template.FuncMap{
|
||||
"addColumnIfNeeded": s.genAddColumnIfNeeded,
|
||||
"dropColumnIfNeeded": s.genDropColumnIfNeeded,
|
||||
"createIndexIfNeeded": s.genCreateIndexIfNeeded,
|
||||
"renameTableIfNeeded": s.genRenameTableIfNeeded,
|
||||
"renameColumnIfNeeded": s.genRenameColumnIfNeeded,
|
||||
"doesTableExist": s.doesTableExist,
|
||||
"doesColumnExist": s.doesColumnExist,
|
||||
"addColumnIfNeeded": s.genAddColumnIfNeeded,
|
||||
"dropColumnIfNeeded": s.genDropColumnIfNeeded,
|
||||
"createIndexIfNeeded": s.genCreateIndexIfNeeded,
|
||||
"renameTableIfNeeded": s.genRenameTableIfNeeded,
|
||||
"renameColumnIfNeeded": s.genRenameColumnIfNeeded,
|
||||
"doesTableExist": s.doesTableExist,
|
||||
"doesColumnExist": s.doesColumnExist,
|
||||
"addConstraintIfNeeded": s.genAddConstraintIfNeeded,
|
||||
}
|
||||
return funcs
|
||||
}
|
||||
@ -607,6 +608,67 @@ func (s *SQLStore) doesColumnExist(tableName, columnName string) (bool, error) {
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
func (s *SQLStore) genAddConstraintIfNeeded(tableName, constraintName, constraintType, constraintDefinition string) (string, error) {
|
||||
tableName = addPrefixIfNeeded(tableName, s.tablePrefix)
|
||||
normTableName := normalizeTablename(s.schemaName, tableName)
|
||||
|
||||
var query string
|
||||
|
||||
vars := map[string]string{
|
||||
"schema": s.schemaName,
|
||||
"constraint_name": constraintName,
|
||||
"constraint_type": constraintType,
|
||||
"table_name": tableName,
|
||||
"constraint_definition": constraintDefinition,
|
||||
"norm_table_name": normTableName,
|
||||
}
|
||||
|
||||
switch s.dbType {
|
||||
case model.SqliteDBType:
|
||||
// SQLite doesn't have a generic way to add constraint. For example, you can only create indexes on existing tables.
|
||||
// For other constraints, you need to re-build the table. So skipping here.
|
||||
// Include SQLite specific migration in original migration file.
|
||||
query = fmt.Sprintf("\n-- Sqlite3 cannot drop constraints; drop constraint '%s' in table '%s' skipped\n", constraintName, tableName)
|
||||
case model.MysqlDBType:
|
||||
query = replaceVars(`
|
||||
SET @stmt = (SELECT IF(
|
||||
(
|
||||
SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
|
||||
WHERE constraint_schema = '[[schema]]'
|
||||
AND constraint_name = '[[constraint_name]]'
|
||||
AND constraint_type = '[[constraint_type]]'
|
||||
AND table_name = '[[table_name]]'
|
||||
) > 0,
|
||||
'SELECT 1;',
|
||||
'ALTER TABLE [[norm_table_name]] ADD CONSTRAINT [[constraint_name]] [[constraint_definition]];'
|
||||
));
|
||||
PREPARE addConstraintIfNeeded FROM @stmt;
|
||||
EXECUTE addConstraintIfNeeded;
|
||||
DEALLOCATE PREPARE addConstraintIfNeeded;
|
||||
`, vars)
|
||||
case model.PostgresDBType:
|
||||
query = replaceVars(`
|
||||
DO
|
||||
$$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
|
||||
WHERE constraint_schema = '[[schema]]'
|
||||
AND constraint_name = '[[constraint_name]]'
|
||||
AND constraint_type = '[[constraint_type]]'
|
||||
AND table_name = '[[table_name]]'
|
||||
) THEN
|
||||
ALTER TABLE [[norm_table_name]] ADD CONSTRAINT [[constraint_name]] [[constraint_definition]];
|
||||
END IF;
|
||||
END;
|
||||
$$
|
||||
LANGUAGE plpgsql;
|
||||
`, vars)
|
||||
}
|
||||
|
||||
return query, nil
|
||||
}
|
||||
|
||||
func addPrefixIfNeeded(s, prefix string) string {
|
||||
if !strings.HasPrefix(s, prefix) {
|
||||
return prefix + s
|
||||
|
@ -0,0 +1 @@
|
||||
SELECT 1;
|
@ -0,0 +1 @@
|
||||
DELETE FROM {{.prefix}}category_boards WHERE delete_at > 0;
|
@ -0,0 +1 @@
|
||||
SELECT 1;
|
@ -0,0 +1,3 @@
|
||||
{{ if or .postgres .mysql }}
|
||||
{{ dropColumnIfNeeded "category_boards" "delete_at" }}
|
||||
{{end}}
|
@ -0,0 +1 @@
|
||||
SELECT 1;
|
@ -0,0 +1 @@
|
||||
{{ addColumnIfNeeded "category_boards" "hidden" "boolean" "" }}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user