diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 296435f5f..783c58582 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -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']}}
diff --git a/.github/workflows/dev-release.yml b/.github/workflows/dev-release.yml
index 2376c1f1d..9ebf34df6 100644
--- a/.github/workflows/dev-release.yml
+++ b/.github/workflows/dev-release.yml
@@ -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
diff --git a/.github/workflows/lint-server.yml b/.github/workflows/lint-server.yml
index eb5093406..3c114e71b 100644
--- a/.github/workflows/lint-server.yml
+++ b/.github/workflows/lint-server.yml
@@ -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
diff --git a/.github/workflows/prod-release.yml b/.github/workflows/prod-release.yml
index feb078617..8ca6f4f98 100644
--- a/.github/workflows/prod-release.yml
+++ b/.github/workflows/prod-release.yml
@@ -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
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 09944dba2..f6107d60f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -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
diff --git a/build/gowork/main.go b/build/gowork/main.go
index c962369d5..b8619ebdf 100644
--- a/build/gowork/main.go
+++ b/build/gowork/main.go
@@ -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 {
diff --git a/linux/go.mod b/linux/go.mod
index b5a89f917..497722568 100644
--- a/linux/go.mod
+++ b/linux/go.mod
@@ -1,6 +1,6 @@
module github.com/mattermost/focalboard/linux
-go 1.18
+go 1.19
replace github.com/mattermost/focalboard/server => ../server
diff --git a/mattermost-plugin/build/go.mod b/mattermost-plugin/build/go.mod
index 679c3003e..0ba2a34ef 100644
--- a/mattermost-plugin/build/go.mod
+++ b/mattermost-plugin/build/go.mod
@@ -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
diff --git a/mattermost-plugin/go.mod b/mattermost-plugin/go.mod
index b36f6db8d..6d34bf537 100644
--- a/mattermost-plugin/go.mod
+++ b/mattermost-plugin/go.mod
@@ -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
diff --git a/mattermost-plugin/go.sum b/mattermost-plugin/go.sum
index e6f5d07fb..b77eb00dc 100644
--- a/mattermost-plugin/go.sum
+++ b/mattermost-plugin/go.sum
@@ -33,17 +33,14 @@ cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM=
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
-cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
-cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
-cloud.google.com/go/errorreporting v0.2.0/go.mod h1:QkYzg92wgpJ0ChLdcO5LhtCEyYwq0tIa+jLrj6Nh5ME=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
@@ -56,9 +53,6 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
-code.sajari.com/docconv v1.2.0 h1:4UFgUodoW/hZy8dgghiCSSrcKLJP3Imo6UYFatP0PqI=
-code.sajari.com/docconv v1.2.0/go.mod h1:r8yfCP6OKbZ9Xkd87aBa4nfpk6ud/PoyLwex3n6cXSc=
-code.sajari.com/docconv v1.3.5/go.mod h1:EDkTrwa2yO2O9EbVpD3dlHXDVcxbfKDWnDNE/8vbbP8=
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
@@ -87,16 +81,11 @@ github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935
github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
-github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
+github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
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/HdrHistogram/hdrhistogram-go v0.9.0 h1:dpujRju0R4M/QZzcnR1LH1qm+TVG3UzkWdp5tH1WMcg=
-github.com/JalfResi/justext v0.0.0-20170829062021-c0282dea7198 h1:8P+AjBhGByCuCX2zTkAf6UY+dj0JczX+t6cSdCSyvfw=
-github.com/JalfResi/justext v0.0.0-20170829062021-c0282dea7198/go.mod h1:0SURuH1rsE8aVWvutuMZghRNrNrYEUzibzJfhEYR8L0=
-github.com/JalfResi/justext v0.0.0-20221106200834-be571e3e3052/go.mod h1:0SURuH1rsE8aVWvutuMZghRNrNrYEUzibzJfhEYR8L0=
-github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
-github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Masterminds/squirrel v1.5.2 h1:UiOEi2ZX4RCSkpiNDQN5kro/XIBpSRk9iTqdIRPzUXE=
github.com/Masterminds/squirrel v1.5.2/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
@@ -125,21 +114,11 @@ github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:m
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
-github.com/PuerkitoBio/goquery v1.4.1/go.mod h1:T9ezsOHcCrDCgA8aF1Cqr3sSYbO/xgdy8/R/XiIMAhA=
-github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
-github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U=
-github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
-github.com/RoaringBitmap/roaring v0.9.4/go.mod h1:icnadbWcNyfEHlYdr+tDlOTih1Bf/h+rzPpv4sbomAA=
-github.com/RoaringBitmap/roaring v1.2.1 h1:58/LJlg/81wfEHd5L9qsHduznOIhyv4qb1yWcSvVq9A=
-github.com/RoaringBitmap/roaring v1.2.1/go.mod h1:icnadbWcNyfEHlYdr+tDlOTih1Bf/h+rzPpv4sbomAA=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
-github.com/advancedlogic/GoOse v0.0.0-20191112112754-e742535969c1/go.mod h1:f3HCSN1fBWjcpGtXyM119MJgeQl838v6so/PQOqvE1w=
-github.com/advancedlogic/GoOse v0.0.0-20210820140952-9d5822d4a625 h1:LZIP5Bj5poWWRZ8fcL4ZwCupb4FwcTFK2RCTxkGnCX8=
-github.com/advancedlogic/GoOse v0.0.0-20210820140952-9d5822d4a625/go.mod h1:f3HCSN1fBWjcpGtXyM119MJgeQl838v6so/PQOqvE1w=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -148,34 +127,17 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0=
github.com/alexflint/go-filemutex v1.1.0/go.mod h1:7P4iRhttt/nUvUOrYIhcpMzv2G6CY9UnI16Z+UJqRyk=
-github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
-github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
-github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
-github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
-github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
-github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY=
-github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
-github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/apache/arrow/go/arrow v0.0.0-20210818145353-234c94e4ce64/go.mod h1:2qMFB56yOP3KzkB3PbYZ4AlUFg3a88F67TIx5lB/WwY=
github.com/apache/arrow/go/arrow v0.0.0-20211013220434-5962184e7a30/go.mod h1:Q7yQnSMnLvcXlZ8RV+jwz/6y1rQTqbX6C82SndT52Zs=
-github.com/araddon/dateparse v0.0.0-20180729174819-cfd92a431d0e/go.mod h1:SLqhdZcd+dF3TEVL2RMoob5bBP5R1P1qkox+HtCBgGI=
-github.com/araddon/dateparse v0.0.0-20200409225146-d820a6159ab1/go.mod h1:SLqhdZcd+dF3TEVL2RMoob5bBP5R1P1qkox+HtCBgGI=
-github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
-github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
-github.com/avct/uasurfer v0.0.0-20191028135549-26b5daa857f1 h1:9h8f71kuF1pqovnn9h7LTHLEjxzyQaj0j1rQq5nsMM4=
-github.com/avct/uasurfer v0.0.0-20191028135549-26b5daa857f1/go.mod h1:noBAuukeYOXa0aXGqxr24tADqkwDO2KRD15FsuaZ5a8=
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
-github.com/aws/aws-sdk-go v1.44.34 h1:+ZtWIbtSGLNB99P8eBrxXfJZgiIouzUbpkf/MNxq2yQ=
-github.com/aws/aws-sdk-go v1.44.34/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
-github.com/aws/aws-sdk-go v1.44.138/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go-v2 v1.8.0/go.mod h1:xEFuWz+3TYdlPRuo+CqATbeDWIWyaT5uAPwPaWtgse0=
github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
github.com/aws/aws-sdk-go-v2/config v1.6.0/go.mod h1:TNtBVmka80lRPk5+S9ZqVfFszOQAGJJ9KbT3EM3CHNU=
@@ -202,8 +164,6 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.6.1/go.mod h1:hLZ/AnkIKHLuPGjEiyghNE
github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21TfrhJ8AEMzVybRNSb/b4g=
github.com/aws/smithy-go v1.7.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
-github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
-github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
@@ -214,11 +174,6 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
-github.com/bits-and-blooms/bitset v1.2.2 h1:J5gbX05GpMdBjCvQ9MteIg2KKDExr7DrgK+Yc15FvIk=
-github.com/bits-and-blooms/bitset v1.2.2/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
-github.com/bits-and-blooms/bitset v1.3.1/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
-github.com/bits-and-blooms/bitset v1.3.3/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
-github.com/bits-and-blooms/bloom/v3 v3.3.1/go.mod h1:bhUUknWd5khVbTe4UgMCSiOOVJzr3tMoijSK3WwvW90=
github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
@@ -226,57 +181,6 @@ github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdn
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
-github.com/blevesearch/bleve/v2 v2.3.2 h1:BJUnMhi2nrkl+vboHmKfW+9l+tJSj39HeWa5c3BN3/Y=
-github.com/blevesearch/bleve/v2 v2.3.2/go.mod h1:96+xE5pZUOsr3Y4vHzV1cBC837xZCpwLlX0hrrxnvIg=
-github.com/blevesearch/bleve/v2 v2.3.6-0.20221111171245-56dc9b25507e/go.mod h1:mfCWvuwg/XnPVZHEejATm5TyFqyeLmm8p9Y3xDvwz4k=
-github.com/blevesearch/bleve_index_api v1.0.1/go.mod h1:fiwKS0xLEm+gBRgv5mumf0dhgFr2mDgZah1pqv1c1M4=
-github.com/blevesearch/bleve_index_api v1.0.2 h1:rO736FwEPMVY1mGi7d4n7CgBB3+tB7uYN7QTjR+Ij+s=
-github.com/blevesearch/bleve_index_api v1.0.2/go.mod h1:fiwKS0xLEm+gBRgv5mumf0dhgFr2mDgZah1pqv1c1M4=
-github.com/blevesearch/bleve_index_api v1.0.5/go.mod h1:YXMDwaXFFXwncRS8UobWs7nvo0DmusriM1nztTlj1ms=
-github.com/blevesearch/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:9eJDeqxJ3E7WnLebQUlPD7ZjSce7AnDb9vjGmMCbD0A=
-github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo=
-github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M=
-github.com/blevesearch/goleveldb v1.0.1/go.mod h1:WrU8ltZbIp0wAoig/MHbrPCXSOLpe79nz5lv5nqfYrQ=
-github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y=
-github.com/blevesearch/gtreap v0.1.1/go.mod h1:QaQyDRAT51sotthUWAH4Sj08awFSSWzgYICSZ3w0tYk=
-github.com/blevesearch/mmap-go v1.0.2/go.mod h1:ol2qBqYaOUsGdm7aRMRrYGgPvnwLe6Y+7LMvAB5IbSA=
-github.com/blevesearch/mmap-go v1.0.3/go.mod h1:pYvKl/grLQrBxuaRYgoTssa4rVujYYeenDp++2E+yvs=
-github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc=
-github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs=
-github.com/blevesearch/scorch_segment_api/v2 v2.1.0 h1:NFwteOpZEvJk5Vg0H6gD0hxupsG3JYocE4DBvsA2GZI=
-github.com/blevesearch/scorch_segment_api/v2 v2.1.0/go.mod h1:uch7xyyO/Alxkuxa+CGs79vw0QY8BENSBjg6Mw5L5DE=
-github.com/blevesearch/scorch_segment_api/v2 v2.1.4/go.mod h1:PgVnbbg/t1UkgezPDu8EHLi1BHQ17xUwsFdU6NnOYS0=
-github.com/blevesearch/segment v0.9.0 h1:5lG7yBCx98or7gK2cHMKPukPZ/31Kag7nONpoBt22Ac=
-github.com/blevesearch/segment v0.9.0/go.mod h1:9PfHYUdQCgHktBgvtUOF4x+pc4/l8rdH0u5spnW85UQ=
-github.com/blevesearch/snowball v0.6.1/go.mod h1:ZF0IBg5vgpeoUhnMza2v0A/z8m1cWPlwhke08LpNusg=
-github.com/blevesearch/snowballstem v0.9.0 h1:lMQ189YspGP6sXvZQ4WZ+MLawfV8wOmPoD/iWeNXm8s=
-github.com/blevesearch/snowballstem v0.9.0/go.mod h1:PivSj3JMc8WuaFkTSRDW2SlrulNWPl4ABg1tC/hlgLs=
-github.com/blevesearch/upsidedown_store_api v1.0.1 h1:1SYRwyoFLwG3sj0ed89RLtM15amfX2pXlYbFOnF8zNU=
-github.com/blevesearch/upsidedown_store_api v1.0.1/go.mod h1:MQDVGpHZrpe3Uy26zJBf/a8h0FZY6xJbthIMm8myH2Q=
-github.com/blevesearch/vellum v1.0.7/go.mod h1:doBZpmRhwTsASB4QdUZANlJvqVAUdUyX0ZK7QJCTeBE=
-github.com/blevesearch/vellum v1.0.8 h1:iMGh4lfxza4BnWO/UJTMPlI3HsK9YawjPv+TteVa9ck=
-github.com/blevesearch/vellum v1.0.8/go.mod h1:+cpRi/tqq49xUYSQN2P7A5zNSNrS+MscLeeaZ3J46UA=
-github.com/blevesearch/vellum v1.0.9/go.mod h1:ul1oT0FhSMDIExNjIxHqJoGpVrBpKCdgDQNxfqgJt7k=
-github.com/blevesearch/zapx/v11 v11.3.3/go.mod h1:YzTfUm4kS3e8OmTXDHVV8OzC5MWPO/VPJZQgPNVb4Lc=
-github.com/blevesearch/zapx/v11 v11.3.4 h1:MjYFN8fwDajRgeUsKMfW673zI6MI3twy0pCsUH/LXgc=
-github.com/blevesearch/zapx/v11 v11.3.4/go.mod h1:HJ7qdfBxdziuymKvXbsBVhCK5pB98tdzQbc8pJV6tJo=
-github.com/blevesearch/zapx/v11 v11.3.7/go.mod h1:Xk9Z69AoAWIOvWudNDMlxJDqSYGf90LS0EfnaAIvXCA=
-github.com/blevesearch/zapx/v12 v12.3.3/go.mod h1:RMl6lOZqF+sTxKvhQDJ5yK2LT3Mu7E2p/jGdjAaiRxs=
-github.com/blevesearch/zapx/v12 v12.3.4 h1:OpPoHQjsjvDImDzwKZXTXubIPJz28EaRynJGJSS6mvU=
-github.com/blevesearch/zapx/v12 v12.3.4/go.mod h1:uQrKrK9XjXAAsJfAIE8ViLqIKP/keA2DQhS1XXpjkwA=
-github.com/blevesearch/zapx/v12 v12.3.7/go.mod h1:SgEtYIBGvM0mgIBn2/tQE/5SdrPXaJUaT/kVqpAPxm0=
-github.com/blevesearch/zapx/v13 v13.3.3/go.mod h1:eppobNM35U4C22yDvTuxV9xPqo10pwfP/jugL4INWG4=
-github.com/blevesearch/zapx/v13 v13.3.4 h1:f646k6300VGRIR7eJ6lLtF8UC95NIWmF899j49o7PJA=
-github.com/blevesearch/zapx/v13 v13.3.4/go.mod h1:Wl7hO1gT+IDvJb7i06g2iW5Qvw0KzncJPsBx7WGWhLA=
-github.com/blevesearch/zapx/v13 v13.3.7/go.mod h1:yyrB4kJ0OT75UPZwT/zS+Ru0/jYKorCOOSY5dBzAy+s=
-github.com/blevesearch/zapx/v14 v14.3.3/go.mod h1:zXNcVzukh0AvG57oUtT1T0ndi09H0kELNaNmekEy0jw=
-github.com/blevesearch/zapx/v14 v14.3.4 h1:/FVzSGFG5rbVWfPEqlcaJd8lZSJMQpTdmFhz/l2QI7w=
-github.com/blevesearch/zapx/v14 v14.3.4/go.mod h1:b1YhRXXhAj9i+9aOwhRKCHUmJyYieK/QbDvPJDLddUk=
-github.com/blevesearch/zapx/v14 v14.3.7/go.mod h1:9J/RbOkqZ1KSjmkOes03AkETX7hrXT0sFMpWH4ewC4w=
-github.com/blevesearch/zapx/v15 v15.3.3/go.mod h1:C+f/97ZzTzK6vt/7sVlZdzZxKu+5+j4SrGCvr9dJzaY=
-github.com/blevesearch/zapx/v15 v15.3.4 h1:/y6AOxRuBiZPFAItqcrKcXPPtlAwuW/jMoOFO7tc7rs=
-github.com/blevesearch/zapx/v15 v15.3.4/go.mod h1:TQ/qDC2q7TSSpeC6Vgr9fDN56Ra0u49lZJQ4v30WEx4=
-github.com/blevesearch/zapx/v15 v15.3.7/go.mod h1:m7Y6m8soYUvS7MjN9eKlz1xrLCcmqfFadmu7GhWIrLY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
@@ -427,7 +331,6 @@ github.com/containers/ocicrypt v1.1.2/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
-github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
@@ -444,9 +347,6 @@ github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
-github.com/couchbase/ghistogram v0.1.0/go.mod h1:s1Jhy76zqfEecpNWJfWUiKZookAFaiGOEoyzgHt9i7k=
-github.com/couchbase/moss v0.2.0/go.mod h1:9MaHIaRuy9pvLPUJxB8sh8OrLfyDczECVL37grCIubs=
-github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
@@ -466,16 +366,9 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
-github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
-github.com/dgryski/dgoogauth v0.0.0-20190221195224-5a805980a5f3 h1:AqeKSZIG/NIC75MNQlPy/LM3LxfpLwahICJBHwSMFNc=
-github.com/dgryski/dgoogauth v0.0.0-20190221195224-5a805980a5f3/go.mod h1:hEfFauPHz7+NnjR/yHJGhrKo1Za+zStgwUETx3yzqgY=
-github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
-github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dhui/dktest v0.3.10/go.mod h1:h5Enh0nG3Qbo9WjNFRrwmKUaePEBhXMOygbz3Ww7Sz0=
-github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
-github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
@@ -494,9 +387,6 @@ github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
-github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY=
-github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
-github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
@@ -515,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=
@@ -524,11 +413,7 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
-github.com/fatih/set v0.2.1 h1:nn2CaJyknWE/6txyUDGwysr3G5QC6xWB/PtVjPBbeaA=
-github.com/fatih/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0CI=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
-github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
-github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
@@ -540,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=
@@ -549,20 +433,13 @@ github.com/gabriel-vasile/mimetype v1.3.1/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmx
github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8=
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
-github.com/getsentry/sentry-go v0.13.0 h1:20dgTiUSfxRB/EhMPtxcL9ZEbM1ZdR+W/7f7NWD+xWo=
-github.com/getsentry/sentry-go v0.13.0/go.mod h1:EOsfu5ZdvKPfeHYV6pTVQnsjfp30+XA7//UooKNumH0=
-github.com/getsentry/sentry-go v0.15.0/go.mod h1:RZPJKSw+adu8PBNygiri/A98FqVr2HtRckJk9XVxJ9I=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/gigawattio/window v0.0.0-20180317192513-0f5467e35573 h1:u8AQ9bPa9oC+8/A/jlWouakhIvkFfuxgIIRjiy8av7I=
-github.com/gigawattio/window v0.0.0-20180317192513-0f5467e35573/go.mod h1:eBvb3i++NHDH4Ugo9qCvMw8t0mTSctaEa5blJbWcNxs=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-asn1-ber/asn1-ber v1.3.2-0.20191121212151-29be175fc3a3/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A=
github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
-github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
-github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g=
github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks=
github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY=
@@ -603,13 +480,6 @@ github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dp
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
-github.com/go-redis/redis/v8 v8.0.0/go.mod h1:isLoQT/NFSP7V67lyvM9GmdvLdyZ7pEhsXvvyQtnQTo=
-github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
-github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
-github.com/go-resty/resty/v2 v2.0.0/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8=
-github.com/go-resty/resty/v2 v2.3.0/go.mod h1:UpN9CgLZNsv4e9XG50UU8xdI0F43UQ4HmxLBDwaroHU=
-github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
-github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
@@ -663,7 +533,6 @@ github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzw
github.com/golang-migrate/migrate/v4 v4.15.2 h1:vU+M05vs6jWHKDdmE1Ecwj0BznygFc4QsdRe2E/L7kc=
github.com/golang-migrate/migrate/v4 v4.15.2/go.mod h1:f2toGLkYqD3JH+Todi4aZ2ZdbeUNx4sIwiOK96rE9Lw=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
-github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -703,13 +572,9 @@ github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
@@ -726,9 +591,9 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-github/v39 v39.2.0/go.mod h1:C1s8C5aCC9L+JXIYpJM5GYytdX52vC1bLvHEF1IhBrE=
@@ -753,7 +618,6 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
@@ -775,14 +639,8 @@ github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2c
github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
-github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
-github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
-github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
-github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
-github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
-github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
@@ -804,8 +662,6 @@ github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpg
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
-github.com/h2non/go-is-svg v0.0.0-20160927212452-35e8c4b0612c h1:fEE5/5VNnYUoBOj2I9TP8Jc+a7lge3QWn9DKE7NCwfc=
-github.com/h2non/go-is-svg v0.0.0-20160927212452-35e8c4b0612c/go.mod h1:ObS/W+h8RYb1Y7fYivughjxojTmIu5iAIjSrSLCLeqE=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
@@ -814,8 +670,7 @@ 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-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
@@ -823,8 +678,7 @@ github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:
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-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
@@ -834,16 +688,13 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
-github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
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=
@@ -901,10 +752,6 @@ github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0f
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
-github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
-github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
-github.com/jaytaylor/html2text v0.0.0-20211105163654-bc68cce691ba h1:QFQpJdgbON7I0jr2hYW7Bs+XV0qjc3d5tZoDnRFnqTg=
-github.com/jaytaylor/html2text v0.0.0-20211105163654-bc68cce691ba/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
@@ -912,9 +759,7 @@ github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
-github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
-github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/jmoiron/sqlx v1.3.1/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ=
@@ -950,25 +795,18 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
-github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
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/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
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/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
-github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
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=
@@ -992,18 +830,13 @@ github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
-github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo=
-github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
-github.com/levigross/exp-html v0.0.0-20120902181939-8df60c69a8f5 h1:W7p+m/AECTL3s/YR5RpQ4hz5SjNeKzZBl1q36ws12s0=
-github.com/levigross/exp-html v0.0.0-20120902181939-8df60c69a8f5/go.mod h1:QMe2wuKJ0o7zIVE8AqiT8rd8epmm6WDIZ2wyuBqYPzM=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
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=
@@ -1032,18 +865,10 @@ 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-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/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=
github.com/mattermost/morph v1.0.5-0.20221115094356-4c18a75b1f5e/go.mod h1:xo0ljDknTpPxEdhhrUdwhLCexIsYyDKS6b41HqG8wGU=
-github.com/mattermost/rsc v0.0.0-20160330161541-bbaefb05eaa0 h1:G9tL6JXRBMzjuD1kkBtcnd42kUiT6QDwxfFYu7adM6o=
-github.com/mattermost/rsc v0.0.0-20160330161541-bbaefb05eaa0/go.mod h1:nV5bfVpT//+B1RPD2JvRnxbkLmJEYXmRaaVl15fsXjs=
github.com/mattermost/squirrel v0.2.0 h1:8ZWeyf+MWQ2cL7hu9REZgLtz2IJi51qqZEovI3T3TT8=
github.com/mattermost/squirrel v0.2.0/go.mod h1:NPPtk+CdpWre4GxMGoOpzEVFVc0ZoEFyJBZGCtn9nSU=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
@@ -1052,8 +877,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=
@@ -1063,23 +888,15 @@ 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-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
-github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
-github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
-github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
-github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
-github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
-github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
-github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
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=
@@ -1088,25 +905,18 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY=
-github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo=
-github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
-github.com/microcosm-cc/bluemonday v1.0.18 h1:6HcxvXDAi3ARt3slx6nTesbvorIc3QeTzBNRvWktHBo=
-github.com/microcosm-cc/bluemonday v1.0.18/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM=
-github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
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/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=
@@ -1140,8 +950,6 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
-github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
-github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mutecomm/go-sqlcipher/v4 v4.4.0/go.mod h1:PyN04SaWalavxRGH9E8ZftG6Ju7rsPrGmQRjrEaVpiY=
@@ -1154,24 +962,15 @@ github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJE
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/neo4j/neo4j-go-driver v1.8.1-0.20200803113522-b626aa943eba/go.mod h1:ncO5VaFWh0Nrt+4KT4mOZboaczBZcLuHrG+/sUeP8gI=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
-github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc=
-github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
-github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
-github.com/olekukonko/tablewriter v0.0.0-20180506121414-d4647c9c7a84/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
-github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
-github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
-github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -1180,27 +979,16 @@ github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
-github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
-github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
-github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
-github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
-github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
-github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
-github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
-github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
-github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
-github.com/oov/psd v0.0.0-20220121172623-5db5eafcecbb h1:JF9kOhBBk4WPF7luXFu5yR+WgaFm9L/KiHJHhU9vDwA=
-github.com/oov/psd v0.0.0-20220121172623-5db5eafcecbb/go.mod h1:GHI1bnmAcbp96z6LNfBJvtrjxhaXGkbsk967utPlvL8=
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
@@ -1229,21 +1017,8 @@ github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3
github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8=
github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
-github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
-github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
-github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
-github.com/otiai10/gosseract/v2 v2.2.4/go.mod h1:ahOp/kHojnOMGv1RaUnR0jwY5JVa6BYKhYAS8nbMLSo=
-github.com/otiai10/gosseract/v2 v2.3.1 h1:BFy9Rru7dzqEYX7/tJuEvjVPkkJck0f+b5fYzzr6/RM=
-github.com/otiai10/gosseract/v2 v2.3.1/go.mod h1:2ZOGgdTIXQzCS5f+N1HkcXRgDX6K3ZoYe3Yvo++cpp4=
-github.com/otiai10/gosseract/v2 v2.4.0 h1:gYd3mx6FuMtIlxL4sYb9JLCFEDzg09VgNSZRNbqpiGM=
-github.com/otiai10/gosseract/v2 v2.4.0/go.mod h1:fhbIDRh29bj13vni6RT3gtWKjKCAeqDYI4C1dxeJuek=
-github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
-github.com/otiai10/mint v1.3.2 h1:VYWnrP5fXmz1MXvjuUvcBrXSjGE6xjON+axB/UrpO3E=
-github.com/otiai10/mint v1.3.2/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
-github.com/otiai10/mint v1.3.3 h1:7JgpsBaN0uMkyju4tbYHu0mnM55hNKVYLsXmwr15NQI=
-github.com/otiai10/mint v1.3.3/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
@@ -1259,12 +1034,7 @@ github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG
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=
-github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.8/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
-github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
-github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
-github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
-github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -1321,55 +1091,35 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
-github.com/reflog/dateconstraints v0.2.1 h1:Hz1n2Q1vEm0Rj5gciDQcCN1iPBwfFjxUJy32NknGP/s=
-github.com/reflog/dateconstraints v0.2.1/go.mod h1:Ax8AxTBcJc3E/oVS2hd2j7RDM/5MDtuPwuR7lIHtPLo=
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/richardlehane/mscfb v1.0.3/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
-github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
-github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
-github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
-github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM=
-github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
-github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rivo/uniseg v0.3.4 h1:3Z3Eu6FGHZWSfNKJTOUiPatWwfc7DzJRU04jFUqJODw=
-github.com/rivo/uniseg v0.3.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
-github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
-github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U=
-github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
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=
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
-github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc=
-github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
-github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg=
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=
@@ -1400,7 +1150,6 @@ github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
-github.com/simplereach/timeutils v1.2.0/go.mod h1:VVbQDfN/FHRZa1LSqcwo4kNZ62OOyqLLGQKYB3pB0Q8=
github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
@@ -1409,8 +1158,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=
@@ -1432,7 +1181,6 @@ github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
-github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
@@ -1444,30 +1192,18 @@ github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.10.1 h1:nuJZuYpG7gTj/XqiUwg8bA0cp1+M2mC3J4g5luUYBKk=
github.com/spf13/viper v1.10.1/go.mod h1:IGlFPqhNAPKRxohIzWpI5QEy4kuI7tcl5WvR+8qy1rU=
-github.com/splitio/go-client/v6 v6.1.0 h1:hxDqrMfYlyuJNtfml4chl2LXGL2urxMBfP8WpLS55Zo=
-github.com/splitio/go-client/v6 v6.1.0/go.mod h1:CEGAEFT99Fwb32ZIRcnZoXTMXddtB6IIpTmt3RP8mnM=
-github.com/splitio/go-client/v6 v6.2.1/go.mod h1:+HnGMevmSUk56va2egs9W2s9mJ7LW9IXiDPB1ExOi+k=
-github.com/splitio/go-split-commons/v3 v3.1.0 h1:MDudl8YIjyB7By0/3Rv2wi5WZDbbjak+12NDfQPYy5A=
-github.com/splitio/go-split-commons/v3 v3.1.0/go.mod h1:29NCy20oAS4ZMy4qkwTd6277eieVDonx4V/aeDU/wUQ=
-github.com/splitio/go-split-commons/v4 v4.2.0/go.mod h1:mzanM00PV8t1FL6IHc2UXepIH2z79d49ArZ2LoJHGrY=
-github.com/splitio/go-toolkit/v4 v4.2.0 h1:DnC7zQXR7Be9i9bZzXBuUUpaJ21EYku2oGSsIiG8Wc0=
-github.com/splitio/go-toolkit/v4 v4.2.0/go.mod h1:EdIHN0yzB1GTXDYQc0KdKvnjkO/jfUM2YqHVYfhD3Wo=
-github.com/splitio/go-toolkit/v5 v5.2.2/go.mod h1:SYi/svhhtEgdMSb5tNcDcMjOSUH/7XVkvjp5dPL+nBE=
-github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
-github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
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=
@@ -1478,8 +1214,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=
@@ -1488,16 +1224,13 @@ 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/throttled/throttled v2.2.5+incompatible h1:65UB52X0qNTYiT0Sohp8qLYVFwZQPDw85uSa65OljjQ=
-github.com/throttled/throttled v2.2.5+incompatible/go.mod h1:0BjlrEGQmvxps+HuXLsyRdqpSRvJpq0PNIsOtqP9Nos=
-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/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=
@@ -1505,17 +1238,7 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1
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=
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM=
-github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
-github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o=
-github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
-github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg=
-github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
-github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
-github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
-github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
-github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
-github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
@@ -1535,8 +1258,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=
@@ -1549,8 +1271,6 @@ github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
-github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
-github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c h1:3lbZUMbMiGUW/LMkfsEABsc5zNT9+b1CvsJx47JzJ8g=
@@ -1561,9 +1281,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
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 h1:3HUJmBFbQW9fhQOzMgseU134xfi6hU+mjWywx5Ty+/M=
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=
@@ -1573,7 +1291,6 @@ gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
-go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
@@ -1597,7 +1314,6 @@ go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUz
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.28.0/go.mod h1:vEhqr0m4eTc+DWxfsXoXue2GBgV2uUwVznkGIHW/e5w=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4=
-go.opentelemetry.io/otel v0.11.0/go.mod h1:G8UCk+KooF2HLkgo8RHX9epABH/aRGYET7gQOqBVdB0=
go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo=
go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=
go.opentelemetry.io/otel v1.6.3/go.mod h1:7BgNga5fNlF/iZjG06hM3yofffp0ofKCDwSXx1GC4dI=
@@ -1621,9 +1337,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.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
-go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
+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=
@@ -1640,7 +1354,6 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -1664,8 +1377,7 @@ 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/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=
@@ -1681,20 +1393,15 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
-golang.org/x/exp v0.0.0-20200908183739-ae8ad444f925/go.mod h1:1phAWC201xIgDyaFpmDeZkgf70Q4Pd/CNqfRtVPtxNw=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/image v0.0.0-20220601225756-64ec528b34cd h1:9NbNcTg//wfC5JskFW4Z3sqwVnjmJKHxLAol1bW2qgw=
-golang.org/x/image v0.0.0-20220601225756-64ec528b34cd/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
-golang.org/x/image v0.1.0/go.mod h1:iyPr49SD/G/TBxYVB/9RRtGUT5eNbo2u4NamWeQcD5c=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@@ -1716,13 +1423,11 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
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=
@@ -1767,7 +1472,6 @@ golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
@@ -1790,18 +1494,13 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
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.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
+golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
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=
@@ -1837,9 +1536,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
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=
@@ -1850,8 +1547,6 @@ golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1956,7 +1651,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=
@@ -1966,30 +1660,22 @@ golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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-20220128215802-99c3d69c2c27/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.1.0/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/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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
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=
@@ -1998,8 +1684,8 @@ 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/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=
@@ -2092,9 +1778,7 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
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 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM=
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
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=
@@ -2102,7 +1786,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
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/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=
@@ -2144,8 +1827,6 @@ google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqiv
google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw=
-google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
-google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
google.golang.org/appengine v1.0.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -2231,13 +1912,9 @@ google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ6
google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220111164026-67b88f271998/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/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/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
@@ -2274,10 +1951,8 @@ google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
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.44.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/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=
@@ -2293,13 +1968,10 @@ 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=
-gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
-gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -2314,19 +1986,14 @@ 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/mail.v2 v2.3.1 h1:WYFn/oANrAGP2C0dcV6/pbkPzv8yGzqTjPmTeO7qoXk=
-gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw=
-gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -2408,81 +2075,14 @@ 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/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/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=
modernc.org/db v1.0.0/go.mod h1:kYD/cO29L/29RM0hXYl4i3+Q5VojL31kTUVpVJDw0s8=
@@ -2495,57 +2095,6 @@ 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=
@@ -2554,14 +2103,9 @@ 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/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/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A=
@@ -2569,26 +2113,19 @@ modernc.org/opt v0.1.1/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/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/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/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=
diff --git a/mattermost-plugin/plugin.json b/mattermost-plugin/plugin.json
index b57b5f7f6..7c458cc74 100644
--- a/mattermost-plugin/plugin.json
+++ b/mattermost-plugin/plugin.json
@@ -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": {
diff --git a/mattermost-plugin/product/api_adapter.go b/mattermost-plugin/product/api_adapter.go
index 594445e64..6f680f804 100644
--- a/mattermost-plugin/product/api_adapter.go
+++ b/mattermost-plugin/product/api_adapter.go
@@ -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)
diff --git a/mattermost-plugin/server/api_adapter.go b/mattermost-plugin/server/api_adapter.go
index ac9c78542..8d192087c 100644
--- a/mattermost-plugin/server/api_adapter.go
+++ b/mattermost-plugin/server/api_adapter.go
@@ -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)
diff --git a/mattermost-plugin/server/boards/boardsapp_util.go b/mattermost-plugin/server/boards/boardsapp_util.go
index e1a8ea986..0a2b7eaa8 100644
--- a/mattermost-plugin/server/boards/boardsapp_util.go
+++ b/mattermost-plugin/server/boards/boardsapp_util.go
@@ -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,
diff --git a/mattermost-plugin/server/boards/configuration.go b/mattermost-plugin/server/boards/configuration.go
index fcd44a5c4..63d87a9cd 100644
--- a/mattermost-plugin/server/boards/configuration.go
+++ b/mattermost-plugin/server/boards/configuration.go
@@ -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,
}
diff --git a/mattermost-plugin/server/boards/notifications.go b/mattermost-plugin/server/boards/notifications.go
index 3df9c6f07..5a860c966 100644
--- a/mattermost-plugin/server/boards/notifications.go
+++ b/mattermost-plugin/server/boards/notifications.go
@@ -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) {
diff --git a/mattermost-plugin/server/manifest.go b/mattermost-plugin/server/manifest.go
index b59304865..db79def9f 100644
--- a/mattermost-plugin/server/manifest.go
+++ b/mattermost-plugin/server/manifest.go
@@ -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": {
diff --git a/mattermost-plugin/webapp/loaders/globalScssClassLoader.js b/mattermost-plugin/webapp/loaders/globalScssClassLoader.js
index 3bccdbf98..80b58e7ad 100644
--- a/mattermost-plugin/webapp/loaders/globalScssClassLoader.js
+++ b/mattermost-plugin/webapp/loaders/globalScssClassLoader.js
@@ -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) {
diff --git a/mattermost-plugin/webapp/package-lock.json b/mattermost-plugin/webapp/package-lock.json
index abfb81bb5..75837a4ce 100644
--- a/mattermost-plugin/webapp/package-lock.json
+++ b/mattermost-plugin/webapp/package-lock.json
@@ -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",
diff --git a/mattermost-plugin/webapp/package.json b/mattermost-plugin/webapp/package.json
index d9f6645ff..d992f424f 100644
--- a/mattermost-plugin/webapp/package.json
+++ b/mattermost-plugin/webapp/package.json
@@ -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",
diff --git a/mattermost-plugin/webapp/src/components/__snapshots__/boardSelector.test.tsx.snap b/mattermost-plugin/webapp/src/components/__snapshots__/boardSelector.test.tsx.snap
index c601e4124..c36ec5048 100644
--- a/mattermost-plugin/webapp/src/components/__snapshots__/boardSelector.test.tsx.snap
+++ b/mattermost-plugin/webapp/src/components/__snapshots__/boardSelector.test.tsx.snap
@@ -6,7 +6,7 @@ exports[`components/boardSelector escape button should unmount the component 1`]
class="focalboard-body"
>
+
+
+
+
+
+
+`;
diff --git a/mattermost-plugin/webapp/src/components/createBoardFromTemplate.scss b/mattermost-plugin/webapp/src/components/createBoardFromTemplate.scss
new file mode 100644
index 000000000..3b85588a7
--- /dev/null
+++ b/mattermost-plugin/webapp/src/components/createBoardFromTemplate.scss
@@ -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);
+ }
+ }
+}
diff --git a/mattermost-plugin/webapp/src/components/createBoardFromTemplate.test.tsx b/mattermost-plugin/webapp/src/components/createBoardFromTemplate.test.tsx
new file mode 100644
index 000000000..ead4d012d
--- /dev/null
+++ b/mattermost-plugin/webapp/src/components/createBoardFromTemplate.test.tsx
@@ -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 = (
)
+
+ await act(async () => {
+ const result = render(wrapIntl(
+
+
+
+ ))
+ 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 = (
)
+
+ await act(async () => {
+ render(wrapIntl(
+
+
+
+ ))
+ })
+
+ // 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()
+ })
+
+ })
+})
diff --git a/mattermost-plugin/webapp/src/components/createBoardFromTemplate.tsx b/mattermost-plugin/webapp/src/components/createBoardFromTemplate.tsx
new file mode 100644
index 000000000..af9c81560
--- /dev/null
+++ b/mattermost-plugin/webapp/src/components/createBoardFromTemplate.tsx
@@ -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
) => 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('')
+
+ 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) => {
+ const cssPrefix = 'CreateBoardFromTemplate--templates-selector__menu-portal__option'
+
+ const descriptionLabel = description ? getSubstringWithCompleteWords(description, TEMPLATE_DESCRIPTION_LENGTH) : 'ㅤ'
+
+ const templateDescription = (
+
+ {descriptionLabel}
+
+ )
+
+ // 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 (
+
+
+ {icon || }
+
+
+ {title}
+
+ {!selectedOption && templateDescription}
+
+ )
+ }
+
+ const CustomValueContainer = ({ children, ...props }: any) => {
+ return (
+
+
+ {props.selectProps.placeholder}
+
+ {React.Children.map(children, (child) =>
+ child && child.type !== Placeholder ? child : null
+ )}
+
+ )
+ }
+
+ 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) => {
+ 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>): 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 (
+
+
+
+ {addBoard &&
+
}
+
+
+ )
+}
+
+const IntlCreateBoardFromTemplate = (props: Props) => {
+ const language = useAppSelector(getLanguage)
+ return (
+
+
+
+ )
+}
+
+export default IntlCreateBoardFromTemplate
diff --git a/mattermost-plugin/webapp/src/index.tsx b/mattermost-plugin/webapp/src/index.tsx
index 7d04cb7f2..46024ada6 100644
--- a/mattermost-plugin/webapp/src/index.tsx
+++ b/mattermost-plugin/webapp/src/index.tsx
@@ -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) => void,
+ newBoardInfoIcon: React.ReactNode,
+ }) => (
+
+
+
+ ))
+ }
+
this.registry.registerPostWillRenderEmbedComponent(
(embed) => embed.type === 'boards',
(props: {embed: {data: string}, webSocketClient: MMWebSocketClient}) => (
diff --git a/mattermost-plugin/webapp/src/types/mattermost-webapp/index.d.ts b/mattermost-plugin/webapp/src/types/mattermost-webapp/index.d.ts
index 74959ebde..4ec28e485 100644
--- a/mattermost-plugin/webapp/src/types/mattermost-webapp/index.d.ts
+++ b/mattermost-plugin/webapp/src/types/mattermost-webapp/index.d.ts
@@ -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
}
diff --git a/mattermost-plugin/webapp/webpack.config.js b/mattermost-plugin/webapp/webpack.config.js
index 6425b3142..023369dc2 100644
--- a/mattermost-plugin/webapp/webpack.config.js
+++ b/mattermost-plugin/webapp/webpack.config.js
@@ -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/');
diff --git a/server/.golangci.yml b/server/.golangci.yml
index c1381759f..23f4fce36 100644
--- a/server/.golangci.yml
+++ b/server/.golangci.yml
@@ -27,10 +27,7 @@ linters:
enable:
- gofmt
- goimports
- - deadcode
- ineffassign
- - structcheck
- - varcheck
- unparam
- errcheck
- govet
diff --git a/server/api/api.go b/server/api/api.go
index da87f0b24..ec376a9c0 100644
--- a/server/api/api.go
+++ b/server/api/api.go
@@ -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
diff --git a/server/api/archive.go b/server/api/archive.go
index da56350a4..c1856c7d8 100644
--- a/server/api/archive.go
+++ b/server/api/archive.go
@@ -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)
diff --git a/server/api/audit.go b/server/api/audit.go
index dba563ffb..875f5c28c 100644
--- a/server/api/audit.go
+++ b/server/api/audit.go
@@ -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
diff --git a/server/api/auth.go b/server/api/auth.go
index dffe86f18..b81660ab2 100644
--- a/server/api/auth.go
+++ b/server/api/auth.go
@@ -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
}
diff --git a/server/api/categories.go b/server/api/categories.go
index 9181c42d5..e4f9cf0fe 100644
--- a/server/api/categories.go
+++ b/server/api/categories.go
@@ -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()
+}
diff --git a/server/api/compliance.go b/server/api/compliance.go
new file mode 100644
index 000000000..f5135c6e5
--- /dev/null
+++ b/server/api/compliance.go
@@ -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)
+}
diff --git a/server/api/members.go b/server/api/members.go
index 3b5dff592..2014b84f3 100644
--- a/server/api/members.go
+++ b/server/api/members.go
@@ -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
}
diff --git a/server/app/app.go b/server/app/app.go
index 4825c9882..3ba7f2c3a 100644
--- a/server/app/app.go
+++ b/server/app/app.go
@@ -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()
+}
diff --git a/server/app/boards.go b/server/app/boards.go
index f9ea5085a..479926447 100644
--- a/server/app/boards.go
+++ b/server/app/boards.go
@@ -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
}
diff --git a/server/app/boards_test.go b/server/app/boards_test.go
index e41e30fea..51bb88296 100644
--- a/server/app/boards_test.go
+++ b/server/app/boards_test.go
@@ -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)
diff --git a/server/app/category.go b/server/app/category.go
index 6fbd0bb10..f91591762 100644
--- a/server/app/category.go
+++ b/server/app/category.go
@@ -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)
}
diff --git a/server/app/category_boards.go b/server/app/category_boards.go
index 79728716f..e8a5cca09 100644
--- a/server/app/category_boards.go
+++ b/server/app/category_boards.go
@@ -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
+}
diff --git a/server/app/category_boards_test.go b/server/app/category_boards_test.go
index e495a8ff2..1ff0c2bf1 100644
--- a/server/app/category_boards_test.go
+++ b/server/app/category_boards_test.go
@@ -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)
diff --git a/server/app/category_test.go b/server/app/category_test.go
index 34588aad5..04b1bade0 100644
--- a/server/app/category_test.go
+++ b/server/app/category_test.go
@@ -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)
diff --git a/server/app/compliance.go b/server/app/compliance.go
new file mode 100644
index 000000000..6f37707de
--- /dev/null
+++ b/server/app/compliance.go
@@ -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)
+}
diff --git a/server/app/export.go b/server/app/export.go
index fd74397b5..81cf07e71 100644
--- a/server/app/export.go
+++ b/server/app/export.go
@@ -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)
diff --git a/server/app/import.go b/server/app/import.go
index eab0d24bd..6dc68d9c9 100644
--- a/server/app/import.go
+++ b/server/app/import.go
@@ -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)
}
}
}
diff --git a/server/app/import_test.go b/server/app/import_test.go
index 6ec7e63de..4755f8254 100644
--- a/server/app/import_test.go
+++ b/server/app/import_test.go
@@ -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}}
+`
diff --git a/server/app/insights.go b/server/app/insights.go
index 28027d54d..9179a86c2 100644
--- a/server/app/insights.go
+++ b/server/app/insights.go
@@ -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) {
diff --git a/server/app/insights_test.go b/server/app/insights_test.go
index 9a419c79e..c6897ff24 100644
--- a/server/app/insights_test.go
+++ b/server/app/insights_test.go
@@ -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)
diff --git a/server/app/onboarding_test.go b/server/app/onboarding_test.go
index 29c2597e2..90a77170d 100644
--- a/server/app/onboarding_test.go
+++ b/server/app/onboarding_test.go
@@ -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)
diff --git a/server/assets/assets.go b/server/assets/assets.go
index 4eeecefb9..b558d4e80 100644
--- a/server/assets/assets.go
+++ b/server/assets/assets.go
@@ -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
diff --git a/server/client/client.go b/server/client/client.go
index 00923b7fd..6be594f40 100644
--- a/server/client/client.go
+++ b/server/client/client.go
@@ -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)
+}
diff --git a/server/go.mod b/server/go.mod
index 6d34056ef..43d89004f 100644
--- a/server/go.mod
+++ b/server/go.mod
@@ -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
)
diff --git a/server/go.sum b/server/go.sum
index 804c77180..a039930ec 100644
--- a/server/go.sum
+++ b/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=
diff --git a/server/go.tools.mod b/server/go.tools.mod
index 32450070e..ee3a74487 100644
--- a/server/go.tools.mod
+++ b/server/go.tools.mod
@@ -1,6 +1,6 @@
module github.com/mattermost/focalboard/server
-go 1.18
+go 1.19
require github.com/golang/mock v1.6.0
diff --git a/server/integrationtests/board_test.go b/server/integrationtests/board_test.go
index b1cf9928e..500ccfd9d 100644
--- a/server/integrationtests/board_test.go
+++ b/server/integrationtests/board_test.go
@@ -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
}
}
diff --git a/server/integrationtests/cards_test.go b/server/integrationtests/cards_test.go
index 8cd51fd59..a6ec5f7d8 100644
--- a/server/integrationtests/cards_test.go
+++ b/server/integrationtests/cards_test.go
@@ -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-- {
diff --git a/server/integrationtests/clienttestlib.go b/server/integrationtests/clienttestlib.go
index 46607e38c..5cbe18bb9 100644
--- a/server/integrationtests/clienttestlib.go
+++ b/server/integrationtests/clienttestlib.go
@@ -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)
diff --git a/server/integrationtests/compliance_test.go b/server/integrationtests/compliance_test.go
new file mode 100644
index 000000000..e0f8fd678
--- /dev/null
+++ b/server/integrationtests/compliance_test.go
@@ -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)
+ })
+}
diff --git a/server/integrationtests/permissions_test.go b/server/integrationtests/permissions_test.go
index 7f5bfb095..6cea0e1d8 100644
--- a/server/integrationtests/permissions_test.go
+++ b/server/integrationtests/permissions_test.go
@@ -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)
})
}
diff --git a/server/integrationtests/pluginteststore.go b/server/integrationtests/pluginteststore.go
index 7e0ec42d9..dce822099 100644
--- a/server/integrationtests/pluginteststore.go
+++ b/server/integrationtests/pluginteststore.go
@@ -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
+}
diff --git a/server/integrationtests/sidebar_test.go b/server/integrationtests/sidebar_test.go
index 976f1aa9c..c251b2f8b 100644
--- a/server/integrationtests/sidebar_test.go
+++ b/server/integrationtests/sidebar_test.go
@@ -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)
}
diff --git a/server/integrationtests/work_template_test.go b/server/integrationtests/work_template_test.go
new file mode 100644
index 000000000..a26d2d289
--- /dev/null
+++ b/server/integrationtests/work_template_test.go
@@ -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)
+ }
+}
diff --git a/server/main/main.go b/server/main/main.go
index ba7298f91..31eaa7d14 100644
--- a/server/main/main.go
+++ b/server/main/main.go
@@ -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()
diff --git a/server/model/block.go b/server/model/block.go
index 341e9b67a..9e1658ec6 100644
--- a/server/model/block.go
+++ b/server/model/block.go
@@ -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 = ""
diff --git a/server/model/blocktype.go b/server/model/blocktype.go
index 6c6c35444..0c42349a9 100644
--- a/server/model/blocktype.go
+++ b/server/model/blocktype.go
@@ -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
}
diff --git a/server/model/category_boards.go b/server/model/category_boards.go
index 81cd568a5..b01ee0d11 100644
--- a/server/model/category_boards.go
+++ b/server/model/category_boards.go
@@ -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"`
}
diff --git a/server/model/compliance.go b/server/model/compliance.go
new file mode 100644
index 000000000..7ecf3190a
--- /dev/null
+++ b/server/model/compliance.go
@@ -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)
+}
diff --git a/server/model/mocks/mockservicesapi.go b/server/model/mocks/mockservicesapi.go
index 819ac5d6d..07d1daf98 100644
--- a/server/model/mocks/mockservicesapi.go
+++ b/server/model/mocks/mockservicesapi.go
@@ -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()
diff --git a/server/model/mocks/propValueResolverMock.go b/server/model/mocks/propValueResolverMock.go
new file mode 100644
index 000000000..1846269dc
--- /dev/null
+++ b/server/model/mocks/propValueResolverMock.go
@@ -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)
+}
diff --git a/server/model/properties.go b/server/model/properties.go
index 8bb478ce3..1c8c764be 100644
--- a/server/model/properties.go
+++ b/server/model/properties.go
@@ -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{})
diff --git a/server/model/properties_test.go b/server/model/properties_test.go
index 070e21705..e0a9bf5cb 100644
--- a/server/model/properties_test.go
+++ b/server/model/properties_test.go
@@ -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 = `[
{
diff --git a/server/model/services_api.go b/server/model/services_api.go
index da2e63771..c9920e0da 100644
--- a/server/model/services_api.go
+++ b/server/model/services_api.go
@@ -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)
diff --git a/server/model/version.go b/server/model/version.go
index 64c6bad80..cace1f357 100644
--- a/server/model/version.go
+++ b/server/model/version.go
@@ -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),
diff --git a/server/server/server.go b/server/server/server.go
index fa72b42fa..b7e939c1c 100644
--- a/server/server/server.go
+++ b/server/server/server.go
@@ -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,
}
diff --git a/server/services/audit/audit.go b/server/services/audit/audit.go
index 51b196b1e..cfb240097 100644
--- a/server/services/audit/audit.go
+++ b/server/services/audit/audit.go
@@ -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)
}
diff --git a/server/services/metrics/service.go b/server/services/metrics/service.go
index 5345e1f03..415da04b6 100644
--- a/server/services/metrics/service.go
+++ b/server/services/metrics/service.go
@@ -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),
diff --git a/server/services/notify/notifysubscriptions/app_api.go b/server/services/notify/notifysubscriptions/app_api.go
index a822e9c3d..9f2a25b50 100644
--- a/server/services/notify/notifysubscriptions/app_api.go
+++ b/server/services/notify/notifysubscriptions/app_api.go
@@ -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)
diff --git a/server/services/notify/notifysubscriptions/diff.go b/server/services/notify/notifysubscriptions/diff.go
index ecabe23e5..e96b98525 100644
--- a/server/services/notify/notifysubscriptions/diff.go
+++ b/server/services/notify/notifysubscriptions/diff.go
@@ -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)
}
diff --git a/server/services/notify/notifysubscriptions/diff2slackattachments.go b/server/services/notify/notifysubscriptions/diff2slackattachments.go
index 2afd23d09..31c66768b 100644
--- a/server/services/notify/notifysubscriptions/diff2slackattachments.go
+++ b/server/services/notify/notifysubscriptions/diff2slackattachments.go
@@ -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
}
diff --git a/server/services/notify/notifysubscriptions/notifier.go b/server/services/notify/notifysubscriptions/notifier.go
index 6b0d26761..a8deae7f5 100644
--- a/server/services/notify/notifysubscriptions/notifier.go
+++ b/server/services/notify/notifysubscriptions/notifier.go
@@ -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))
},
diff --git a/server/services/notify/notifysubscriptions/subscriptions_backend.go b/server/services/notify/notifysubscriptions/subscriptions_backend.go
index baf66e728..1323c5c3c 100644
--- a/server/services/notify/notifysubscriptions/subscriptions_backend.go
+++ b/server/services/notify/notifysubscriptions/subscriptions_backend.go
@@ -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)
diff --git a/server/services/notify/plugindelivery/plugin_delivery.go b/server/services/notify/plugindelivery/plugin_delivery.go
index db16e1ee8..aa01ec3cb 100644
--- a/server/services/notify/plugindelivery/plugin_delivery.go
+++ b/server/services/notify/plugindelivery/plugin_delivery.go
@@ -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)
diff --git a/server/services/notify/plugindelivery/subscription_deliver.go b/server/services/notify/plugindelivery/subscription_deliver.go
index 5672369b5..ff2f18720 100644
--- a/server/services/notify/plugindelivery/subscription_deliver.go
+++ b/server/services/notify/plugindelivery/subscription_deliver.go
@@ -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)
}
diff --git a/server/services/notify/plugindelivery/user_test.go b/server/services/notify/plugindelivery/user_test.go
index 2f92d3fb8..e0627439b 100644
--- a/server/services/notify/plugindelivery/user_test.go
+++ b/server/services/notify/plugindelivery/user_test.go
@@ -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
}
diff --git a/server/services/store/generators/main.go b/server/services/store/generators/main.go
index 4f7fafd7c..02fde8335 100644
--- a/server/services/store/generators/main.go
+++ b/server/services/store/generators/main.go
@@ -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 {
diff --git a/server/services/store/mattermostauthlayer/mattermostauthlayer.go b/server/services/store/mattermostauthlayer/mattermostauthlayer.go
index f7dda8185..2696b382c 100644
--- a/server/services/store/mattermostauthlayer/mattermostauthlayer.go
+++ b/server/services/store/mattermostauthlayer/mattermostauthlayer.go
@@ -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 {
diff --git a/server/services/store/mockstore/mockstore.go b/server/services/store/mockstore/mockstore.go
index 63813e7a7..d21cc4bff 100644
--- a/server/services/store/mockstore/mockstore.go
+++ b/server/services/store/mockstore/mockstore.go
@@ -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()
diff --git a/server/services/store/sqlstore/blocks.go b/server/services/store/sqlstore/blocks.go
index 361128eca..69b650af6 100644
--- a/server/services/store/sqlstore/blocks.go
+++ b/server/services/store/sqlstore/blocks.go
@@ -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) {
diff --git a/server/services/store/sqlstore/board.go b/server/services/store/sqlstore/board.go
index c81ceb411..48faa76e2 100644
--- a/server/services/store/sqlstore/board.go
+++ b/server/services/store/sqlstore/board.go
@@ -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 {
diff --git a/server/services/store/sqlstore/board_insights.go b/server/services/store/sqlstore/board_insights.go
index 73c92c9f6..8f4dad92e 100644
--- a/server/services/store/sqlstore/board_insights.go
+++ b/server/services/store/sqlstore/board_insights.go
@@ -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").
diff --git a/server/services/store/sqlstore/category_boards.go b/server/services/store/sqlstore/category_boards.go
index 6a8415c67..b1e396b66 100644
--- a/server/services/store/sqlstore/category_boards.go
+++ b/server/services/store/sqlstore/category_boards.go
@@ -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
+}
diff --git a/server/services/store/sqlstore/compliance.go b/server/services/store/sqlstore/compliance.go
new file mode 100644
index 000000000..867829444
--- /dev/null
+++ b/server/services/store/sqlstore/compliance.go
@@ -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
+}
diff --git a/server/services/store/sqlstore/migrate.go b/server/services/store/sqlstore/migrate.go
index 6c95481ac..afd9463ac 100644
--- a/server/services/store/sqlstore/migrate.go
+++ b/server/services/store/sqlstore/migrate.go
@@ -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
diff --git a/server/services/store/sqlstore/migrations/000033_remove_deleted_category_boards.down.sql b/server/services/store/sqlstore/migrations/000033_remove_deleted_category_boards.down.sql
new file mode 100644
index 000000000..027b7d63f
--- /dev/null
+++ b/server/services/store/sqlstore/migrations/000033_remove_deleted_category_boards.down.sql
@@ -0,0 +1 @@
+SELECT 1;
\ No newline at end of file
diff --git a/server/services/store/sqlstore/migrations/000033_remove_deleted_category_boards.up.sql b/server/services/store/sqlstore/migrations/000033_remove_deleted_category_boards.up.sql
new file mode 100644
index 000000000..1714ace0c
--- /dev/null
+++ b/server/services/store/sqlstore/migrations/000033_remove_deleted_category_boards.up.sql
@@ -0,0 +1 @@
+DELETE FROM {{.prefix}}category_boards WHERE delete_at > 0;
\ No newline at end of file
diff --git a/server/services/store/sqlstore/migrations/000034_category_boards_remove_unused_delete_at_column.down.sql b/server/services/store/sqlstore/migrations/000034_category_boards_remove_unused_delete_at_column.down.sql
new file mode 100644
index 000000000..027b7d63f
--- /dev/null
+++ b/server/services/store/sqlstore/migrations/000034_category_boards_remove_unused_delete_at_column.down.sql
@@ -0,0 +1 @@
+SELECT 1;
\ No newline at end of file
diff --git a/server/services/store/sqlstore/migrations/000034_category_boards_remove_unused_delete_at_column.up.sql b/server/services/store/sqlstore/migrations/000034_category_boards_remove_unused_delete_at_column.up.sql
new file mode 100644
index 000000000..215b9c762
--- /dev/null
+++ b/server/services/store/sqlstore/migrations/000034_category_boards_remove_unused_delete_at_column.up.sql
@@ -0,0 +1,3 @@
+{{ if or .postgres .mysql }}
+ {{ dropColumnIfNeeded "category_boards" "delete_at" }}
+{{end}}
\ No newline at end of file
diff --git a/server/services/store/sqlstore/migrations/000035_add_hidden_board_column.down.sql b/server/services/store/sqlstore/migrations/000035_add_hidden_board_column.down.sql
new file mode 100644
index 000000000..027b7d63f
--- /dev/null
+++ b/server/services/store/sqlstore/migrations/000035_add_hidden_board_column.down.sql
@@ -0,0 +1 @@
+SELECT 1;
\ No newline at end of file
diff --git a/server/services/store/sqlstore/migrations/000035_add_hidden_board_column.up.sql b/server/services/store/sqlstore/migrations/000035_add_hidden_board_column.up.sql
new file mode 100644
index 000000000..47140be28
--- /dev/null
+++ b/server/services/store/sqlstore/migrations/000035_add_hidden_board_column.up.sql
@@ -0,0 +1 @@
+{{ addColumnIfNeeded "category_boards" "hidden" "boolean" "" }}
\ No newline at end of file
diff --git a/server/services/store/sqlstore/migrations/000036_category_board_add_unique_constraint.down.sql b/server/services/store/sqlstore/migrations/000036_category_board_add_unique_constraint.down.sql
new file mode 100644
index 000000000..027b7d63f
--- /dev/null
+++ b/server/services/store/sqlstore/migrations/000036_category_board_add_unique_constraint.down.sql
@@ -0,0 +1 @@
+SELECT 1;
\ No newline at end of file
diff --git a/server/services/store/sqlstore/migrations/000036_category_board_add_unique_constraint.up.sql b/server/services/store/sqlstore/migrations/000036_category_board_add_unique_constraint.up.sql
new file mode 100644
index 000000000..8858033b0
--- /dev/null
+++ b/server/services/store/sqlstore/migrations/000036_category_board_add_unique_constraint.up.sql
@@ -0,0 +1,26 @@
+{{if or .mysql .postgres}}
+{{ addConstraintIfNeeded "category_boards" "unique_user_category_board" "UNIQUE" "UNIQUE(user_id, board_id)"}}
+{{end}}
+
+{{if .sqlite}}
+ ALTER TABLE {{.prefix}}category_boards RENAME TO {{.prefix}}category_boards_old;
+
+ CREATE TABLE {{.prefix}}category_boards (
+ id varchar(36) NOT NULL,
+ user_id varchar(36) NOT NULL,
+ category_id varchar(36) NOT NULL,
+ board_id VARCHAR(36) NOT NULL,
+ create_at BIGINT,
+ update_at BIGINT,
+ sort_order BIGINT,
+ hidden boolean,
+ PRIMARY KEY (id),
+ CONSTRAINT unique_user_category_board UNIQUE (user_id, board_id)
+ );
+
+ INSERT INTO {{.prefix}}category_boards
+ (id, user_id, category_id, board_id, create_at, update_at, sort_order, hidden)
+ SELECT id, user_id, category_id, board_id, create_at, update_at, sort_order, hidden FROM {{.prefix}}category_boards_old;
+ DROP TABLE {{.prefix}}category_boards_old;
+
+{{end}}
\ No newline at end of file
diff --git a/server/services/store/sqlstore/migrations/000037_hidden_boards_from_preferences.down.sql b/server/services/store/sqlstore/migrations/000037_hidden_boards_from_preferences.down.sql
new file mode 100644
index 000000000..027b7d63f
--- /dev/null
+++ b/server/services/store/sqlstore/migrations/000037_hidden_boards_from_preferences.down.sql
@@ -0,0 +1 @@
+SELECT 1;
\ No newline at end of file
diff --git a/server/services/store/sqlstore/migrations/000037_hidden_boards_from_preferences.up.sql b/server/services/store/sqlstore/migrations/000037_hidden_boards_from_preferences.up.sql
new file mode 100644
index 000000000..b9bd56bb1
--- /dev/null
+++ b/server/services/store/sqlstore/migrations/000037_hidden_boards_from_preferences.up.sql
@@ -0,0 +1,57 @@
+{{if .plugin}}
+ {{if .mysql}}
+ UPDATE {{.prefix}}category_boards AS fcb
+ JOIN Preferences p
+ ON fcb.user_id = p.userid
+ AND p.category = 'focalboard'
+ AND p.name = 'hiddenBoardIDs'
+ SET hidden = true
+ WHERE p.value LIKE concat('%', fcb.board_id, '%');
+ {{end}}
+
+ {{if .postgres}}
+ UPDATE {{.prefix}}category_boards as fcb
+ SET hidden = true
+ FROM preferences p
+ WHERE p.userid = fcb.user_id
+ AND p.category = 'focalboard'
+ AND p.name = 'hiddenBoardIDs'
+ AND p.value like ('%' || fcb.board_id || '%');
+ {{end}}
+{{else}}
+ {{if .mysql}}
+ UPDATE {{.prefix}}category_boards AS fcb
+ JOIN {{.prefix}}preferences p
+ ON fcb.user_id = p.userid
+ AND p.category = 'focalboard'
+ AND p.name = 'hiddenBoardIDs'
+ SET hidden = true
+ WHERE p.value LIKE concat('%', fcb.board_id, '%');
+ {{end}}
+
+ {{if .postgres}}
+ UPDATE {{.prefix}}category_boards as fcb
+ SET hidden = true
+ FROM {{.prefix}}preferences p
+ WHERE p.userid = fcb.user_id
+ AND p.category = 'focalboard'
+ AND p.name = 'hiddenBoardIDs'
+ AND p.value like ('%' || fcb.board_id || '%');
+ {{end}}
+{{end}}
+
+{{if .sqlite}}
+ UPDATE {{.prefix}}category_boards
+ SET hidden = true
+ WHERE (user_id || '_' || board_id)
+ IN
+ (
+ SELECT (fcb.user_id || '_' || fcb.board_id)
+ FROM {{.prefix}}category_boards AS fcb
+ JOIN {{.prefix}}preferences p
+ ON p.userid = fcb.user_id
+ AND p.category = 'focalboard'
+ AND p.name = 'hiddenBoardIDs' WHERE
+ p.value LIKE ('%' || fcb.board_id || '%')
+ );
+{{end}}
\ No newline at end of file
diff --git a/server/services/store/sqlstore/migrations/000038_delete_hiddenBoardIDs_from_preferences.down.sql b/server/services/store/sqlstore/migrations/000038_delete_hiddenBoardIDs_from_preferences.down.sql
new file mode 100644
index 000000000..027b7d63f
--- /dev/null
+++ b/server/services/store/sqlstore/migrations/000038_delete_hiddenBoardIDs_from_preferences.down.sql
@@ -0,0 +1 @@
+SELECT 1;
\ No newline at end of file
diff --git a/server/services/store/sqlstore/migrations/000038_delete_hiddenBoardIDs_from_preferences.up.sql b/server/services/store/sqlstore/migrations/000038_delete_hiddenBoardIDs_from_preferences.up.sql
new file mode 100644
index 000000000..f86eb0c9e
--- /dev/null
+++ b/server/services/store/sqlstore/migrations/000038_delete_hiddenBoardIDs_from_preferences.up.sql
@@ -0,0 +1,5 @@
+{{if .plugin}}
+ DELETE FROM Preferences WHERE category = 'focalboard' AND name = 'hiddenBoardIDs';
+{{else}}
+ DELETE FROM {{.prefix}}preferences WHERE category = 'focalboard' AND name = 'hiddenBoardIDs';
+{{end}}
\ No newline at end of file
diff --git a/server/services/store/sqlstore/migrationstests/fixtures/test33_with_deleted_data.sql b/server/services/store/sqlstore/migrationstests/fixtures/test33_with_deleted_data.sql
new file mode 100644
index 000000000..c5593660b
--- /dev/null
+++ b/server/services/store/sqlstore/migrationstests/fixtures/test33_with_deleted_data.sql
@@ -0,0 +1,6 @@
+INSERT INTO focalboard_category_boards values
+('id-1', 'user_id-1', 'category-id-1', 'board-id-1', 1672988834402, 1672988834402, 0, 0),
+('id-2', 'user_id-1', 'category-id-2', 'board-id-1', 1672988834402, 1672988834402, 0, 0),
+('id-3', 'user_id-2', 'category-id-3', 'board-id-2', 1672988834402, 1672988834402, 1672988834402, 0),
+('id-4', 'user_id-2', 'category-id-3', 'board-id-4', 1672988834402, 1672988834402, 0, 0),
+('id-5', 'user_id-3', 'category-id-4', 'board-id-3', 1672988834402, 1672988834402, 1672988834402, 0);
\ No newline at end of file
diff --git a/server/services/store/sqlstore/migrationstests/fixtures/test33_with_no_deleted_data.sql b/server/services/store/sqlstore/migrationstests/fixtures/test33_with_no_deleted_data.sql
new file mode 100644
index 000000000..5f4337bb2
--- /dev/null
+++ b/server/services/store/sqlstore/migrationstests/fixtures/test33_with_no_deleted_data.sql
@@ -0,0 +1,6 @@
+INSERT INTO focalboard_category_boards values
+('id-1', 'user_id-1', 'category-id-1', 'board-id-1', 1672988834402, 1672988834402, 0, 0),
+('id-2', 'user_id-1', 'category-id-2', 'board-id-1', 1672988834402, 1672988834402, 0, 0),
+('id-3', 'user_id-2', 'category-id-3', 'board-id-2', 1672988834402, 1672988834402, 0, 0),
+('id-4', 'user_id-2', 'category-id-3', 'board-id-4', 1672988834402, 1672988834402, 0, 0),
+('id-5', 'user_id-3', 'category-id-4', 'board-id-3', 1672988834402, 1672988834402, 0, 0);
\ No newline at end of file
diff --git a/server/services/store/sqlstore/migrationstests/fixtures/test34_drop_delete_at_column.sql b/server/services/store/sqlstore/migrationstests/fixtures/test34_drop_delete_at_column.sql
new file mode 100644
index 000000000..4ab1ccbc8
--- /dev/null
+++ b/server/services/store/sqlstore/migrationstests/fixtures/test34_drop_delete_at_column.sql
@@ -0,0 +1 @@
+ALTER TABLE focalboard_category_boards DROP COLUMN delete_at;
\ No newline at end of file
diff --git a/server/services/store/sqlstore/migrationstests/fixtures/test35_add_hidden_column.sql b/server/services/store/sqlstore/migrationstests/fixtures/test35_add_hidden_column.sql
new file mode 100644
index 000000000..1896a0416
--- /dev/null
+++ b/server/services/store/sqlstore/migrationstests/fixtures/test35_add_hidden_column.sql
@@ -0,0 +1 @@
+ALTER TABLE focalboard_category_boards ADD COLUMN hidden boolean;
\ No newline at end of file
diff --git a/server/services/store/sqlstore/migrationstests/fixtures/test36_add_unique_constraint.sql b/server/services/store/sqlstore/migrationstests/fixtures/test36_add_unique_constraint.sql
new file mode 100644
index 000000000..e69de29bb
diff --git a/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data.sql b/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data.sql
new file mode 100644
index 000000000..8f75da315
--- /dev/null
+++ b/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data.sql
@@ -0,0 +1,10 @@
+INSERT INTO focalboard_category_boards VALUES
+('id-1', 'user-id-1', 'category-id-1', 'board-id-1', 1672889246832, 1672889246832, 0, false),
+('id-2', 'user-id-1', 'category-id-2', 'board-id-2', 1672889246832, 1672889246832, 0, false),
+('id-3', 'user-id-2', 'category-id-3', 'board-id-3', 1672889246832, 1672889246832, 0, false),
+('id-4', 'user-id-2', 'category-id-3', 'board-id-4', 1672889246832, 1672889246832, 0, false),
+('id-5', 'user-id-3', 'category-id-4', 'board-id-5', 1672889246832, 1672889246832, 0, false);
+
+INSERT INTO Preferences VALUES
+('user-id-1', 'focalboard', 'hiddenBoardIDs', '["board-id-1"]'),
+('user-id-2', 'focalboard', 'hiddenBoardIDs', '["board-id-3", "board-id-4"]');
\ No newline at end of file
diff --git a/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data_no_hidden_boards.sql b/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data_no_hidden_boards.sql
new file mode 100644
index 000000000..2986bad28
--- /dev/null
+++ b/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data_no_hidden_boards.sql
@@ -0,0 +1,6 @@
+INSERT INTO focalboard_category_boards VALUES
+('id-1', 'user-id-1', 'category-id-1', 'board-id-1', 1672889246832, 1672889246832, 0, false),
+('id-2', 'user-id-1', 'category-id-2', 'board-id-2', 1672889246832, 1672889246832, 0, false),
+('id-3', 'user-id-2', 'category-id-3', 'board-id-3', 1672889246832, 1672889246832, 0, false),
+('id-4', 'user-id-2', 'category-id-3', 'board-id-4', 1672889246832, 1672889246832, 0, false),
+('id-5', 'user-id-3', 'category-id-4', 'board-id-5', 1672889246832, 1672889246832, 0, false);
\ No newline at end of file
diff --git a/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data_preference_but_no_hidden_board.sql b/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data_preference_but_no_hidden_board.sql
new file mode 100644
index 000000000..943932e54
--- /dev/null
+++ b/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data_preference_but_no_hidden_board.sql
@@ -0,0 +1,10 @@
+INSERT INTO focalboard_category_boards VALUES
+('id-1', 'user-id-1', 'category-id-1', 'board-id-1', 1672889246832, 1672889246832, 0, false),
+('id-2', 'user-id-1', 'category-id-2', 'board-id-2', 1672889246832, 1672889246832, 0, false),
+('id-3', 'user-id-2', 'category-id-3', 'board-id-3', 1672889246832, 1672889246832, 0, false),
+('id-4', 'user-id-2', 'category-id-3', 'board-id-4', 1672889246832, 1672889246832, 0, false),
+('id-5', 'user-id-3', 'category-id-4', 'board-id-5', 1672889246832, 1672889246832, 0, false);
+
+INSERT INTO Preferences VALUES
+('user-id-1', 'focalboard', 'hiddenBoardIDs', ''),
+('user-id-2', 'focalboard', 'hiddenBoardIDs', '');
\ No newline at end of file
diff --git a/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data_sqlite.sql b/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data_sqlite.sql
new file mode 100644
index 000000000..114273c30
--- /dev/null
+++ b/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data_sqlite.sql
@@ -0,0 +1,10 @@
+INSERT INTO focalboard_category_boards VALUES
+('id-1', 'user-id-1', 'category-id-1', 'board-id-1', 1672889246832, 1672889246832, 0, false),
+('id-2', 'user-id-1', 'category-id-2', 'board-id-2', 1672889246832, 1672889246832, 0, false),
+('id-3', 'user-id-2', 'category-id-3', 'board-id-3', 1672889246832, 1672889246832, 0, false),
+('id-4', 'user-id-2', 'category-id-3', 'board-id-4', 1672889246832, 1672889246832, 0, false),
+('id-5', 'user-id-3', 'category-id-4', 'board-id-5', 1672889246832, 1672889246832, 0, false);
+
+INSERT INTO focalboard_preferences VALUES
+('user-id-1', 'focalboard', 'hiddenBoardIDs', '["board-id-1"]'),
+('user-id-2', 'focalboard', 'hiddenBoardIDs', '["board-id-3", "board-id-4"]');
\ No newline at end of file
diff --git a/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data_sqlite_preference_but_no_hidden_board.sql b/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data_sqlite_preference_but_no_hidden_board.sql
new file mode 100644
index 000000000..4b45cacdf
--- /dev/null
+++ b/server/services/store/sqlstore/migrationstests/fixtures/test37_valid_data_sqlite_preference_but_no_hidden_board.sql
@@ -0,0 +1,10 @@
+INSERT INTO focalboard_category_boards VALUES
+('id-1', 'user-id-1', 'category-id-1', 'board-id-1', 1672889246832, 1672889246832, 0, false),
+('id-2', 'user-id-1', 'category-id-2', 'board-id-2', 1672889246832, 1672889246832, 0, false),
+('id-3', 'user-id-2', 'category-id-3', 'board-id-3', 1672889246832, 1672889246832, 0, false),
+('id-4', 'user-id-2', 'category-id-3', 'board-id-4', 1672889246832, 1672889246832, 0, false),
+('id-5', 'user-id-3', 'category-id-4', 'board-id-5', 1672889246832, 1672889246832, 0, false);
+
+INSERT INTO focalboard_preferences VALUES
+('user-id-1', 'focalboard', 'hiddenBoardIDs', ''),
+('user-id-2', 'focalboard', 'hiddenBoardIDs', '');
\ No newline at end of file
diff --git a/server/services/store/sqlstore/migrationstests/fixtures/test38_add_plugin_preferences.sql b/server/services/store/sqlstore/migrationstests/fixtures/test38_add_plugin_preferences.sql
new file mode 100644
index 000000000..600be118a
--- /dev/null
+++ b/server/services/store/sqlstore/migrationstests/fixtures/test38_add_plugin_preferences.sql
@@ -0,0 +1,5 @@
+INSERT INTO Preferences VALUES
+('user-id-1', 'focalboard', 'hiddenBoardIDs', '["board-id-1"]'),
+('user-id-2', 'focalboard', 'hiddenBoardIDs', '["board-id-3", "board-id-4"]'),
+('user-id-3', 'lorem', 'lorem', ''),
+('user-id-4', 'ipsum', 'ipsum', '');
\ No newline at end of file
diff --git a/server/services/store/sqlstore/migrationstests/fixtures/test38_add_standalone_preferences.sql b/server/services/store/sqlstore/migrationstests/fixtures/test38_add_standalone_preferences.sql
new file mode 100644
index 000000000..422a967c3
--- /dev/null
+++ b/server/services/store/sqlstore/migrationstests/fixtures/test38_add_standalone_preferences.sql
@@ -0,0 +1,5 @@
+INSERT INTO focalboard_preferences VALUES
+('user-id-1', 'focalboard', 'hiddenBoardIDs', '["board-id-1"]'),
+('user-id-2', 'focalboard', 'hiddenBoardIDs', '["board-id-3", "board-id-4"]'),
+('user-id-2', 'lorem', 'lorem', ''),
+('user-id-2', 'ipsum', 'ipsum', '');
\ No newline at end of file
diff --git a/server/services/store/sqlstore/migrationstests/helpers_test.go b/server/services/store/sqlstore/migrationstests/helpers_test.go
index 673ff3bff..b3356f9d4 100644
--- a/server/services/store/sqlstore/migrationstests/helpers_test.go
+++ b/server/services/store/sqlstore/migrationstests/helpers_test.go
@@ -15,6 +15,18 @@ type TestHelper struct {
isPlugin bool
}
+func (th *TestHelper) IsPostgres() bool {
+ return th.f.DB().DriverName() == "postgres"
+}
+
+func (th *TestHelper) IsMySQL() bool {
+ return th.f.DB().DriverName() == "mysql"
+}
+
+func (th *TestHelper) IsSQLite() bool {
+ return th.f.DB().DriverName() == "sqlite3"
+}
+
func SetupPluginTestHelper(t *testing.T) (*TestHelper, func()) {
dbType := strings.TrimSpace(os.Getenv("FOCALBOARD_STORE_TEST_DB_TYPE"))
if dbType == "" || dbType == model.SqliteDBType {
diff --git a/server/services/store/sqlstore/migrationstests/migrate_34_test.go b/server/services/store/sqlstore/migrationstests/migrate_34_test.go
new file mode 100644
index 000000000..5e82386d1
--- /dev/null
+++ b/server/services/store/sqlstore/migrationstests/migrate_34_test.go
@@ -0,0 +1,57 @@
+package migrationstests
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func Test34DropDeleteAtColumnMySQLPostgres(t *testing.T) {
+ t.Run("column exists", func(t *testing.T) {
+ th, tearDown := SetupTestHelper(t)
+ defer tearDown()
+
+ th.f.MigrateToStep(34)
+
+ // migration 34 only works for MySQL and PostgreSQL
+ if th.IsMySQL() {
+ var count int
+ query := "SELECT COUNT(column_name) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = 'focalboard_category_boards' AND column_name = 'delete_at'"
+ th.f.DB().Get(&count, query)
+ require.Equal(t, 0, count)
+ } else if th.IsPostgres() {
+ var count int
+ query := "select count(*) from information_schema.columns where table_name = 'focalboard_category_boards' and column_name = 'delete_at'"
+ th.f.DB().Get(&count, query)
+ require.Equal(t, 0, count)
+ }
+ })
+
+ t.Run("column already deleted", func(t *testing.T) {
+ th, tearDown := SetupTestHelper(t)
+ defer tearDown()
+
+ // For migration 34, we don't drop column
+ // on SQLite, so no need to test for it.
+ if th.IsSQLite() {
+ return
+ }
+
+ th.f.MigrateToStep(33).
+ ExecFile("./fixtures/test34_drop_delete_at_column.sql")
+
+ th.f.MigrateToStep(34)
+
+ if th.IsMySQL() {
+ var count int
+ query := "SELECT COUNT(column_name) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = 'focalboard_category_boards' AND column_name = 'delete_at'"
+ th.f.DB().Get(&count, query)
+ require.Equal(t, 0, count)
+ } else if th.IsPostgres() {
+ var count int
+ query := "select count(*) from information_schema.columns where table_name = 'focalboard_category_boards' and column_name = 'delete_at'"
+ th.f.DB().Get(&count, query)
+ require.Equal(t, 0, count)
+ }
+ })
+}
diff --git a/server/services/store/sqlstore/migrationstests/migration35_test.go b/server/services/store/sqlstore/migrationstests/migration35_test.go
new file mode 100644
index 000000000..685478010
--- /dev/null
+++ b/server/services/store/sqlstore/migrationstests/migration35_test.go
@@ -0,0 +1,27 @@
+package migrationstests
+
+import "testing"
+
+func Test35AddHIddenColumnToCategoryBoards(t *testing.T) {
+ t.Run("base case - column doesn't already exist", func(t *testing.T) {
+ th, tearDown := SetupTestHelper(t)
+ defer tearDown()
+ th.f.MigrateToStep(35)
+ })
+
+ t.Run("column already exist", func(t *testing.T) {
+ th, tearDown := SetupTestHelper(t)
+ defer tearDown()
+
+ // We don't support adding column in idempotent manner
+ // for SQLite, so no need to check for it.
+ if th.IsSQLite() {
+ return
+ }
+
+ th.f.MigrateToStep(34).
+ ExecFile("./fixtures/test35_add_hidden_column.sql")
+
+ th.f.MigrateToStep(35)
+ })
+}
diff --git a/server/services/store/sqlstore/migrationstests/migration36_test.go b/server/services/store/sqlstore/migrationstests/migration36_test.go
new file mode 100644
index 000000000..5d1af8bb5
--- /dev/null
+++ b/server/services/store/sqlstore/migrationstests/migration36_test.go
@@ -0,0 +1,69 @@
+package migrationstests
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func Test36AddUniqueConstraintToCategoryBoards(t *testing.T) {
+ t.Run("constraint doesn't alreadt exists", func(t *testing.T) {
+ th, tearDown := SetupTestHelper(t)
+ defer tearDown()
+
+ th.f.MigrateToStep(36)
+
+ // verifying if constraint has been added
+
+ //can't verify in sqlite, so skipping it
+ if th.IsSQLite() {
+ return
+ }
+
+ var count int
+ query := "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS " +
+ "WHERE constraint_name = 'unique_user_category_board' " +
+ "AND constraint_type = 'UNIQUE' " +
+ "AND table_name = 'focalboard_category_boards'"
+ th.f.DB().Get(&count, query)
+
+ require.Equal(t, 1, count)
+ })
+
+ t.Run("constraint already exists", func(t *testing.T) {
+ th, tearDown := SetupTestHelper(t)
+ defer tearDown()
+
+ // SQLIte doesn't support adding constraint to existing table
+ // and neither do we, so skipping for sqlite
+ if th.IsSQLite() {
+ return
+ }
+
+ th.f.MigrateToStep(35)
+
+ if th.IsMySQL() {
+ th.f.DB().Exec("alter table focalboard_category_boards add constraint unique_user_category_board UNIQUE(user_id, board_id);")
+ } else if th.IsPostgres() {
+ th.f.DB().Exec("ALTER TABLE focalboard_category_boards ADD CONSTRAINT unique_user_category_board UNIQUE(user_id, board_id);")
+ }
+
+ th.f.MigrateToStep(36)
+
+ var schema string
+ if th.IsMySQL() {
+ schema = "DATABASE()"
+ } else if th.IsPostgres() {
+ schema = "'public'"
+ }
+
+ var count int
+ query := "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS " +
+ "WHERE constraint_schema = " + schema + " " +
+ "AND constraint_name = 'unique_user_category_board' " +
+ "AND constraint_type = 'UNIQUE' " +
+ "AND table_name = 'focalboard_category_boards'"
+ th.f.DB().Get(&count, query)
+ require.Equal(t, 1, count)
+ })
+}
diff --git a/server/services/store/sqlstore/migrationstests/migration37_test.go b/server/services/store/sqlstore/migrationstests/migration37_test.go
new file mode 100644
index 000000000..9958c257b
--- /dev/null
+++ b/server/services/store/sqlstore/migrationstests/migration37_test.go
@@ -0,0 +1,134 @@
+package migrationstests
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func Test37MigrateHiddenBoardIDTest(t *testing.T) {
+ t.Run("no existing hidden boards exist", func(t *testing.T) {
+ th, tearDown := SetupTestHelper(t)
+ defer tearDown()
+ th.f.MigrateToStep(37)
+ })
+
+ t.Run("SQLite - existsing category boards with some hidden boards", func(t *testing.T) {
+ th, tearDown := SetupTestHelper(t)
+ defer tearDown()
+
+ if th.IsMySQL() || th.IsPostgres() {
+ return
+ }
+
+ th.f.MigrateToStep(36).
+ ExecFile("./fixtures/test37_valid_data_sqlite.sql")
+
+ th.f.MigrateToStep(37)
+
+ type categoryBoard struct {
+ User_ID string
+ Category_ID string
+ Board_ID string
+ Hidden bool
+ }
+
+ var hiddenCategoryBoards []categoryBoard
+
+ query := "SELECT user_id, category_id, board_id, hidden FROM focalboard_category_boards WHERE hidden = true"
+ err := th.f.DB().Select(&hiddenCategoryBoards, query)
+ require.NoError(t, err)
+ require.Equal(t, 3, len(hiddenCategoryBoards))
+ require.Contains(t, hiddenCategoryBoards, categoryBoard{User_ID: "user-id-1", Category_ID: "category-id-1", Board_ID: "board-id-1", Hidden: true})
+ require.Contains(t, hiddenCategoryBoards, categoryBoard{User_ID: "user-id-2", Category_ID: "category-id-3", Board_ID: "board-id-3", Hidden: true})
+ require.Contains(t, hiddenCategoryBoards, categoryBoard{User_ID: "user-id-2", Category_ID: "category-id-3", Board_ID: "board-id-4", Hidden: true})
+ })
+
+ t.Run("MySQL and PostgreSQL - existsing category boards with some hidden boards", func(t *testing.T) {
+ th, tearDown := SetupPluginTestHelper(t)
+ defer tearDown()
+
+ if th.IsSQLite() {
+ return
+ }
+
+ th.f.MigrateToStep(36).
+ ExecFile("./fixtures/test37_valid_data.sql")
+
+ th.f.MigrateToStep(37)
+
+ type categoryBoard struct {
+ User_ID string
+ Category_ID string
+ Board_ID string
+ Hidden bool
+ }
+
+ var hiddenCategoryBoards []categoryBoard
+
+ query := "SELECT user_id, category_id, board_id, hidden FROM focalboard_category_boards WHERE hidden = true"
+ err := th.f.DB().Select(&hiddenCategoryBoards, query)
+ require.NoError(t, err)
+ require.Equal(t, 3, len(hiddenCategoryBoards))
+ require.Contains(t, hiddenCategoryBoards, categoryBoard{User_ID: "user-id-1", Category_ID: "category-id-1", Board_ID: "board-id-1", Hidden: true})
+ require.Contains(t, hiddenCategoryBoards, categoryBoard{User_ID: "user-id-2", Category_ID: "category-id-3", Board_ID: "board-id-3", Hidden: true})
+ require.Contains(t, hiddenCategoryBoards, categoryBoard{User_ID: "user-id-2", Category_ID: "category-id-3", Board_ID: "board-id-4", Hidden: true})
+ })
+
+ t.Run("no hidden boards", func(t *testing.T) {
+ th, tearDown := SetupPluginTestHelper(t)
+ defer tearDown()
+
+ th.f.MigrateToStep(36).
+ ExecFile("./fixtures/test37_valid_data_no_hidden_boards.sql")
+
+ th.f.MigrateToStep(37)
+
+ var count int
+ query := "SELECT count(*) FROM focalboard_category_boards WHERE hidden = true"
+ err := th.f.DB().Get(&count, query)
+ require.NoError(t, err)
+ require.Equal(t, 0, count)
+ })
+
+ t.Run("SQLite - preference but no hidden board", func(t *testing.T) {
+ th, tearDown := SetupTestHelper(t)
+ defer tearDown()
+
+ if th.IsMySQL() || th.IsPostgres() {
+ return
+ }
+
+ th.f.MigrateToStep(36).
+ ExecFile("./fixtures/test37_valid_data_sqlite_preference_but_no_hidden_board.sql")
+
+ th.f.MigrateToStep(37)
+
+ var count int
+ query := "SELECT count(*) FROM focalboard_category_boards WHERE hidden = true"
+ err := th.f.DB().Get(&count, query)
+ require.NoError(t, err)
+ require.Equal(t, 0, count)
+ })
+
+ t.Run("MySQL and PostgreSQL - preference but no hidden board", func(t *testing.T) {
+ th, tearDown := SetupPluginTestHelper(t)
+ defer tearDown()
+
+ if th.IsSQLite() {
+ return
+ }
+
+ th.f.MigrateToStep(36).
+ ExecFile("./fixtures/test37_valid_data_preference_but_no_hidden_board.sql")
+
+ th.f.MigrateToStep(37)
+
+ var count int
+ query := "SELECT count(*) FROM focalboard_category_boards WHERE hidden = true"
+ err := th.f.DB().Get(&count, query)
+ require.NoError(t, err)
+ require.Equal(t, 0, count)
+ })
+
+}
diff --git a/server/services/store/sqlstore/migrationstests/migration38_test.go b/server/services/store/sqlstore/migrationstests/migration38_test.go
new file mode 100644
index 000000000..d58f73bff
--- /dev/null
+++ b/server/services/store/sqlstore/migrationstests/migration38_test.go
@@ -0,0 +1,63 @@
+package migrationstests
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func Test38RemoveHiddenBoardIDsFromPreferences(t *testing.T) {
+ t.Run("standalone - no data exist", func(t *testing.T) {
+ th, tearDown := SetupTestHelper(t)
+ defer tearDown()
+ th.f.MigrateToStep(38)
+ })
+
+ t.Run("plugin - no data exist", func(t *testing.T) {
+ th, tearDown := SetupTestHelper(t)
+ defer tearDown()
+ th.f.MigrateToStep(38)
+ })
+
+ t.Run("standalone - some data exist", func(t *testing.T) {
+ th, tearDown := SetupTestHelper(t)
+ defer tearDown()
+ th.f.MigrateToStep(37).
+ ExecFile("./fixtures/test38_add_standalone_preferences.sql")
+
+ // verify existing data count
+ var count int
+ countQuery := "SELECT COUNT(*) FROM focalboard_preferences"
+ err := th.f.DB().Get(&count, countQuery)
+ require.NoError(t, err)
+ require.Equal(t, 4, count)
+
+ th.f.MigrateToStep(38)
+
+ // now the count should be 0
+ err = th.f.DB().Get(&count, countQuery)
+ require.NoError(t, err)
+ require.Equal(t, 2, count)
+ })
+
+ t.Run("plugin - some data exist", func(t *testing.T) {
+ th, tearDown := SetupPluginTestHelper(t)
+ defer tearDown()
+ th.f.MigrateToStep(37).
+ ExecFile("./fixtures/test38_add_plugin_preferences.sql")
+
+ // verify existing data count
+ var count int
+ countQuery := "SELECT COUNT(*) FROM Preferences"
+ err := th.f.DB().Get(&count, countQuery)
+ require.NoError(t, err)
+ require.Equal(t, 4, count)
+
+ th.f.MigrateToStep(38)
+
+ // now the count should be 0
+ err = th.f.DB().Get(&count, countQuery)
+ require.NoError(t, err)
+ require.Equal(t, 2, count)
+ })
+}
diff --git a/server/services/store/sqlstore/migrationstests/migration_33_test.go b/server/services/store/sqlstore/migrationstests/migration_33_test.go
new file mode 100644
index 000000000..ccbdeaa30
--- /dev/null
+++ b/server/services/store/sqlstore/migrationstests/migration_33_test.go
@@ -0,0 +1,63 @@
+package migrationstests
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func Test33RemoveDeletedCategoryBoards(t *testing.T) {
+ t.Run("base case - no data in table", func(t *testing.T) {
+ th, tearDown := SetupTestHelper(t)
+ defer tearDown()
+ th.f.MigrateToStep(33)
+ })
+
+ t.Run("existing data - 2 soft deleted records", func(t *testing.T) {
+ th, tearDown := SetupTestHelper(t)
+ defer tearDown()
+
+ th.f.MigrateToStep(32).
+ ExecFile("./fixtures/test33_with_deleted_data.sql")
+
+ // cound total records
+ var count int
+ err := th.f.DB().Get(&count, "SELECT COUNT(*) FROM focalboard_category_boards")
+ require.NoError(t, err)
+ require.Equal(t, 5, count)
+
+ // now we run the migration
+ th.f.MigrateToStep(33)
+
+ // and verify record count again.
+ // The soft deleted records should have been removed from the DB now
+ err = th.f.DB().Get(&count, "SELECT COUNT(*) FROM focalboard_category_boards")
+ require.NoError(t, err)
+ require.Equal(t, 3, count)
+ })
+
+ t.Run("existing data - no soft deleted records", func(t *testing.T) {
+ th, tearDown := SetupTestHelper(t)
+ defer tearDown()
+
+ th.f.MigrateToStep(32).
+ ExecFile("./fixtures/test33_with_no_deleted_data.sql")
+
+ // cound total records
+ var count int
+ err := th.f.DB().Get(&count, "SELECT COUNT(*) FROM focalboard_category_boards")
+ require.NoError(t, err)
+ require.Equal(t, 5, count)
+
+ // now we run the migration
+ th.f.MigrateToStep(33)
+
+ // and verify record count again.
+ // Since there were no soft-deleted records, nothing should have been
+ // deleted from the database.
+ err = th.f.DB().Get(&count, "SELECT COUNT(*) FROM focalboard_category_boards")
+ require.NoError(t, err)
+ require.Equal(t, 5, count)
+
+ })
+}
diff --git a/server/services/store/sqlstore/public_methods.go b/server/services/store/sqlstore/public_methods.go
index e3acf4955..58a129b54 100644
--- a/server/services/store/sqlstore/public_methods.go
+++ b/server/services/store/sqlstore/public_methods.go
@@ -22,15 +22,15 @@ import (
"github.com/mattermost/mattermost-server/v6/shared/mlog"
)
-func (s *SQLStore) AddUpdateCategoryBoard(userID string, boardCategoryMapping map[string]string) error {
+func (s *SQLStore) AddUpdateCategoryBoard(userID string, categoryID string, boardIDs []string) error {
if s.dbType == model.SqliteDBType {
- return s.addUpdateCategoryBoard(s.db, userID, boardCategoryMapping)
+ return s.addUpdateCategoryBoard(s.db, userID, categoryID, boardIDs)
}
tx, txErr := s.db.BeginTx(context.Background(), nil)
if txErr != nil {
return txErr
}
- err := s.addUpdateCategoryBoard(tx, userID, boardCategoryMapping)
+ err := s.addUpdateCategoryBoard(tx, userID, categoryID, boardIDs)
if err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
s.logger.Error("transaction rollback error", mlog.Err(rollbackErr), mlog.String("methodName", "AddUpdateCategoryBoard"))
@@ -143,11 +143,6 @@ func (s *SQLStore) CreateUser(user *model.User) (*model.User, error) {
}
-func (s *SQLStore) DBVersion() string {
- return s.dBVersion(s.db)
-
-}
-
func (s *SQLStore) DeleteBlock(blockID string, modifiedBy string) error {
if s.dbType == model.SqliteDBType {
return s.deleteBlock(s.db, blockID, modifiedBy)
@@ -333,6 +328,11 @@ func (s *SQLStore) GetBlockHistoryDescendants(boardID string, opts model.QueryBl
}
+func (s *SQLStore) GetBlockHistoryNewestChildren(parentID string, opts model.QueryBlockHistoryChildOptions) ([]*model.Block, bool, error) {
+ return s.getBlockHistoryNewestChildren(s.db, parentID, opts)
+
+}
+
func (s *SQLStore) GetBlocks(opts model.QueryBlocksOptions) ([]*model.Block, error) {
return s.getBlocks(s.db, opts)
@@ -343,6 +343,11 @@ func (s *SQLStore) GetBlocksByIDs(ids []string) ([]*model.Block, error) {
}
+func (s *SQLStore) GetBlocksComplianceHistory(opts model.QueryBlocksComplianceHistoryOptions) ([]*model.BlockHistory, bool, error) {
+ return s.getBlocksComplianceHistory(s.db, opts)
+
+}
+
func (s *SQLStore) GetBlocksForBoard(boardID string) ([]*model.Block, error) {
return s.getBlocksForBoard(s.db, boardID)
@@ -393,6 +398,16 @@ func (s *SQLStore) GetBoardMemberHistory(boardID string, userID string, limit ui
}
+func (s *SQLStore) GetBoardsComplianceHistory(opts model.QueryBoardsComplianceHistoryOptions) ([]*model.BoardHistory, bool, error) {
+ return s.getBoardsComplianceHistory(s.db, opts)
+
+}
+
+func (s *SQLStore) GetBoardsForCompliance(opts model.QueryBoardsForComplianceOptions) ([]*model.Board, bool, error) {
+ return s.getBoardsForCompliance(s.db, opts)
+
+}
+
func (s *SQLStore) GetBoardsForUserAndTeam(userID string, teamID string, includePublicBoards bool) ([]*model.Board, error) {
return s.getBoardsForUserAndTeam(s.db, userID, teamID, includePublicBoards)
@@ -513,8 +528,8 @@ func (s *SQLStore) GetTeam(ID string) (*model.Team, error) {
}
-func (s *SQLStore) GetTeamBoardsInsights(teamID string, userID string, since int64, offset int, limit int, boardIDs []string) (*model.BoardInsightsList, error) {
- return s.getTeamBoardsInsights(s.db, teamID, userID, since, offset, limit, boardIDs)
+func (s *SQLStore) GetTeamBoardsInsights(teamID string, since int64, offset int, limit int, boardIDs []string) (*model.BoardInsightsList, error) {
+ return s.getTeamBoardsInsights(s.db, teamID, since, offset, limit, boardIDs)
}
@@ -850,6 +865,11 @@ func (s *SQLStore) SendMessage(message string, postType string, receipts []strin
}
+func (s *SQLStore) SetBoardVisibility(userID string, categoryID string, boardID string, visible bool) error {
+ return s.setBoardVisibility(s.db, userID, categoryID, boardID, visible)
+
+}
+
func (s *SQLStore) SetSystemSetting(key string, value string) error {
return s.setSystemSetting(s.db, key, value)
diff --git a/server/services/store/sqlstore/sqlstore.go b/server/services/store/sqlstore/sqlstore.go
index 854886d8d..54c352579 100644
--- a/server/services/store/sqlstore/sqlstore.go
+++ b/server/services/store/sqlstore/sqlstore.go
@@ -136,7 +136,7 @@ func (s *SQLStore) getQueryBuilder(db sq.BaseRunner) sq.StatementBuilderType {
return builder.RunWith(db)
}
-func (s *SQLStore) escapeField(fieldName string) string {
+func (s *SQLStore) escapeField(fieldName string) string { //nolint:unparam
if s.dbType == model.MysqlDBType {
return "`" + fieldName + "`"
}
@@ -185,7 +185,7 @@ func (s *SQLStore) getChannel(db sq.BaseRunner, teamID, channel string) (*mmMode
return nil, store.NewNotSupportedError("get channel not supported on standalone mode")
}
-func (s *SQLStore) dBVersion(db sq.BaseRunner) string {
+func (s *SQLStore) DBVersion() string {
var version string
var row *sql.Row
diff --git a/server/services/store/sqlstore/sqlstore_test.go b/server/services/store/sqlstore/sqlstore_test.go
index e802655c7..b2bf8b958 100644
--- a/server/services/store/sqlstore/sqlstore_test.go
+++ b/server/services/store/sqlstore/sqlstore_test.go
@@ -29,6 +29,7 @@ func TestSQLStore(t *testing.T) {
t.Run("StoreTestCategoryStore", func(t *testing.T) { storetests.StoreTestCategoryStore(t, SetupTests) })
t.Run("StoreTestCategoryBoardsStore", func(t *testing.T) { storetests.StoreTestCategoryBoardsStore(t, SetupTests) })
t.Run("BoardsInsightsStore", func(t *testing.T) { storetests.StoreTestBoardsInsightsStore(t, SetupTests) })
+ t.Run("ComplianceHistoryStore", func(t *testing.T) { storetests.StoreTestComplianceHistoryStore(t, SetupTests) })
}
// tests for utility functions inside sqlstore.go
diff --git a/server/services/store/store.go b/server/services/store/store.go
index ac1f4fc37..7cacc8298 100644
--- a/server/services/store/store.go
+++ b/server/services/store/store.go
@@ -38,6 +38,7 @@ type Store interface {
PatchBlock(blockID string, blockPatch *model.BlockPatch, userID string) error
GetBlockHistory(blockID string, opts model.QueryBlockHistoryOptions) ([]*model.Block, error)
GetBlockHistoryDescendants(boardID string, opts model.QueryBlockHistoryOptions) ([]*model.Block, error)
+ GetBlockHistoryNewestChildren(parentID string, opts model.QueryBlockHistoryChildOptions) ([]*model.Block, bool, error)
GetBoardHistory(boardID string, opts model.QueryBoardHistoryOptions) ([]*model.Board, error)
GetBoardAndCardByID(blockID string) (board *model.Board, card *model.Block, err error)
GetBoardAndCard(block *model.Block) (board *model.Board, card *model.Block, err error)
@@ -131,8 +132,9 @@ type Store interface {
SaveFileInfo(fileInfo *mmModel.FileInfo) error
// @withTransaction
- AddUpdateCategoryBoard(userID string, boardCategoryMapping map[string]string) error
+ AddUpdateCategoryBoard(userID, categoryID string, boardIDs []string) error
ReorderCategoryBoards(categoryID string, newBoardsOrder []string) ([]string, error)
+ SetBoardVisibility(userID, categoryID, boardID string, visible bool) error
CreateSubscription(sub *model.Subscription) (*model.Subscription, error)
DeleteSubscription(blockID string, subscriberID string) error
@@ -168,10 +170,15 @@ type Store interface {
SendMessage(message, postType string, receipts []string) error
// Insights
- GetTeamBoardsInsights(teamID string, userID string, since int64, offset int, limit int, boardIDs []string) (*model.BoardInsightsList, error)
+ GetTeamBoardsInsights(teamID string, since int64, offset int, limit int, boardIDs []string) (*model.BoardInsightsList, error)
GetUserBoardsInsights(teamID string, userID string, since int64, offset int, limit int, boardIDs []string) (*model.BoardInsightsList, error)
GetUserTimezone(userID string) (string, error)
+ // Compliance
+ GetBoardsForCompliance(opts model.QueryBoardsForComplianceOptions) ([]*model.Board, bool, error)
+ GetBoardsComplianceHistory(opts model.QueryBoardsComplianceHistoryOptions) ([]*model.BoardHistory, bool, error)
+ GetBlocksComplianceHistory(opts model.QueryBlocksComplianceHistoryOptions) ([]*model.BlockHistory, bool, error)
+
// For unit testing only
DeleteBoardRecord(boardID, modifiedBy string) error
DeleteBlockRecord(blockID, modifiedBy string) error
diff --git a/server/services/store/storetests/blocks.go b/server/services/store/storetests/blocks.go
index 6a7f65e1a..bbd01ef72 100644
--- a/server/services/store/storetests/blocks.go
+++ b/server/services/store/storetests/blocks.go
@@ -1,6 +1,8 @@
package storetests
import (
+ "math"
+ "strconv"
"testing"
"time"
@@ -79,6 +81,11 @@ func StoreTestBlocksStore(t *testing.T, setup func(t *testing.T) (store.Store, f
defer tearDown()
testUndeleteBlockChildren(t, store)
})
+ t.Run("GetBlockHistoryNewestChildren", func(t *testing.T) {
+ store, tearDown := setup(t)
+ defer tearDown()
+ testGetBlockHistoryNewestChildren(t, store)
+ })
}
func testInsertBlock(t *testing.T, store store.Store) {
@@ -1066,17 +1073,18 @@ func testGetBlockMetadata(t *testing.T, store store.Store) {
}
func testUndeleteBlockChildren(t *testing.T, store store.Store) {
- boards := createTestBoards(t, store, testUserID, 2)
+ boards := createTestBoards(t, store, testTeamID, testUserID, 2)
boardDelete := boards[0]
boardKeep := boards[1]
+ userID := testUserID
// create some blocks to be deleted
- cardsDelete := createTestCards(t, store, testUserID, boardDelete.ID, 3)
+ cardsDelete := createTestCards(t, store, userID, boardDelete.ID, 3)
blocksDelete := createTestBlocksForCard(t, store, cardsDelete[0].ID, 5)
require.Len(t, blocksDelete, 5)
// create some blocks to keep
- cardsKeep := createTestCards(t, store, testUserID, boardKeep.ID, 3)
+ cardsKeep := createTestCards(t, store, userID, boardKeep.ID, 3)
blocksKeep := createTestBlocksForCard(t, store, cardsKeep[0].ID, 4)
require.Len(t, blocksKeep, 4)
@@ -1153,3 +1161,94 @@ func testUndeleteBlockChildren(t *testing.T, store store.Store) {
assert.Len(t, blocks, len(blocksDelete)+len(cardsDelete))
})
}
+
+func testGetBlockHistoryNewestChildren(t *testing.T, store store.Store) {
+ boards := createTestBoards(t, store, testTeamID, testUserID, 2)
+ board := boards[0]
+
+ const cardCount = 10
+ const patchCount = 5
+
+ // create a card and some content blocks
+ cards := createTestCards(t, store, testUserID, board.ID, 1)
+ card := cards[0]
+ content := createTestBlocksForCard(t, store, card.ID, cardCount)
+
+ // patch the content blocks to create some history records
+ for i := 1; i <= patchCount; i++ {
+ for _, block := range content {
+ title := strconv.FormatInt(int64(i), 10)
+ patch := &model.BlockPatch{
+ Title: &title,
+ }
+ err := store.PatchBlock(block.ID, patch, testUserID)
+ require.NoError(t, err, "error patching content blocks")
+ }
+ }
+
+ // delete some of the content blocks
+ err := store.DeleteBlock(content[0].ID, testUserID)
+ require.NoError(t, err, "error deleting content block")
+ err = store.DeleteBlock(content[3].ID, testUserID)
+ require.NoError(t, err, "error deleting content block")
+ err = store.DeleteBlock(content[7].ID, testUserID)
+ require.NoError(t, err, "error deleting content block")
+
+ t.Run("invalid card", func(t *testing.T) {
+ opts := model.QueryBlockHistoryChildOptions{}
+ blocks, hasMore, err := store.GetBlockHistoryNewestChildren(utils.NewID(utils.IDTypeCard), opts)
+ require.NoError(t, err)
+ require.False(t, hasMore)
+ require.Empty(t, blocks)
+ })
+
+ t.Run("valid card with no children", func(t *testing.T) {
+ opts := model.QueryBlockHistoryChildOptions{}
+ emptyCard := createTestCards(t, store, testUserID, board.ID, 1)[0]
+ blocks, hasMore, err := store.GetBlockHistoryNewestChildren(emptyCard.ID, opts)
+ require.NoError(t, err)
+ require.False(t, hasMore)
+ require.Empty(t, blocks)
+ })
+
+ t.Run("valid card with children", func(t *testing.T) {
+ opts := model.QueryBlockHistoryChildOptions{}
+ blocks, hasMore, err := store.GetBlockHistoryNewestChildren(card.ID, opts)
+ require.NoError(t, err)
+ require.False(t, hasMore)
+ require.Len(t, blocks, cardCount)
+ require.ElementsMatch(t, extractIDs(t, blocks), extractIDs(t, content))
+
+ expected := strconv.FormatInt(patchCount, 10)
+ for _, b := range blocks {
+ require.Equal(t, expected, b.Title)
+ }
+ })
+
+ t.Run("pagination", func(t *testing.T) {
+ opts := model.QueryBlockHistoryChildOptions{
+ PerPage: 3,
+ }
+
+ collected := make([]*model.Block, 0)
+ reps := 0
+ for {
+ reps++
+ blocks, hasMore, err := store.GetBlockHistoryNewestChildren(card.ID, opts)
+ require.NoError(t, err)
+ collected = append(collected, blocks...)
+ if !hasMore {
+ break
+ }
+ opts.Page++
+ }
+
+ assert.Len(t, collected, cardCount)
+ assert.Equal(t, math.Floor(float64(cardCount/opts.PerPage)+1), float64(reps))
+
+ expected := strconv.FormatInt(patchCount, 10)
+ for _, b := range collected {
+ require.Equal(t, expected, b.Title)
+ }
+ })
+}
diff --git a/server/services/store/storetests/board_insights.go b/server/services/store/storetests/board_insights.go
index 85470cd47..f13091568 100644
--- a/server/services/store/storetests/board_insights.go
+++ b/server/services/store/storetests/board_insights.go
@@ -72,7 +72,7 @@ func getBoardsInsightsTest(t *testing.T, store store.Store) {
boardsUser2, _ := store.GetBoardsForUserAndTeam(testInsightsUserID1, testTeamID, true)
t.Run("team insights", func(t *testing.T) {
boardIDs := []string{boardsUser1[0].ID, boardsUser1[1].ID, boardsUser1[2].ID}
- topTeamBoards, err := store.GetTeamBoardsInsights(testTeamID, testUserID,
+ topTeamBoards, err := store.GetTeamBoardsInsights(testTeamID,
0, 0, 10, boardIDs)
require.NoError(t, err)
require.Len(t, topTeamBoards.Items, 3)
diff --git a/server/services/store/storetests/category.go b/server/services/store/storetests/category.go
index 113e13b2b..bf09da297 100644
--- a/server/services/store/storetests/category.go
+++ b/server/services/store/storetests/category.go
@@ -294,11 +294,11 @@ func testReorderCategoryBoards(t *testing.T, store store.Store) {
})
assert.NoError(t, err)
- err = store.AddUpdateCategoryBoard("user_id", map[string]string{
- "board_id_1": "category_id_1",
- "board_id_2": "category_id_1",
- "board_id_3": "category_id_1",
- "board_id_4": "category_id_1",
+ err = store.AddUpdateCategoryBoard("user_id", "category_id_1", []string{
+ "board_id_1",
+ "board_id_2",
+ "board_id_3",
+ "board_id_4",
})
assert.NoError(t, err)
@@ -306,11 +306,11 @@ func testReorderCategoryBoards(t *testing.T, store store.Store) {
categoryBoards, err := store.GetUserCategoryBoards("user_id", "team_id")
assert.NoError(t, err)
assert.Equal(t, 1, len(categoryBoards))
- assert.Equal(t, 4, 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.Contains(t, categoryBoards[0].BoardIDs, "board_id_4")
+ assert.Equal(t, 4, 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})
+ assert.Contains(t, categoryBoards[0].BoardMetadata, model.CategoryBoardMetadata{BoardID: "board_id_4", Hidden: false})
// reordering
newOrder, err := store.ReorderCategoryBoards("category_id_1", []string{
@@ -329,9 +329,9 @@ func testReorderCategoryBoards(t *testing.T, store store.Store) {
categoryBoards, err = store.GetUserCategoryBoards("user_id", "team_id")
assert.NoError(t, err)
assert.Equal(t, 1, len(categoryBoards))
- assert.Equal(t, 4, len(categoryBoards[0].BoardIDs))
- assert.Equal(t, "board_id_3", categoryBoards[0].BoardIDs[0])
- assert.Equal(t, "board_id_1", categoryBoards[0].BoardIDs[1])
- assert.Equal(t, "board_id_2", categoryBoards[0].BoardIDs[2])
- assert.Equal(t, "board_id_4", categoryBoards[0].BoardIDs[3])
+ assert.Equal(t, 4, len(categoryBoards[0].BoardMetadata))
+ assert.Equal(t, "board_id_3", categoryBoards[0].BoardMetadata[0].BoardID)
+ assert.Equal(t, "board_id_1", categoryBoards[0].BoardMetadata[1].BoardID)
+ assert.Equal(t, "board_id_2", categoryBoards[0].BoardMetadata[2].BoardID)
+ assert.Equal(t, "board_id_4", categoryBoards[0].BoardMetadata[3].BoardID)
}
diff --git a/server/services/store/storetests/categoryBoards.go b/server/services/store/storetests/categoryBoards.go
index 2cc83b197..96b1d34ec 100644
--- a/server/services/store/storetests/categoryBoards.go
+++ b/server/services/store/storetests/categoryBoards.go
@@ -15,6 +15,18 @@ func StoreTestCategoryBoardsStore(t *testing.T, setup func(t *testing.T) (store.
defer tearDown()
testGetUserCategoryBoards(t, store)
})
+
+ t.Run("AddUpdateCategoryBoard", func(t *testing.T) {
+ store, tearDown := setup(t)
+ defer tearDown()
+ testAddUpdateCategoryBoard(t, store)
+ })
+
+ t.Run("SetBoardVisibility", func(t *testing.T) {
+ store, tearDown := setup(t)
+ defer tearDown()
+ testSetBoardVisibility(t, store)
+ })
}
func testGetUserCategoryBoards(t *testing.T, store store.Store) {
@@ -60,14 +72,14 @@ func testGetUserCategoryBoards(t *testing.T, store store.Store) {
// Adding Board 1 and Board 2 to Category 1
// The boards don't need to exists in DB for this test
- err = store.AddUpdateCategoryBoard("user_id_1", map[string]string{"board_1": "category_id_1"})
+ err = store.AddUpdateCategoryBoard("user_id_1", "category_id_1", []string{"board_1"})
assert.NoError(t, err)
- err = store.AddUpdateCategoryBoard("user_id_1", map[string]string{"board_2": "category_id_1"})
+ err = store.AddUpdateCategoryBoard("user_id_1", "category_id_1", []string{"board_2"})
assert.NoError(t, err)
// Adding Board 3 to Category 2
- err = store.AddUpdateCategoryBoard("user_id_1", map[string]string{"board_3": "category_id_2"})
+ err = store.AddUpdateCategoryBoard("user_id_1", "category_id_2", []string{"board_3"})
assert.NoError(t, err)
// we'll leave category 3 empty
@@ -94,13 +106,13 @@ func testGetUserCategoryBoards(t *testing.T, store store.Store) {
}
assert.NotEmpty(t, category1BoardCategory)
- assert.Equal(t, 2, len(category1BoardCategory.BoardIDs))
+ assert.Equal(t, 2, len(category1BoardCategory.BoardMetadata))
assert.NotEmpty(t, category1BoardCategory)
- assert.Equal(t, 1, len(category2BoardCategory.BoardIDs))
+ assert.Equal(t, 1, len(category2BoardCategory.BoardMetadata))
assert.NotEmpty(t, category1BoardCategory)
- assert.Equal(t, 0, len(category3BoardCategory.BoardIDs))
+ assert.Equal(t, 0, len(category3BoardCategory.BoardMetadata))
t.Run("get empty category boards", func(t *testing.T) {
userCategoryBoards, err := store.GetUserCategoryBoards("nonexistent-user-id", "nonexistent-team-id")
@@ -108,3 +120,142 @@ func testGetUserCategoryBoards(t *testing.T, store store.Store) {
assert.Empty(t, userCategoryBoards)
})
}
+
+func testAddUpdateCategoryBoard(t *testing.T, store store.Store) {
+ // creating few boards and categories to later associoate with the category
+ _, _, err := store.CreateBoardsAndBlocksWithAdmin(&model.BoardsAndBlocks{
+ Boards: []*model.Board{
+ {
+ ID: "board_id_1",
+ TeamID: "team_id",
+ },
+ {
+ ID: "board_id_2",
+ TeamID: "team_id",
+ },
+ },
+ }, "user_id")
+ assert.NoError(t, err)
+
+ err = store.CreateCategory(model.Category{
+ ID: "category_id",
+ Name: "Category",
+ UserID: "user_id",
+ TeamID: "team_id",
+ })
+ assert.NoError(t, err)
+
+ // adding a few boards to the category
+ err = store.AddUpdateCategoryBoard("user_id", "category_id", []string{"board_id_1", "board_id_2"})
+ assert.NoError(t, err)
+
+ // verify inserted data
+ categoryBoards, err := store.GetUserCategoryBoards("user_id", "team_id")
+ assert.NoError(t, err)
+ assert.Equal(t, 1, len(categoryBoards))
+ assert.Equal(t, "category_id", categoryBoards[0].ID)
+ assert.Equal(t, 2, 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})
+
+ // adding new boards to the same category
+ err = store.AddUpdateCategoryBoard("user_id", "category_id", []string{"board_id_3"})
+ assert.NoError(t, err)
+
+ // verify inserted data
+ categoryBoards, err = store.GetUserCategoryBoards("user_id", "team_id")
+ assert.NoError(t, err)
+ assert.Equal(t, 1, len(categoryBoards))
+ assert.Equal(t, "category_id", categoryBoards[0].ID)
+ assert.Equal(t, 3, len(categoryBoards[0].BoardMetadata))
+ assert.Contains(t, categoryBoards[0].BoardMetadata, model.CategoryBoardMetadata{BoardID: "board_id_3", Hidden: false})
+
+ // passing empty array
+ err = store.AddUpdateCategoryBoard("user_id", "category_id", []string{})
+ assert.NoError(t, err)
+
+ // verify inserted data
+ categoryBoards, err = store.GetUserCategoryBoards("user_id", "team_id")
+ assert.NoError(t, err)
+ assert.Equal(t, 1, len(categoryBoards))
+ assert.Equal(t, "category_id", categoryBoards[0].ID)
+ assert.Equal(t, 3, len(categoryBoards[0].BoardMetadata))
+
+ // passing duplicate data in input
+ err = store.AddUpdateCategoryBoard("user_id", "category_id", []string{"board_id_4", "board_id_4"})
+ assert.NoError(t, err)
+
+ // verify inserted data
+ categoryBoards, err = store.GetUserCategoryBoards("user_id", "team_id")
+ assert.NoError(t, err)
+ assert.Equal(t, 1, len(categoryBoards))
+ assert.Equal(t, "category_id", categoryBoards[0].ID)
+ assert.Equal(t, 4, len(categoryBoards[0].BoardMetadata))
+ assert.Contains(t, categoryBoards[0].BoardMetadata, model.CategoryBoardMetadata{BoardID: "board_id_4", Hidden: false})
+
+ // adding already added board
+ err = store.AddUpdateCategoryBoard("user_id", "category_id", []string{"board_id_1", "board_id_2"})
+ assert.NoError(t, err)
+
+ // verify inserted data
+ categoryBoards, err = store.GetUserCategoryBoards("user_id", "team_id")
+ assert.NoError(t, err)
+ assert.Equal(t, 1, len(categoryBoards))
+ assert.Equal(t, "category_id", categoryBoards[0].ID)
+ assert.Equal(t, 4, len(categoryBoards[0].BoardMetadata))
+
+ // passing already added board along with a new board
+ err = store.AddUpdateCategoryBoard("user_id", "category_id", []string{"board_id_1", "board_id_5"})
+ assert.NoError(t, err)
+
+ // verify inserted data
+ categoryBoards, err = store.GetUserCategoryBoards("user_id", "team_id")
+ assert.NoError(t, err)
+ assert.Equal(t, 1, len(categoryBoards))
+ assert.Equal(t, "category_id", categoryBoards[0].ID)
+ assert.Equal(t, 5, len(categoryBoards[0].BoardMetadata))
+ assert.Contains(t, categoryBoards[0].BoardMetadata, model.CategoryBoardMetadata{BoardID: "board_id_5", Hidden: false})
+}
+
+func testSetBoardVisibility(t *testing.T, store store.Store) {
+ _, _, err := store.CreateBoardsAndBlocksWithAdmin(&model.BoardsAndBlocks{
+ Boards: []*model.Board{
+ {
+ ID: "board_id_1",
+ TeamID: "team_id",
+ },
+ },
+ }, "user_id")
+ assert.NoError(t, err)
+
+ err = store.CreateCategory(model.Category{
+ ID: "category_id",
+ Name: "Category",
+ UserID: "user_id",
+ TeamID: "team_id",
+ })
+ assert.NoError(t, err)
+
+ // adding a few boards to the category
+ err = store.AddUpdateCategoryBoard("user_id", "category_id", []string{"board_id_1"})
+ assert.NoError(t, err)
+
+ err = store.SetBoardVisibility("user_id", "category_id", "board_id_1", true)
+ assert.NoError(t, err)
+
+ // verify set visibility
+ categoryBoards, err := store.GetUserCategoryBoards("user_id", "team_id")
+ assert.NoError(t, err)
+ assert.Equal(t, 1, len(categoryBoards))
+ assert.Equal(t, "category_id", categoryBoards[0].ID)
+ assert.Equal(t, 1, len(categoryBoards[0].BoardMetadata))
+ assert.False(t, categoryBoards[0].BoardMetadata[0].Hidden)
+
+ err = store.SetBoardVisibility("user_id", "category_id", "board_id_1", false)
+ assert.NoError(t, err)
+
+ // verify set visibility
+ categoryBoards, err = store.GetUserCategoryBoards("user_id", "team_id")
+ assert.NoError(t, err)
+ assert.True(t, categoryBoards[0].BoardMetadata[0].Hidden)
+}
diff --git a/server/services/store/storetests/compliance.go b/server/services/store/storetests/compliance.go
new file mode 100644
index 000000000..baeed373f
--- /dev/null
+++ b/server/services/store/storetests/compliance.go
@@ -0,0 +1,294 @@
+package storetests
+
+import (
+ "math"
+ "testing"
+
+ "github.com/mattermost/focalboard/server/model"
+ "github.com/mattermost/focalboard/server/services/store"
+ "github.com/mattermost/focalboard/server/utils"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func StoreTestComplianceHistoryStore(t *testing.T, setup func(t *testing.T) (store.Store, func())) {
+ t.Run("GetBoardsForCompliance", func(t *testing.T) {
+ store, tearDown := setup(t)
+ defer tearDown()
+ testGetBoardsForCompliance(t, store)
+ })
+ t.Run("GetBoardsComplianceHistory", func(t *testing.T) {
+ store, tearDown := setup(t)
+ defer tearDown()
+ testGetBoardsComplianceHistory(t, store)
+ })
+ t.Run("GetBlocksComplianceHistory", func(t *testing.T) {
+ store, tearDown := setup(t)
+ defer tearDown()
+ testGetBlocksComplianceHistory(t, store)
+ })
+}
+
+func testGetBoardsForCompliance(t *testing.T, store store.Store) {
+ team1 := testTeamID
+ team2 := utils.NewID(utils.IDTypeTeam)
+
+ boardsAdded1 := createTestBoards(t, store, team1, testUserID, 10)
+ boardsAdded2 := createTestBoards(t, store, team2, testUserID, 7)
+
+ deleteTestBoard(t, store, boardsAdded1[0].ID, testUserID)
+ deleteTestBoard(t, store, boardsAdded1[1].ID, testUserID)
+ boardsAdded1 = boardsAdded1[2:]
+
+ t.Run("Invalid teamID", func(t *testing.T) {
+ opts := model.QueryBoardsForComplianceOptions{
+ TeamID: utils.NewID(utils.IDTypeTeam),
+ }
+
+ boards, hasMore, err := store.GetBoardsForCompliance(opts)
+
+ assert.Empty(t, boards)
+ assert.False(t, hasMore)
+ assert.NoError(t, err)
+ })
+
+ t.Run("All teams", func(t *testing.T) {
+ opts := model.QueryBoardsForComplianceOptions{}
+
+ boards, hasMore, err := store.GetBoardsForCompliance(opts)
+
+ assert.ElementsMatch(t, extractIDs(t, boards), extractIDs(t, boardsAdded1, boardsAdded2))
+ assert.False(t, hasMore)
+ assert.NoError(t, err)
+ })
+
+ t.Run("Specific team", func(t *testing.T) {
+ opts := model.QueryBoardsForComplianceOptions{
+ TeamID: team1,
+ }
+
+ boards, hasMore, err := store.GetBoardsForCompliance(opts)
+
+ assert.ElementsMatch(t, extractIDs(t, boards), extractIDs(t, boardsAdded1))
+ assert.False(t, hasMore)
+ assert.NoError(t, err)
+ })
+
+ t.Run("Pagination", func(t *testing.T) {
+ opts := model.QueryBoardsForComplianceOptions{
+ Page: 0,
+ PerPage: 3,
+ }
+
+ reps := 0
+ allBoards := make([]*model.Board, 0, 20)
+
+ for {
+ boards, hasMore, err := store.GetBoardsForCompliance(opts)
+ require.NoError(t, err)
+ require.NotEmpty(t, boards)
+ allBoards = append(allBoards, boards...)
+
+ if !hasMore {
+ break
+ }
+ opts.Page++
+ reps++
+ }
+
+ assert.ElementsMatch(t, extractIDs(t, allBoards), extractIDs(t, boardsAdded1, boardsAdded2))
+ })
+}
+
+func testGetBoardsComplianceHistory(t *testing.T, store store.Store) {
+ team1 := testTeamID
+ team2 := utils.NewID(utils.IDTypeTeam)
+
+ boardsTeam1 := createTestBoards(t, store, team1, testUserID, 11)
+ boardsTeam2 := createTestBoards(t, store, team2, testUserID, 7)
+ boardsAdded := make([]*model.Board, 0)
+ boardsAdded = append(boardsAdded, boardsTeam1...)
+ boardsAdded = append(boardsAdded, boardsTeam2...)
+
+ deleteTestBoard(t, store, boardsTeam1[0].ID, testUserID)
+ deleteTestBoard(t, store, boardsTeam1[1].ID, testUserID)
+ boardsDeleted := boardsTeam1[0:2]
+ boardsTeam1 = boardsTeam1[2:]
+
+ t.Log("boardsTeam1: ", extractIDs(t, boardsTeam1))
+ t.Log("boardsTeam2: ", extractIDs(t, boardsTeam2))
+ t.Log("boardsAdded: ", extractIDs(t, boardsAdded))
+ t.Log("boardsDeleted: ", extractIDs(t, boardsDeleted))
+
+ t.Run("Invalid teamID", func(t *testing.T) {
+ opts := model.QueryBoardsComplianceHistoryOptions{
+ TeamID: utils.NewID(utils.IDTypeTeam),
+ }
+
+ boardHistories, hasMore, err := store.GetBoardsComplianceHistory(opts)
+
+ assert.Empty(t, boardHistories)
+ assert.False(t, hasMore)
+ assert.NoError(t, err)
+ })
+
+ t.Run("All teams, include deleted", func(t *testing.T) {
+ opts := model.QueryBoardsComplianceHistoryOptions{
+ IncludeDeleted: true,
+ }
+
+ boardHistories, hasMore, err := store.GetBoardsComplianceHistory(opts)
+
+ // boardHistories should contain a record for each board added, plus a record for the 2 deleted.
+ assert.ElementsMatch(t, extractIDs(t, boardHistories), extractIDs(t, boardsAdded, boardsDeleted))
+ assert.False(t, hasMore)
+ assert.NoError(t, err)
+ })
+
+ t.Run("All teams, exclude deleted", func(t *testing.T) {
+ opts := model.QueryBoardsComplianceHistoryOptions{
+ IncludeDeleted: false,
+ }
+
+ boardHistories, hasMore, err := store.GetBoardsComplianceHistory(opts)
+
+ // boardHistories should contain a record for each board added, minus the two deleted.
+ assert.ElementsMatch(t, extractIDs(t, boardHistories), extractIDs(t, boardsTeam1, boardsTeam2))
+ assert.False(t, hasMore)
+ assert.NoError(t, err)
+ })
+
+ t.Run("Specific team", func(t *testing.T) {
+ opts := model.QueryBoardsComplianceHistoryOptions{
+ TeamID: team1,
+ }
+
+ boardHistories, hasMore, err := store.GetBoardsComplianceHistory(opts)
+
+ assert.ElementsMatch(t, extractIDs(t, boardHistories), extractIDs(t, boardsTeam1))
+ assert.False(t, hasMore)
+ assert.NoError(t, err)
+ })
+
+ t.Run("Pagination", func(t *testing.T) {
+ opts := model.QueryBoardsComplianceHistoryOptions{
+ Page: 0,
+ PerPage: 3,
+ }
+
+ reps := 0
+ allHistories := make([]*model.BoardHistory, 0)
+
+ for {
+ reps++
+ boardHistories, hasMore, err := store.GetBoardsComplianceHistory(opts)
+ require.NoError(t, err)
+ require.NotEmpty(t, boardHistories)
+ allHistories = append(allHistories, boardHistories...)
+
+ if !hasMore {
+ break
+ }
+ opts.Page++
+ }
+
+ assert.ElementsMatch(t, extractIDs(t, allHistories), extractIDs(t, boardsTeam1, boardsTeam2))
+ expectedCount := len(boardsTeam1) + len(boardsTeam2)
+ assert.Equal(t, math.Floor(float64(expectedCount/opts.PerPage)+1), float64(reps))
+ })
+}
+
+func testGetBlocksComplianceHistory(t *testing.T, store store.Store) {
+ team1 := testTeamID
+ team2 := utils.NewID(utils.IDTypeTeam)
+
+ boardsTeam1 := createTestBoards(t, store, team1, testUserID, 3)
+ boardsTeam2 := createTestBoards(t, store, team2, testUserID, 1)
+
+ // add cards (13 in total)
+ cards1Team1 := createTestCards(t, store, testUserID, boardsTeam1[0].ID, 3)
+ cards2Team1 := createTestCards(t, store, testUserID, boardsTeam1[1].ID, 5)
+ cards3Team1 := createTestCards(t, store, testUserID, boardsTeam1[2].ID, 2)
+ cards1Team2 := createTestCards(t, store, testUserID, boardsTeam2[0].ID, 3)
+
+ deleteTestBoard(t, store, boardsTeam1[0].ID, testUserID)
+ cardsDeleted := cards1Team1
+
+ t.Run("Invalid teamID", func(t *testing.T) {
+ opts := model.QueryBlocksComplianceHistoryOptions{
+ TeamID: utils.NewID(utils.IDTypeTeam),
+ }
+
+ boards, hasMore, err := store.GetBlocksComplianceHistory(opts)
+
+ assert.Empty(t, boards)
+ assert.False(t, hasMore)
+ assert.NoError(t, err)
+ })
+
+ t.Run("All teams, include deleted", func(t *testing.T) {
+ opts := model.QueryBlocksComplianceHistoryOptions{
+ IncludeDeleted: true,
+ }
+
+ blockHistories, hasMore, err := store.GetBlocksComplianceHistory(opts)
+
+ // blockHistories should have records for all cards added, plus all cards deleted
+ assert.ElementsMatch(t, extractIDs(t, blockHistories, nil),
+ extractIDs(t, cards1Team1, cards2Team1, cards3Team1, cards1Team2, cardsDeleted))
+ assert.False(t, hasMore)
+ assert.NoError(t, err)
+ })
+
+ t.Run("All teams, exclude deleted", func(t *testing.T) {
+ opts := model.QueryBlocksComplianceHistoryOptions{}
+
+ blockHistories, hasMore, err := store.GetBlocksComplianceHistory(opts)
+
+ // blockHistories should have records for all cards added that have not been deleted
+ assert.ElementsMatch(t, extractIDs(t, blockHistories, nil),
+ extractIDs(t, cards2Team1, cards3Team1, cards1Team2))
+ assert.False(t, hasMore)
+ assert.NoError(t, err)
+ })
+
+ t.Run("Specific team", func(t *testing.T) {
+ opts := model.QueryBlocksComplianceHistoryOptions{
+ TeamID: team1,
+ }
+
+ blockHistories, hasMore, err := store.GetBlocksComplianceHistory(opts)
+
+ assert.ElementsMatch(t, extractIDs(t, blockHistories), extractIDs(t, cards2Team1, cards3Team1))
+ assert.False(t, hasMore)
+ assert.NoError(t, err)
+ })
+
+ t.Run("Pagination", func(t *testing.T) {
+ opts := model.QueryBlocksComplianceHistoryOptions{
+ Page: 0,
+ PerPage: 3,
+ }
+
+ reps := 0
+ allHistories := make([]*model.BlockHistory, 0)
+
+ for {
+ reps++
+ blockHistories, hasMore, err := store.GetBlocksComplianceHistory(opts)
+ require.NoError(t, err)
+ require.NotEmpty(t, blockHistories)
+ allHistories = append(allHistories, blockHistories...)
+
+ if !hasMore {
+ break
+ }
+ opts.Page++
+ }
+
+ assert.ElementsMatch(t, extractIDs(t, allHistories), extractIDs(t, cards2Team1, cards3Team1, cards1Team2))
+
+ expectedCount := len(cards2Team1) + len(cards3Team1) + len(cards1Team2)
+ assert.Equal(t, math.Floor(float64(expectedCount/opts.PerPage)+1), float64(reps))
+ })
+}
diff --git a/server/services/store/storetests/data_retention.go b/server/services/store/storetests/data_retention.go
index a73d40afa..2f7f36526 100644
--- a/server/services/store/storetests/data_retention.go
+++ b/server/services/store/storetests/data_retention.go
@@ -92,7 +92,7 @@ func LoadData(t *testing.T, store store.Store) {
err = store.UpsertSharing(sharing)
require.NoError(t, err)
- err = store.AddUpdateCategoryBoard(testUserID, map[string]string{boardID: categoryID})
+ err = store.AddUpdateCategoryBoard(testUserID, categoryID, []string{boardID})
require.NoError(t, err)
}
diff --git a/server/services/store/storetests/util.go b/server/services/store/storetests/util.go
index 24d99eead..a56056bd9 100644
--- a/server/services/store/storetests/util.go
+++ b/server/services/store/storetests/util.go
@@ -5,6 +5,7 @@ package storetests
import (
"fmt"
+ "sort"
"testing"
"github.com/mattermost/focalboard/server/model"
@@ -71,6 +72,7 @@ func createTestBlocksForCard(t *testing.T, store store.Store, cardID string, num
return blocks
}
+//nolint:unparam
func createTestCards(t *testing.T, store store.Store, userID string, boardID string, num int) []*model.Block {
var blocks []*model.Block
for i := 0; i < num; i++ {
@@ -90,12 +92,13 @@ func createTestCards(t *testing.T, store store.Store, userID string, boardID str
return blocks
}
-func createTestBoards(t *testing.T, store store.Store, userID string, num int) []*model.Board {
+//nolint:unparam
+func createTestBoards(t *testing.T, store store.Store, teamID string, userID string, num int) []*model.Board {
var boards []*model.Board
for i := 0; i < num; i++ {
board := &model.Board{
ID: utils.NewID(utils.IDTypeBoard),
- TeamID: testTeamID,
+ TeamID: teamID,
Type: "O",
CreatedBy: userID,
Title: fmt.Sprintf("board %d", i),
@@ -107,3 +110,50 @@ func createTestBoards(t *testing.T, store store.Store, userID string, num int) [
}
return boards
}
+
+//nolint:unparam
+func deleteTestBoard(t *testing.T, store store.Store, boardID string, userID string) {
+ err := store.DeleteBoard(boardID, userID)
+ require.NoError(t, err)
+}
+
+// extractIDs is a test helper that extracts a sorted slice of IDs from slices of various struct types.
+// Might have used generics here except that would require implementing a `GetID` method on each type.
+func extractIDs(t *testing.T, arr ...any) []string {
+ ids := make([]string, 0)
+
+ for _, item := range arr {
+ if item == nil {
+ continue
+ }
+
+ switch tarr := item.(type) {
+ case []*model.Board:
+ for _, b := range tarr {
+ if b != nil {
+ ids = append(ids, b.ID)
+ }
+ }
+ case []*model.BoardHistory:
+ for _, bh := range tarr {
+ ids = append(ids, bh.ID)
+ }
+ case []*model.Block:
+ for _, b := range tarr {
+ if b != nil {
+ ids = append(ids, b.ID)
+ }
+ }
+ case []*model.BlockHistory:
+ for _, bh := range tarr {
+ ids = append(ids, bh.ID)
+ }
+ default:
+ t.Errorf("unsupported type %T extracting board ID", item)
+ }
+ }
+
+ // sort the ids to make it easier to compare lists of ids visually.
+ sort.Strings(ids)
+ return ids
+}
diff --git a/server/utils/utils.go b/server/utils/utils.go
index 995c2cc87..7a1ad9763 100644
--- a/server/utils/utils.go
+++ b/server/utils/utils.go
@@ -11,15 +11,16 @@ import (
type IDType byte
const (
- IDTypeNone IDType = '7'
- IDTypeTeam IDType = 't'
- IDTypeBoard IDType = 'b'
- IDTypeCard IDType = 'c'
- IDTypeView IDType = 'v'
- IDTypeSession IDType = 's'
- IDTypeUser IDType = 'u'
- IDTypeToken IDType = 'k'
- IDTypeBlock IDType = 'a'
+ IDTypeNone IDType = '7'
+ IDTypeTeam IDType = 't'
+ IDTypeBoard IDType = 'b'
+ IDTypeCard IDType = 'c'
+ IDTypeView IDType = 'v'
+ IDTypeSession IDType = 's'
+ IDTypeUser IDType = 'u'
+ IDTypeToken IDType = 'k'
+ IDTypeBlock IDType = 'a'
+ IDTypeAttachment IDType = 'i'
)
// NewId is a globally unique identifier. It is a [A-Z0-9] string 27
@@ -102,3 +103,20 @@ func IsCloudLicense(license *mmModel.License) bool {
license.Features.Cloud != nil &&
*license.Features.Cloud
}
+
+func DedupeStringArr(arr []string) []string {
+ hashMap := map[string]bool{}
+
+ for _, item := range arr {
+ hashMap[item] = true
+ }
+
+ dedupedArr := make([]string, len(hashMap))
+ i := 0
+ for key := range hashMap {
+ dedupedArr[i] = key
+ i++
+ }
+
+ return dedupedArr
+}
diff --git a/server/web/webserver.go b/server/web/webserver.go
index 237c7f40f..ad0539078 100644
--- a/server/web/webserver.go
+++ b/server/web/webserver.go
@@ -59,7 +59,8 @@ func NewServer(rootPath string, serverRoot string, port int, ssl, localOnly bool
baseURL = url.Path
ws := &Server{
- Server: http.Server{
+ // (TODO: Add ReadHeaderTimeout)
+ Server: http.Server{ //nolint:gosec
Addr: addr,
Handler: r,
},
diff --git a/server/ws/plugin_adapter.go b/server/ws/plugin_adapter.go
index f45cae629..65cafb09b 100644
--- a/server/ws/plugin_adapter.go
+++ b/server/ws/plugin_adapter.go
@@ -422,7 +422,7 @@ func (pa *PluginAdapter) sendTeamMessage(event, teamID string, payload map[strin
EnsureUsers: ensureUserIDs,
}
- pa.sendMessageToCluster("websocket_message", clusterMessage)
+ pa.sendMessageToCluster(clusterMessage)
}()
pa.sendTeamMessageSkipCluster(event, teamID, payload)
@@ -447,7 +447,7 @@ func (pa *PluginAdapter) sendBoardMessage(teamID, boardID string, payload map[st
EnsureUsers: ensureUserIDs,
}
- pa.sendMessageToCluster("websocket_message", clusterMessage)
+ pa.sendMessageToCluster(clusterMessage)
}()
pa.sendBoardMessageSkipCluster(teamID, boardID, payload, ensureUserIDs...)
@@ -490,7 +490,7 @@ func (pa *PluginAdapter) BroadcastCategoryChange(category model.Category) {
UserID: category.UserID,
}
- pa.sendMessageToCluster("websocket_message", clusterMessage)
+ pa.sendMessageToCluster(clusterMessage)
}()
pa.sendUserMessageSkipCluster(websocketActionUpdateCategory, payload, category.UserID)
@@ -514,7 +514,7 @@ func (pa *PluginAdapter) BroadcastCategoryReorder(teamID, userID string, categor
UserID: userID,
}
- pa.sendMessageToCluster("websocket_message", clusterMessage)
+ pa.sendMessageToCluster(clusterMessage)
}()
pa.sendUserMessageSkipCluster(message.Action, payload, userID)
@@ -540,7 +540,7 @@ func (pa *PluginAdapter) BroadcastCategoryBoardsReorder(teamID, userID, category
UserID: userID,
}
- pa.sendMessageToCluster("websocket_message", clusterMessage)
+ pa.sendMessageToCluster(clusterMessage)
}()
pa.sendUserMessageSkipCluster(message.Action, payload, userID)
@@ -568,7 +568,7 @@ func (pa *PluginAdapter) BroadcastCategoryBoardChange(teamID, userID string, boa
UserID: userID,
}
- pa.sendMessageToCluster("websocket_message", clusterMessage)
+ pa.sendMessageToCluster(clusterMessage)
}()
pa.sendUserMessageSkipCluster(websocketActionUpdateCategoryBoard, utils.StructToMap(message), userID)
diff --git a/server/ws/plugin_adapter_cluster.go b/server/ws/plugin_adapter_cluster.go
index 754b86afa..f326a61b7 100644
--- a/server/ws/plugin_adapter_cluster.go
+++ b/server/ws/plugin_adapter_cluster.go
@@ -15,7 +15,8 @@ type ClusterMessage struct {
EnsureUsers []string
}
-func (pa *PluginAdapter) sendMessageToCluster(id string, clusterMessage *ClusterMessage) {
+func (pa *PluginAdapter) sendMessageToCluster(clusterMessage *ClusterMessage) {
+ const id = "websocket_message"
b, err := json.Marshal(clusterMessage)
if err != nil {
pa.logger.Error("couldn't get JSON bytes from cluster message",
diff --git a/webapp/cypress/integration/createBoard.ts b/webapp/cypress/integration/createBoard.ts
index 7440fbeb2..27430dad0 100644
--- a/webapp/cypress/integration/createBoard.ts
+++ b/webapp/cypress/integration/createBoard.ts
@@ -28,7 +28,7 @@ describe('Create and delete board / card', () => {
cy.contains('Project Tasks').should('exist')
// Create empty board
- cy.contains('Create empty board').should('exist').click({force: true})
+ cy.contains('Create an empty board').should('exist').click({force: true})
cy.get('.BoardComponent').should('exist')
cy.get('.Editable.title').invoke('attr', 'placeholder').should('contain', 'Untitled board')
diff --git a/webapp/cypress/support/ui_commands.ts b/webapp/cypress/support/ui_commands.ts
index 829022b65..14972e382 100644
--- a/webapp/cypress/support/ui_commands.ts
+++ b/webapp/cypress/support/ui_commands.ts
@@ -17,5 +17,5 @@ Cypress.Commands.add('uiCreateEmptyBoard', () => {
cy.log('Create new empty board')
cy.contains('+ Add board').should('be.visible').click().wait(500)
- return cy.contains('Create empty board').click({force: true}).wait(1000)
+ return cy.contains('Create an empty board').click({force: true}).wait(1000)
})
diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json
index 08ff70a4f..b7a90daf5 100644
--- a/webapp/i18n/en.json
+++ b/webapp/i18n/en.json
@@ -1,12 +1,12 @@
{
- "AppBar.Tooltip": "Toggle Linked Boards",
+ "AppBar.Tooltip": "Toggle linked boards",
"Attachment.Attachment-title": "Attachment",
"AttachmentBlock.DeleteAction": "delete",
"AttachmentBlock.addElement": "add {type}",
- "AttachmentBlock.delete": "Attachment Deleted Successfully.",
- "AttachmentBlock.failed": "Unable to upload the file. Attachment size limit reached.",
+ "AttachmentBlock.delete": "Attachment deleted.",
+ "AttachmentBlock.failed": "This file couldn't be uploaded as the file size limit has been reached.",
"AttachmentBlock.upload": "Attachment uploading.",
- "AttachmentBlock.uploadSuccess": "Attachment uploaded successfull.",
+ "AttachmentBlock.uploadSuccess": "Attachment uploaded.",
"AttachmentElement.delete-confirmation-dialog-button-text": "Delete",
"AttachmentElement.download": "Download",
"AttachmentElement.upload-percentage": "Uploading...({uploadPercent}%)",
@@ -16,7 +16,7 @@
"BoardComponent.hide": "Hide",
"BoardComponent.new": "+ New",
"BoardComponent.no-property": "No {property}",
- "BoardComponent.no-property-title": "Items with an empty {property} property will go here. This column cannot be removed.",
+ "BoardComponent.no-property-title": "Items with an empty {property} property will go here. This column can't be removed.",
"BoardComponent.show": "Show",
"BoardMember.schemeAdmin": "Admin",
"BoardMember.schemeCommenter": "Commenter",
@@ -27,7 +27,7 @@
"BoardPage.newVersion": "A new version of Boards is available, click here to reload.",
"BoardPage.syncFailed": "Board may be deleted or access revoked.",
"BoardTemplateSelector.add-template": "Create new template",
- "BoardTemplateSelector.create-empty-board": "Create empty board",
+ "BoardTemplateSelector.create-empty-board": "Create an empty board",
"BoardTemplateSelector.delete-template": "Delete",
"BoardTemplateSelector.description": "Add a board to the sidebar using any of the templates defined below or start from scratch.",
"BoardTemplateSelector.edit-template": "Edit",
@@ -35,7 +35,7 @@
"BoardTemplateSelector.plugin.no-content-title": "Create a board",
"BoardTemplateSelector.title": "Create a board",
"BoardTemplateSelector.use-this-template": "Use this template",
- "BoardsSwitcher.Title": "Find Boards",
+ "BoardsSwitcher.Title": "Find boards",
"BoardsUnfurl.Limited": "Additional details are hidden due to the card being archived",
"BoardsUnfurl.Remainder": "+{remainder} more",
"BoardsUnfurl.Updated": "Updated {time}",
@@ -88,7 +88,7 @@
"CardDetail.add-icon": "Add icon",
"CardDetail.add-property": "+ Add a property",
"CardDetail.addCardText": "add card text",
- "CardDetail.limited-body": "Upgrade to our Professional or Enterprise plan to view archived cards, have unlimited views per boards, unlimited cards and more.",
+ "CardDetail.limited-body": "Upgrade to our Professional or Enterprise plan.",
"CardDetail.limited-button": "Upgrade",
"CardDetail.limited-title": "This card is hidden",
"CardDetail.moveContent": "Move card content",
@@ -103,9 +103,9 @@
"CardDetailProperty.property-deleted": "Deleted {propertyName} successfully!",
"CardDetailProperty.property-name-change-subtext": "type from \"{oldPropType}\" to \"{newPropType}\"",
"CardDetial.limited-link": "Learn more about our plans.",
- "CardDialog.delete-confirmation-dialog-attachment": "Confirm Attachment delete!",
+ "CardDialog.delete-confirmation-dialog-attachment": "Confirm attachment delete",
"CardDialog.delete-confirmation-dialog-button-text": "Delete",
- "CardDialog.delete-confirmation-dialog-heading": "Confirm card delete!",
+ "CardDialog.delete-confirmation-dialog-heading": "Confirm card delete",
"CardDialog.editing-template": "You're editing a template.",
"CardDialog.nocard": "This card doesn't exist or is inaccessible.",
"Categories.CreateCategoryDialog.CancelText": "Cancel",
@@ -119,6 +119,8 @@
"ColorOption.selectColor": "Select {color} Color",
"Comment.delete": "Delete",
"CommentsList.send": "Send",
+ "ConfirmPerson.empty": "Empty",
+ "ConfirmPerson.search": "Search...",
"ConfirmationDialog.cancel-action": "Cancel",
"ConfirmationDialog.confirm-action": "Confirm",
"ContentBlock.Delete": "Delete",
@@ -166,6 +168,7 @@
"FilterByText.placeholder": "filter text",
"FilterComponent.add-filter": "+ Add filter",
"FilterComponent.delete": "Delete",
+ "FilterValue.empty": "(empty)",
"FindBoardsDialog.IntroText": "Search for boards",
"FindBoardsDialog.NoResultsFor": "No results for \"{searchQuery}\"",
"FindBoardsDialog.NoResultsSubtext": "Check the spelling or try another search.",
@@ -184,7 +187,7 @@
"OnboardingTour.AddComments.Title": "Add comments",
"OnboardingTour.AddDescription.Body": "Add a description to your card so your teammates know what the card is about.",
"OnboardingTour.AddDescription.Title": "Add description",
- "OnboardingTour.AddProperties.Body": "Add various properties to cards to make them more powerful!",
+ "OnboardingTour.AddProperties.Body": "Add various properties to cards to make them more powerful.",
"OnboardingTour.AddProperties.Title": "Add properties",
"OnboardingTour.AddView.Body": "Go here to create a new view to organise your board using different layouts.",
"OnboardingTour.AddView.Title": "Add a new view",
@@ -195,6 +198,7 @@
"OnboardingTour.ShareBoard.Body": "You can share your board internally, within your team, or publish it publicly for visibility outside of your organization.",
"OnboardingTour.ShareBoard.Title": "Share board",
"PersonProperty.board-members": "Board members",
+ "PersonProperty.me": "Me",
"PersonProperty.non-board-members": "Not board members",
"PropertyMenu.Delete": "Delete",
"PropertyMenu.changeType": "Change property type",
@@ -233,11 +237,11 @@
"ShareBoard.copyLink": "Copy link",
"ShareBoard.regenerate": "Regenerate token",
"ShareBoard.searchPlaceholder": "Search for people and channels",
- "ShareBoard.teamPermissionsText": "Everyone at {teamName} Team",
+ "ShareBoard.teamPermissionsText": "Everyone at {teamName} team",
"ShareBoard.tokenRegenrated": "Token regenerated",
"ShareBoard.userPermissionsRemoveMemberText": "Remove member",
"ShareBoard.userPermissionsYouText": "(You)",
- "ShareTemplate.Title": "Share Template",
+ "ShareTemplate.Title": "Share template",
"ShareTemplate.searchPlaceholder": "Search for people",
"Sidebar.about": "About Focalboard",
"Sidebar.add-board": "+ Add board",
@@ -273,8 +277,8 @@
"SidebarTour.SidebarCategories.Body": "All your boards are now organized under your new sidebar. No more switching between workspaces. One-time custom categories based on your prior workspaces may have automatically been created for you as part of your v7.2 upgrade. These can be removed or edited to your preference.",
"SidebarTour.SidebarCategories.Link": "Learn more",
"SidebarTour.SidebarCategories.Title": "Sidebar categories",
- "SiteStats.total_boards": "Total Boards",
- "SiteStats.total_cards": "Total Cards",
+ "SiteStats.total_boards": "Total boards",
+ "SiteStats.total_cards": "Total cards",
"TableComponent.add-icon": "Add icon",
"TableComponent.name": "Name",
"TableComponent.plus-new": "+ New",
@@ -285,8 +289,8 @@
"TableHeaderMenu.insert-right": "Insert right",
"TableHeaderMenu.sort-ascending": "Sort ascending",
"TableHeaderMenu.sort-descending": "Sort descending",
+ "TableRow.DuplicateCard": "duplicate card",
"TableRow.MoreOption": "More actions",
- "TableRow.delete": "Delete",
"TableRow.open": "Open",
"TopBar.give-feedback": "Give feedback",
"URLProperty.copiedLink": "Copied!",
@@ -338,9 +342,9 @@
"ViewLimitDialog.Heading": "Views per board limit reached",
"ViewLimitDialog.PrimaryButton.Title.Admin": "Upgrade",
"ViewLimitDialog.PrimaryButton.Title.RegularUser": "Notify Admin",
- "ViewLimitDialog.Subtext.Admin": "Upgrade to our Professional or Enterprise plan to have unlimited views per boards, unlimited cards, and more.",
+ "ViewLimitDialog.Subtext.Admin": "Upgrade to our Professional or Enterprise plan.",
"ViewLimitDialog.Subtext.Admin.PricingPageLink": "Learn more about our plans.",
- "ViewLimitDialog.Subtext.RegularUser": "Notify your Admin to upgrade to our Professional or Enterprise plan to have unlimited views per boards, unlimited cards, and more.",
+ "ViewLimitDialog.Subtext.RegularUser": "Notify your Admin to upgrade to our Professional or Enterprise plan.",
"ViewLimitDialog.UpgradeImg.AltText": "upgrade image",
"ViewLimitDialog.notifyAdmin.Success": "Your admin has been notified",
"ViewTitle.hide-description": "hide description",
@@ -368,11 +372,13 @@
"calendar.month": "Month",
"calendar.today": "TODAY",
"calendar.week": "Week",
+ "centerPanel.undefined": "No {propertyName}",
+ "centerPanel.unknown-user": "Unknown user",
"cloudMessage.learn-more": "Learn more",
- "createImageBlock.failed": "Unable to upload the file. File size limit reached.",
+ "createImageBlock.failed": "This file couldn't be uploaded as the file size limit has been reached.",
"default-properties.badges": "Comments and description",
"default-properties.title": "Title",
- "error.back-to-home": "Back to Home",
+ "error.back-to-home": "Back to home",
"error.back-to-team": "Back to team",
"error.board-not-found": "Board not found.",
"error.go-login": "Log in",
@@ -384,11 +390,15 @@
"generic.previous": "Previous",
"guest-no-board.subtitle": "You don't have access to any board in this team yet, please wait until somebody adds you to any board.",
"guest-no-board.title": "No boards yet",
- "imagePaste.upload-failed": "Some files not uploaded. File size limit reached",
+ "imagePaste.upload-failed": "Some files weren't uploaded because the file size limit has been reached.",
"limitedCard.title": "Cards hidden",
"login.log-in-button": "Log in",
"login.log-in-title": "Log in",
"login.register-button": "or create an account if you don't have one",
+ "new_channel_modal.create_board.empty_board_description": "Create a new empty board",
+ "new_channel_modal.create_board.empty_board_title": "Empty board",
+ "new_channel_modal.create_board.select_template_placeholder": "Select a template",
+ "new_channel_modal.create_board.title": "Create a board for this channel",
"notification-box-card-limit-reached.close-tooltip": "Snooze for 10 days",
"notification-box-card-limit-reached.contact-link": "notify your admin",
"notification-box-card-limit-reached.link": "Upgrade to a paid plan",
@@ -400,15 +410,15 @@
"person.add-user-to-board-confirm-button": "Add to board",
"person.add-user-to-board-permissions": "Permissions",
"person.add-user-to-board-question": "Do you want to add {username} to the board?",
- "person.add-user-to-board-warning": "{username} is not a member of the board, and will not receive any notifications about it.",
+ "person.add-user-to-board-warning": "{username} isn't a member of the board, and won't receive any notifications about it.",
"register.login-button": "or log in if you already have an account",
"register.signup-title": "Sign up for your account",
- "rhs-board-non-admin-msg": "You are not an admin of the board",
+ "rhs-board-non-admin-msg": "You're not an admin of the board",
"rhs-boards.add": "Add",
"rhs-boards.dm": "DM",
"rhs-boards.gm": "GM",
- "rhs-boards.header.dm": "this Direct Message",
- "rhs-boards.header.gm": "this Group Message",
+ "rhs-boards.header.dm": "this direct message",
+ "rhs-boards.header.gm": "this group message",
"rhs-boards.last-update-at": "Last update at: {datetime}",
"rhs-boards.link-boards-to-channel": "Link boards to {channelName}",
"rhs-boards.linked-boards": "Linked boards",
@@ -439,4 +449,4 @@
"tutorial_tip.ok": "Next",
"tutorial_tip.out": "Opt out of these tips.",
"tutorial_tip.seen": "Seen this before?"
-}
\ No newline at end of file
+}
diff --git a/webapp/i18n/zh_Hant.json b/webapp/i18n/zh_Hant.json
index 168571638..6079d0137 100644
--- a/webapp/i18n/zh_Hant.json
+++ b/webapp/i18n/zh_Hant.json
@@ -165,7 +165,7 @@
"FilterByText.placeholder": "過濾文字",
"FilterComponent.add-filter": "+ 增加過濾條件",
"FilterComponent.delete": "刪除",
- "FindBoardsDialog.IntroText": "查詢面板",
+ "FindBoardsDialog.IntroText": "查詢看板",
"FindBoardsDialog.NoResultsFor": "「{searchQuery}」搜尋未果",
"FindBoardsDialog.NoResultsSubtext": "檢查錯字或嘗試其他搜尋.",
"FindBoardsDialog.SubTitle": "輸入已找到面板.使用 UP/DOWN來瀏覽.ENTER來搜尋, ESC 來取消",
@@ -313,8 +313,9 @@
"View.NewTemplateDefaultTitle": "沒有標題的模板",
"View.NewTemplateTitle": "沒有標題",
"View.Table": "圖表",
- "ViewHeader.add-template": "+ 新範本",
+ "ViewHeader.add-template": "新範本",
"ViewHeader.delete-template": "刪除",
+ "ViewHeader.display-by": "依據{property}顯示",
"ViewHeader.edit-template": "編輯",
"ViewHeader.empty-card": "清空卡片",
"ViewHeader.export-board-archive": "匯出版面打包檔",
@@ -331,11 +332,14 @@
"ViewHeader.set-default-template": "設為預設",
"ViewHeader.sort": "排序",
"ViewHeader.untitled": "無標題",
+ "ViewHeader.view-header-menu": "查看標題菜單",
"ViewHeader.view-menu": "查看菜單",
+ "ViewLimitDialog.Heading": "已達到每個看板觀看限制",
"ViewLimitDialog.PrimaryButton.Title.Admin": "升級",
"ViewLimitDialog.PrimaryButton.Title.RegularUser": "通知管理者",
"ViewLimitDialog.Subtext.Admin": "升級到專業版或企業版,獲得每個看板無限瀏覽、無限卡片,以及更多。",
- "ViewLimitDialog.Subtext.Admin.PricingPageLink": "了解更多我們的計畫",
+ "ViewLimitDialog.Subtext.Admin.PricingPageLink": "了解更多我們的計畫。",
+ "ViewLimitDialog.Subtext.RegularUser": "通知你的管理員升級到專業版或是企業版,獲得無限使用看板、卡片、更多。",
"ViewLimitDialog.UpgradeImg.AltText": "升級圖片",
"ViewLimitDialog.notifyAdmin.Success": "已經通知管理者",
"ViewTitle.hide-description": "隱藏敘述",
@@ -344,14 +348,17 @@
"ViewTitle.remove-icon": "移除圖示",
"ViewTitle.show-description": "顯示敘述",
"ViewTitle.untitled-board": "未命名版面",
+ "WelcomePage.Description": "看板是一個專案管理工具,可以使用熟悉的圖表幫助我們定義、組織、追蹤和管理跨團隊工作。",
"WelcomePage.Explore.Button": "探索",
- "WelcomePage.Heading": "歡迎來到版面",
+ "WelcomePage.Heading": "歡迎來到看板",
"WelcomePage.NoThanks.Text": "不需要,自己想辦法",
"WelcomePage.StartUsingIt.Text": "開始使用",
"Workspace.editing-board-template": "您正在編輯版面範本。",
"badge.guest": "訪客",
"boardSelector.confirm-link-board": "連結看板與頻道",
"boardSelector.confirm-link-board-button": "是,連結看版",
+ "boardSelector.confirm-link-board-subtext": "當你將\"{boardName}\"連接到頻道時,該頻道的所有成員(現有的和新的)都可以編輯。並不包含訪客身分。你可以在任何時候從一個頻道上取消看板的連接。",
+ "boardSelector.confirm-link-board-subtext-with-other-channel": "當你將\"{boardName}\"連接到頻道時,該頻道的所有成員(現有的和新的)都可以編輯。並不包含訪客身分。{lineBreak} 看板目前正連接到另一個頻道。如果选择在這裡連接它,將取消另一個連接。",
"boardSelector.create-a-board": "建立看版",
"boardSelector.link": "連結",
"boardSelector.search-for-boards": "搜尋看板",
@@ -369,12 +376,12 @@
"error.board-not-found": "沒有找到看板.",
"error.go-login": "登入",
"error.invalid-read-only-board": "沒有權限進入此看版.登入後才能訪問.",
- "error.not-logged-in": "已被登出,請再次登入使用看板",
+ "error.not-logged-in": "已被登出,請再次登入使用看板。",
"error.page.title": "很抱歉,發生了些錯誤",
- "error.team-undefined": "不是有效的團隊",
+ "error.team-undefined": "不是有效的團隊。",
"error.unknown": "發生一個錯誤。",
"generic.previous": "上一篇",
- "guest-no-board.subtitle": "你尚未有權限進入此看板,請等人把你加入任何看板",
+ "guest-no-board.subtitle": "你尚未有權限進入此看板,請等人把你加入任何看板。",
"guest-no-board.title": "尚未有看版",
"imagePaste.upload-failed": "有些檔案無法上傳.檔案大小達上限",
"limitedCard.title": "影藏卡片",
@@ -386,7 +393,8 @@
"notification-box-card-limit-reached.link": "升級到付費版",
"notification-box-card-limit-reached.title": "將看板上{cards}卡片隱藏",
"notification-box-cards-hidden.title": "此行為隱藏了其他卡片",
- "notification-box.card-limit-reached.not-admin.text": "要存取已封存的卡片,你可以點擊{contactLink}升級到付費版",
+ "notification-box.card-limit-reached.not-admin.text": "要存取已封存的卡片,你可以點擊{contactLink}升級到付費版。",
+ "notification-box.card-limit-reached.text": "已達卡片上限,觀看舊卡片請點{link}",
"person.add-user-to-board": "將{username} 加入看板",
"person.add-user-to-board-confirm-button": "新增看板",
"person.add-user-to-board-permissions": "權限",
@@ -396,22 +404,34 @@
"register.signup-title": "註冊您的帳戶",
"rhs-board-non-admin-msg": "你不是看板的管理者",
"rhs-boards.add": "新增",
+ "rhs-boards.dm": "私人訊息",
+ "rhs-boards.gm": "群組訊息",
+ "rhs-boards.header.dm": "此私人訊息",
"rhs-boards.header.gm": "此群組訊息",
"rhs-boards.last-update-at": "最後更新日: {datetime}",
+ "rhs-boards.link-boards-to-channel": "將看板連接到{channelName}",
"rhs-boards.linked-boards": "連結看板",
+ "rhs-boards.no-boards-linked-to-channel": "還沒有看板與{channelName}連接",
+ "rhs-boards.no-boards-linked-to-channel-description": "看板是一個專案管理工具,可以使用熟悉的圖表幫助我們定義、組織、追蹤和管理跨團隊工作。",
"rhs-boards.unlink-board": "未連結看版",
"rhs-boards.unlink-board1": "未連結看版",
"rhs-channel-boards-header.title": "板塊",
"share-board.publish": "發布",
"share-board.share": "分享",
"shareBoard.channels-select-group": "頻道",
+ "shareBoard.confirm-change-team-role.body": "此看板上所有低於\"{role}\"的人都將被提升到{role}。你確定要改變這個看板最低角色?",
"shareBoard.confirm-change-team-role.confirmBtnText": "改變最小的看板規則",
"shareBoard.confirm-change-team-role.title": "改變最小的看板規則",
"shareBoard.confirm-link-channel": "連接看板到頻道",
"shareBoard.confirm-link-channel-button": "連接頻道",
"shareBoard.confirm-link-channel-button-with-other-channel": "解除連接或連接這",
- "shareBoard.confirm-link-channel-subtext": "當你連接頻道到看板,該頻道所有成員(包含新的與現有的)都可以編輯,不包括訪客",
+ "shareBoard.confirm-link-channel-subtext": "當你連接頻道到看板,該頻道所有成員(包含新的與現有的)都可以編輯,不包括訪客。",
+ "shareBoard.confirm-link-channel-subtext-with-other-channel": "當你將一個頻道連接到看板時,該頻道的所有成員(現有的和新的)都可以編輯。並不包含訪客身分{lineBreak}看板目前正連接到另一個頻道。如果选择在這裡連接它,將取消另一個連接。",
+ "shareBoard.confirm-unlink.body": "當你取消頻道與看板連接,所有頻道成員(現在和新的)都將無法失去查看權限,除非單獨獲得許可。",
"shareBoard.confirm-unlink.confirmBtnText": "解除連結頻道",
+ "shareBoard.confirm-unlink.title": "從看板上取消頻道連接",
+ "shareBoard.lastAdmin": "看板必須有一位管理者",
+ "shareBoard.members-select-group": "會員",
"shareBoard.unknown-channel-display-name": "未知管道",
"tutorial_tip.finish_tour": "完成",
"tutorial_tip.got_it": "了解",
diff --git a/webapp/package-lock.json b/webapp/package-lock.json
index 3025de89d..65f21b94b 100644
--- a/webapp/package-lock.json
+++ b/webapp/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "focalboard",
- "version": "7.8.0",
+ "version": "7.9.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "focalboard",
- "version": "7.8.0",
+ "version": "7.9.0",
"dependencies": {
"@draft-js-plugins/editor": "^4.1.2",
"@draft-js-plugins/emoji": "^4.6.0",
diff --git a/webapp/package.json b/webapp/package.json
index 2127e23b8..d26d1e3ae 100644
--- a/webapp/package.json
+++ b/webapp/package.json
@@ -1,6 +1,6 @@
{
"name": "focalboard",
- "version": "7.8.0",
+ "version": "7.9.0",
"private": true,
"description": "",
"scripts": {
diff --git a/webapp/src/blocks/filterClause.test.ts b/webapp/src/blocks/filterClause.test.ts
new file mode 100644
index 000000000..e55660564
--- /dev/null
+++ b/webapp/src/blocks/filterClause.test.ts
@@ -0,0 +1,64 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+import {areEqual, createFilterClause} from './filterClause'
+
+describe('filterClause tests', () => {
+ it('create filter clause', () => {
+ const clause = createFilterClause({
+ propertyId: 'myPropertyId',
+ condition: 'contains',
+ values: [],
+ })
+
+ expect(clause).toEqual({
+ propertyId: 'myPropertyId',
+ condition: 'contains',
+ values: [],
+ })
+ })
+
+ it('test filter clauses are equal', () => {
+ const clause = createFilterClause({
+ propertyId: 'myPropertyId',
+ condition: 'contains',
+ values: ['abc', 'def'],
+ })
+ const newClause = createFilterClause(clause)
+ const testEqual = areEqual(clause, newClause)
+ expect(testEqual).toBeTruthy()
+ })
+
+ it('test filter clauses are Not equal property ID', () => {
+ const clause = createFilterClause({
+ propertyId: 'myPropertyId',
+ condition: 'contains',
+ values: ['abc', 'def'],
+ })
+ const newClause = createFilterClause(clause)
+ newClause.propertyId = 'DifferentID'
+ const testEqual = areEqual(clause, newClause)
+ expect(testEqual).toBeFalsy()
+ })
+ it('test filter clauses are Not equal condition', () => {
+ const clause = createFilterClause({
+ propertyId: 'myPropertyId',
+ condition: 'contains',
+ values: ['abc', 'def'],
+ })
+ const newClause = createFilterClause(clause)
+ newClause.condition = 'notContains'
+ const testEqual = areEqual(clause, newClause)
+ expect(testEqual).toBeFalsy()
+ })
+ it('test filter clauses are Not equal values', () => {
+ const clause = createFilterClause({
+ propertyId: 'myPropertyId',
+ condition: 'contains',
+ values: ['abc', 'def'],
+ })
+ const newClause = createFilterClause(clause)
+ newClause.values = ['abc, def']
+ const testEqual = areEqual(clause, newClause)
+ expect(testEqual).toBeFalsy()
+ })
+})
diff --git a/webapp/src/blocks/filterClause.ts b/webapp/src/blocks/filterClause.ts
index 166b05c24..bffcf79f8 100644
--- a/webapp/src/blocks/filterClause.ts
+++ b/webapp/src/blocks/filterClause.ts
@@ -2,7 +2,15 @@
// See LICENSE.txt for license information.
import {Utils} from '../utils'
-type FilterCondition = 'includes' | 'notIncludes' | 'isEmpty' | 'isNotEmpty' | 'isSet' | 'isNotSet' | 'is' | 'contains' | 'notContains' | 'startsWith' | 'notStartsWith' | 'endsWith' | 'notEndsWith'
+type FilterCondition =
+ 'includes' | 'notIncludes' |
+ 'isEmpty' | 'isNotEmpty' |
+ 'isSet' | 'isNotSet' |
+ 'is' |
+ 'contains' | 'notContains' |
+ 'startsWith' | 'notStartsWith' |
+ 'endsWith' | 'notEndsWith' |
+ 'isBefore' | 'isAfter'
type FilterClause = {
propertyId: string
diff --git a/webapp/src/boardUtils.ts b/webapp/src/boardUtils.ts
index 4f6fb731b..bb0776f91 100644
--- a/webapp/src/boardUtils.ts
+++ b/webapp/src/boardUtils.ts
@@ -1,11 +1,9 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-
-import {Utils} from './utils'
import {Card} from './blocks/card'
import {IPropertyTemplate, IPropertyOption, BoardGroup} from './blocks/board'
-export function groupCardsByOptions(cards: Card[], optionIds: string[], groupByProperty?: IPropertyTemplate): BoardGroup[] {
+function groupCardsByOptions(cards: Card[], optionIds: string[], groupByProperty?: IPropertyTemplate): BoardGroup[] {
const groups = []
for (const optionId of optionIds) {
if (optionId) {
@@ -18,7 +16,7 @@ export function groupCardsByOptions(cards: Card[], optionIds: string[], groupByP
}
groups.push(group)
} else {
- Utils.logError(`groupCardsByOptions: Missing option with id: ${optionId}`)
+ // if optionId not found, its an old (deleted) option that can be ignored
}
} else {
// Empty group
@@ -36,7 +34,7 @@ export function groupCardsByOptions(cards: Card[], optionIds: string[], groupByP
return groups
}
-export function getVisibleAndHiddenGroups(cards: Card[], visibleOptionIds: string[], hiddenOptionIds: string[], groupByProperty?: IPropertyTemplate): {visible: BoardGroup[], hidden: BoardGroup[]} {
+function getOptionGroups(cards: Card[], visibleOptionIds: string[], hiddenOptionIds: string[], groupByProperty?: IPropertyTemplate): {visible: BoardGroup[], hidden: BoardGroup[]} {
let unassignedOptionIds: string[] = []
if (groupByProperty) {
unassignedOptionIds = groupByProperty.options.
@@ -54,3 +52,37 @@ export function getVisibleAndHiddenGroups(cards: Card[], visibleOptionIds: strin
const hiddenGroups = groupCardsByOptions(cards, hiddenOptionIds, groupByProperty)
return {visible: visibleGroups, hidden: hiddenGroups}
}
+export function getVisibleAndHiddenGroups(cards: Card[], visibleOptionIds: string[], hiddenOptionIds: string[], groupByProperty?: IPropertyTemplate): {visible: BoardGroup[], hidden: BoardGroup[]} {
+ if (groupByProperty?.type === 'createdBy' || groupByProperty?.type === 'updatedBy' || groupByProperty?.type === 'person') {
+ return getPersonGroups(cards, groupByProperty, hiddenOptionIds)
+ }
+
+ return getOptionGroups(cards, visibleOptionIds, hiddenOptionIds, groupByProperty)
+}
+
+function getPersonGroups(cards: Card[], groupByProperty: IPropertyTemplate, hiddenOptionIds: string[]): {visible: BoardGroup[], hidden: BoardGroup[]} {
+ const groups = cards.reduce((unique: {[key: string]: Card[]}, item: Card): {[key: string]: Card[]} => {
+ let key = item.fields.properties[groupByProperty.id] as string
+ if (groupByProperty?.type === 'createdBy') {
+ key = item.createdBy
+ } else if (groupByProperty?.type === 'updatedBy') {
+ key = item.modifiedBy
+ }
+
+ const curGroup = unique[key] ?? []
+ return {...unique, [key]: [...curGroup, item]}
+ }, {})
+
+ const hiddenGroups: BoardGroup[] = []
+ const visibleGroups: BoardGroup[] = []
+ Object.entries(groups).forEach(([key, value]) => {
+ const propertyOption = {id: key, value: key, color: ''} as IPropertyOption
+ if (hiddenOptionIds.find((e) => e === key)) {
+ hiddenGroups.push({option: propertyOption, cards: value})
+ } else {
+ visibleGroups.push({option: propertyOption, cards: value})
+ }
+ })
+
+ return {visible: visibleGroups, hidden: hiddenGroups}
+}
diff --git a/webapp/src/cardFilter.test.ts b/webapp/src/cardFilter.test.ts
index 9f82add41..2bb003798 100644
--- a/webapp/src/cardFilter.test.ts
+++ b/webapp/src/cardFilter.test.ts
@@ -8,10 +8,14 @@ import {createFilterGroup} from './blocks/filterGroup'
import {CardFilter} from './cardFilter'
import {TestBlockFactory} from './test/testBlockFactory'
import {Utils} from './utils'
+
import {IPropertyTemplate} from './blocks/board'
jest.mock('./utils')
const mockedUtils = mocked(Utils, true)
+
+const dayMillis = 24 * 60 * 60 * 1000
+
describe('src/cardFilter', () => {
const board = TestBlockFactory.createBoard()
board.id = '1'
@@ -21,6 +25,7 @@ describe('src/cardFilter', () => {
card1.title = 'card1'
card1.fields.properties.propertyId = 'Status'
const filterClause = createFilterClause({propertyId: 'propertyId', condition: 'isNotEmpty', values: ['Status']})
+
describe('verify isClauseMet method', () => {
test('should be true with isNotEmpty clause', () => {
const filterClauseIsNotEmpty = createFilterClause({propertyId: 'propertyId', condition: 'isNotEmpty', values: ['Status']})
@@ -53,6 +58,339 @@ describe('src/cardFilter', () => {
expect(result).toBeTruthy()
})
})
+
+ describe('verify isClauseMet method - person property', () => {
+ const personCard = TestBlockFactory.createCard(board)
+ personCard.id = '1'
+ personCard.title = 'card1'
+ personCard.fields.properties.personPropertyID = 'personid1'
+
+ const template: IPropertyTemplate = {
+ id: 'personPropertyID',
+ name: 'myPerson',
+ type: 'person',
+ options: [],
+ }
+
+ test('should be true with isNotEmpty clause', () => {
+ const filterClauseIsNotEmpty = createFilterClause({propertyId: 'personPropertyID', condition: 'isNotEmpty', values: []})
+ const result = CardFilter.isClauseMet(filterClauseIsNotEmpty, [template], personCard)
+ expect(result).toBeTruthy()
+ })
+ test('should be false with isEmpty clause', () => {
+ const filterClauseIsEmpty = createFilterClause({propertyId: 'personPropertyID', condition: 'isEmpty', values: []})
+ const result = CardFilter.isClauseMet(filterClauseIsEmpty, [template], personCard)
+ expect(result).toBeFalsy()
+ })
+ test('verify empty includes clause', () => {
+ const filterClauseIncludes = createFilterClause({propertyId: 'personPropertyID', condition: 'includes', values: []})
+ const result = CardFilter.isClauseMet(filterClauseIncludes, [template], personCard)
+ expect(result).toBeTruthy()
+ })
+ test('verify includes clause', () => {
+ const filterClauseIncludes = createFilterClause({propertyId: 'personPropertyID', condition: 'includes', values: ['personid1']})
+ const result = CardFilter.isClauseMet(filterClauseIncludes, [template], personCard)
+ expect(result).toBeTruthy()
+ })
+ test('verify includes clause multiple values', () => {
+ const filterClauseIncludes = createFilterClause({propertyId: 'personPropertyID', condition: 'includes', values: ['personid2', 'personid1']})
+ const result = CardFilter.isClauseMet(filterClauseIncludes, [template], personCard)
+ expect(result).toBeTruthy()
+ })
+ test('verify not includes clause', () => {
+ const filterClauseIncludes = createFilterClause({propertyId: 'personPropertyID', condition: 'notIncludes', values: ['personid2']})
+ const result = CardFilter.isClauseMet(filterClauseIncludes, [template], personCard)
+ expect(result).toBeTruthy()
+ })
+ })
+
+ describe('verify isClauseMet method - multi-person property', () => {
+ const personCard = TestBlockFactory.createCard(board)
+ personCard.id = '1'
+ personCard.title = 'card1'
+ personCard.fields.properties.personPropertyID = ['personid1', 'personid2']
+
+ const template: IPropertyTemplate = {
+ id: 'personPropertyID',
+ name: 'myPerson',
+ type: 'multiPerson',
+ options: [],
+ }
+
+ test('should be true with isNotEmpty clause', () => {
+ const filterClauseIsNotEmpty = createFilterClause({propertyId: 'personPropertyID', condition: 'isNotEmpty', values: []})
+ const result = CardFilter.isClauseMet(filterClauseIsNotEmpty, [template], personCard)
+ expect(result).toBeTruthy()
+ })
+ test('should be false with isEmpty clause', () => {
+ const filterClauseIsEmpty = createFilterClause({propertyId: 'personPropertyID', condition: 'isEmpty', values: []})
+ const result = CardFilter.isClauseMet(filterClauseIsEmpty, [template], personCard)
+ expect(result).toBeFalsy()
+ })
+ test('verify empty includes clause', () => {
+ const filterClauseIncludes = createFilterClause({propertyId: 'personPropertyID', condition: 'includes', values: []})
+ const result = CardFilter.isClauseMet(filterClauseIncludes, [template], personCard)
+ expect(result).toBeTruthy()
+ })
+ test('verify includes clause', () => {
+ const filterClauseIncludes = createFilterClause({propertyId: 'personPropertyID', condition: 'includes', values: ['personid1']})
+ const result = CardFilter.isClauseMet(filterClauseIncludes, [template], personCard)
+ expect(result).toBeTruthy()
+ })
+ test('verify includes clause 2', () => {
+ const filterClauseIncludes = createFilterClause({propertyId: 'personPropertyID', condition: 'includes', values: ['personid2']})
+ const result = CardFilter.isClauseMet(filterClauseIncludes, [template], personCard)
+ expect(result).toBeTruthy()
+ })
+ test('verify includes clause multiple values', () => {
+ const filterClauseIncludes = createFilterClause({propertyId: 'personPropertyID', condition: 'includes', values: ['personid3', 'personid1']})
+ const result = CardFilter.isClauseMet(filterClauseIncludes, [template], personCard)
+ expect(result).toBeTruthy()
+ })
+ test('verify includes clause multiple values 2', () => {
+ const filterClauseIncludes = createFilterClause({propertyId: 'personPropertyID', condition: 'includes', values: ['personid3', 'personid2']})
+ const result = CardFilter.isClauseMet(filterClauseIncludes, [template], personCard)
+ expect(result).toBeTruthy()
+ })
+ test('verify not includes clause', () => {
+ const filterClauseIncludes = createFilterClause({propertyId: 'personPropertyID', condition: 'notIncludes', values: ['personid3']})
+ const result = CardFilter.isClauseMet(filterClauseIncludes, [template], personCard)
+ expect(result).toBeTruthy()
+ })
+ test('verify not includes clause, multiple values', () => {
+ const filterClauseIncludes = createFilterClause({propertyId: 'personPropertyID', condition: 'notIncludes', values: ['personid3', 'personid4']})
+ const result = CardFilter.isClauseMet(filterClauseIncludes, [template], personCard)
+ expect(result).toBeTruthy()
+ })
+ })
+
+ describe('verify isClauseMet method - (createdBy) person property', () => {
+ const personCard = TestBlockFactory.createCard(board)
+ personCard.id = '1'
+ personCard.title = 'card1'
+ personCard.createdBy = 'personid1'
+
+ const template: IPropertyTemplate = {
+ id: 'personPropertyID',
+ name: 'myPerson',
+ type: 'createdBy',
+ options: [],
+ }
+
+ test('verify empty includes clause', () => {
+ const filterClauseIncludes = createFilterClause({propertyId: 'personPropertyID', condition: 'includes', values: []})
+ const result = CardFilter.isClauseMet(filterClauseIncludes, [template], personCard)
+ expect(result).toBeTruthy()
+ })
+ test('verify includes clause', () => {
+ const filterClauseIncludes = createFilterClause({propertyId: 'personPropertyID', condition: 'includes', values: ['personid1']})
+ const result = CardFilter.isClauseMet(filterClauseIncludes, [template], personCard)
+ expect(result).toBeTruthy()
+ })
+ test('verify includes clause multiple values', () => {
+ const filterClauseIncludes = createFilterClause({propertyId: 'personPropertyID', condition: 'includes', values: ['personid3', 'personid1']})
+ const result = CardFilter.isClauseMet(filterClauseIncludes, [template], personCard)
+ expect(result).toBeTruthy()
+ })
+ test('verify not includes clause', () => {
+ const filterClauseIncludes = createFilterClause({propertyId: 'personPropertyID', condition: 'notIncludes', values: ['personid2']})
+ const result = CardFilter.isClauseMet(filterClauseIncludes, [template], personCard)
+ expect(result).toBeTruthy()
+ })
+ })
+
+ describe('verify isClauseMet method - single date property', () => {
+ // Date Properties are stored as 12PM UTC.
+ const now = new Date(Date.now())
+ const propertyDate = Date.UTC(now.getFullYear(), now.getMonth(), now.getDate(), 12)
+
+ const dateCard = TestBlockFactory.createCard(board)
+ dateCard.id = '1'
+ dateCard.title = 'card1'
+ dateCard.fields.properties.datePropertyID = '{ "from": ' + propertyDate.toString() + ' }'
+
+ const checkDayBefore = propertyDate - dayMillis
+ const checkDayAfter = propertyDate + dayMillis
+
+ const template: IPropertyTemplate = {
+ id: 'datePropertyID',
+ name: 'myDate',
+ type: 'date',
+ options: [],
+ }
+
+ test('should be true with isSet clause', () => {
+ const filterClauseIsSet = createFilterClause({propertyId: 'datePropertyID', condition: 'isSet', values: []})
+ const result = CardFilter.isClauseMet(filterClauseIsSet, [template], dateCard)
+ expect(result).toBeTruthy()
+ })
+ test('should be false with notSet clause', () => {
+ const filterClauseIsNotSet = createFilterClause({propertyId: 'datePropertyID', condition: 'isNotSet', values: []})
+ const result = CardFilter.isClauseMet(filterClauseIsNotSet, [template], dateCard)
+ expect(result).toBeFalsy()
+ })
+ test('verify isBefore clause', () => {
+ const filterClauseIsBefore = createFilterClause({propertyId: 'datePropertyID', condition: 'isBefore', values: [checkDayAfter.toString()]})
+ const result = CardFilter.isClauseMet(filterClauseIsBefore, [template], dateCard)
+ expect(result).toBeTruthy()
+
+ const filterClauseIsNotBefore = createFilterClause({propertyId: 'datePropertyID', condition: 'isBefore', values: [checkDayBefore.toString()]})
+ const result2 = CardFilter.isClauseMet(filterClauseIsNotBefore, [template], dateCard)
+ expect(result2).toBeFalsy()
+ })
+ test('verify isAfter clauses', () => {
+ const filterClauseisAfter = createFilterClause({propertyId: 'datePropertyID', condition: 'isAfter', values: [checkDayBefore.toString()]})
+ const result = CardFilter.isClauseMet(filterClauseisAfter, [template], dateCard)
+ expect(result).toBeTruthy()
+
+ const filterClauseisNotAfter = createFilterClause({propertyId: 'datePropertyID', condition: 'isAfter', values: [checkDayAfter.toString()]})
+ const result2 = CardFilter.isClauseMet(filterClauseisNotAfter, [template], dateCard)
+ expect(result2).toBeFalsy()
+ })
+ test('verify is clause', () => {
+ const filterClauseIs = createFilterClause({propertyId: 'datePropertyID', condition: 'is', values: [propertyDate.toString()]})
+ const result = CardFilter.isClauseMet(filterClauseIs, [template], dateCard)
+ expect(result).toBeTruthy()
+
+ const filterClauseIsNot = createFilterClause({propertyId: 'datePropertyID', condition: 'is', values: [checkDayBefore.toString()]})
+ const result2 = CardFilter.isClauseMet(filterClauseIsNot, [template], dateCard)
+ expect(result2).toBeFalsy()
+ })
+ })
+
+ describe('verify isClauseMet method - date range property', () => {
+ // Date Properties are stored as 12PM UTC.
+ const now = new Date(Date.now())
+ const fromDate = Date.UTC(now.getFullYear(), now.getMonth(), now.getDate(), 12)
+ const toDate = fromDate + (2 * dayMillis)
+ const dateCard = TestBlockFactory.createCard(board)
+ dateCard.id = '1'
+ dateCard.title = 'card1'
+ dateCard.fields.properties.datePropertyID = '{ "from": ' + fromDate.toString() + ', "to": ' + toDate.toString() + ' }'
+
+ const beforeRange = fromDate - dayMillis
+ const afterRange = toDate + dayMillis
+ const inRange = fromDate + dayMillis
+
+ const template: IPropertyTemplate = {
+ id: 'datePropertyID',
+ name: 'myDate',
+ type: 'date',
+ options: [],
+ }
+
+ test('verify isBefore clause', () => {
+ const filterClauseIsBeforeEmpty = createFilterClause({propertyId: 'datePropertyID', condition: 'isBefore', values: []})
+ const resulta = CardFilter.isClauseMet(filterClauseIsBeforeEmpty, [template], dateCard)
+ expect(resulta).toBeTruthy()
+
+ const filterClauseIsBefore = createFilterClause({propertyId: 'datePropertyID', condition: 'isBefore', values: [beforeRange.toString()]})
+ const result = CardFilter.isClauseMet(filterClauseIsBefore, [template], dateCard)
+ expect(result).toBeFalsy()
+
+ const filterClauseIsInRange = createFilterClause({propertyId: 'datePropertyID', condition: 'isBefore', values: [inRange.toString()]})
+ const result2 = CardFilter.isClauseMet(filterClauseIsInRange, [template], dateCard)
+ expect(result2).toBeTruthy()
+
+ const filterClauseIsAfter = createFilterClause({propertyId: 'datePropertyID', condition: 'isBefore', values: [afterRange.toString()]})
+ const result3 = CardFilter.isClauseMet(filterClauseIsAfter, [template], dateCard)
+ expect(result3).toBeTruthy()
+ })
+
+ test('verify isAfter clauses', () => {
+ const filterClauseIsAfterEmpty = createFilterClause({propertyId: 'datePropertyID', condition: 'isBefore', values: []})
+ const resulta = CardFilter.isClauseMet(filterClauseIsAfterEmpty, [template], dateCard)
+ expect(resulta).toBeTruthy()
+
+ const filterClauseIsAfter = createFilterClause({propertyId: 'datePropertyID', condition: 'isAfter', values: [afterRange.toString()]})
+ const result = CardFilter.isClauseMet(filterClauseIsAfter, [template], dateCard)
+ expect(result).toBeFalsy()
+
+ const filterClauseIsInRange = createFilterClause({propertyId: 'datePropertyID', condition: 'isAfter', values: [inRange.toString()]})
+ const result2 = CardFilter.isClauseMet(filterClauseIsInRange, [template], dateCard)
+ expect(result2).toBeTruthy()
+
+ const filterClauseIsBefore = createFilterClause({propertyId: 'datePropertyID', condition: 'isAfter', values: [beforeRange.toString()]})
+ const result3 = CardFilter.isClauseMet(filterClauseIsBefore, [template], dateCard)
+ expect(result3).toBeTruthy()
+ })
+
+ test('verify is clause', () => {
+ const filterClauseIsEmpty = createFilterClause({propertyId: 'datePropertyID', condition: 'isBefore', values: []})
+ const resulta = CardFilter.isClauseMet(filterClauseIsEmpty, [template], dateCard)
+ expect(resulta).toBeTruthy()
+
+ const filterClauseIsBefore = createFilterClause({propertyId: 'datePropertyID', condition: 'is', values: [beforeRange.toString()]})
+ const result = CardFilter.isClauseMet(filterClauseIsBefore, [template], dateCard)
+ expect(result).toBeFalsy()
+
+ const filterClauseIsInRange = createFilterClause({propertyId: 'datePropertyID', condition: 'is', values: [inRange.toString()]})
+ const result2 = CardFilter.isClauseMet(filterClauseIsInRange, [template], dateCard)
+ expect(result2).toBeTruthy()
+
+ const filterClauseIsAfter = createFilterClause({propertyId: 'datePropertyID', condition: 'is', values: [afterRange.toString()]})
+ const result3 = CardFilter.isClauseMet(filterClauseIsAfter, [template], dateCard)
+ expect(result3).toBeFalsy()
+ })
+ })
+
+ describe('verify isClauseMet method - (createdTime) date property', () => {
+ const createDate = new Date(card1.createAt)
+ const checkDate = Date.UTC(createDate.getFullYear(), createDate.getMonth(), createDate.getDate(), 12)
+ const checkDayBefore = checkDate - dayMillis
+ const checkDayAfter = checkDate + dayMillis
+
+ const template: IPropertyTemplate = {
+ id: 'datePropertyID',
+ name: 'myDate',
+ type: 'createdTime',
+ options: [],
+ }
+
+ test('should be true with isSet clause', () => {
+ const filterClauseIsSet = createFilterClause({propertyId: 'datePropertyID', condition: 'isSet', values: []})
+ const result = CardFilter.isClauseMet(filterClauseIsSet, [template], card1)
+ expect(result).toBeTruthy()
+ })
+ test('should be false with notSet clause', () => {
+ const filterClauseIsNotSet = createFilterClause({propertyId: 'datePropertyID', condition: 'isNotSet', values: []})
+ const result = CardFilter.isClauseMet(filterClauseIsNotSet, [template], card1)
+ expect(result).toBeFalsy()
+ })
+ test('verify isBefore clause', () => {
+ const filterClauseIsBefore = createFilterClause({propertyId: 'datePropertyID', condition: 'isBefore', values: [checkDayAfter.toString()]})
+ const result = CardFilter.isClauseMet(filterClauseIsBefore, [template], card1)
+ expect(result).toBeTruthy()
+
+ const filterClauseIsNotBefore = createFilterClause({propertyId: 'datePropertyID', condition: 'isBefore', values: [checkDate.toString()]})
+ const result2 = CardFilter.isClauseMet(filterClauseIsNotBefore, [template], card1)
+ expect(result2).toBeFalsy()
+ })
+ test('verify isAfter clauses', () => {
+ const filterClauseisAfter = createFilterClause({propertyId: 'datePropertyID', condition: 'isAfter', values: [checkDayBefore.toString()]})
+ const result = CardFilter.isClauseMet(filterClauseisAfter, [template], card1)
+ expect(result).toBeTruthy()
+
+ const filterClauseisNotAfter = createFilterClause({propertyId: 'datePropertyID', condition: 'isAfter', values: [checkDate.toString()]})
+ const result2 = CardFilter.isClauseMet(filterClauseisNotAfter, [template], card1)
+ expect(result2).toBeFalsy()
+ })
+ test('verify is clause', () => {
+ // Is should find on that date regardless of time.
+ const filterClauseIs = createFilterClause({propertyId: 'datePropertyID', condition: 'is', values: [checkDate.toString()]})
+ const result = CardFilter.isClauseMet(filterClauseIs, [template], card1)
+ expect(result).toBeTruthy()
+
+ const filterClauseIsNot = createFilterClause({propertyId: 'datePropertyID', condition: 'is', values: [checkDayBefore.toString()]})
+ const result2 = CardFilter.isClauseMet(filterClauseIsNot, [template], card1)
+ expect(result2).toBeFalsy()
+
+ const filterClauseIsNot2 = createFilterClause({propertyId: 'datePropertyID', condition: 'is', values: [checkDayAfter.toString()]})
+ const result3 = CardFilter.isClauseMet(filterClauseIsNot2, [template], card1)
+ expect(result3).toBeFalsy()
+ })
+ })
+
describe('verify isFilterGroupMet method', () => {
test('should return true with no filter', () => {
const filterGroup = createFilterGroup({
@@ -243,6 +581,32 @@ describe('src/cardFilter', () => {
expect(result.value).toBeFalsy()
})
})
+ describe('verify propertyThatMeetsFilterClause method - Person properties', () => {
+ test('should return filterClause propertyId with template, and isEmpty clause', () => {
+ const filterClauseIsEmpty = createFilterClause({propertyId: 'propertyId', condition: 'is', values: []})
+ const templateFilter: IPropertyTemplate = {
+ id: filterClauseIsEmpty.propertyId,
+ name: 'template',
+ type: 'createdBy',
+ options: [],
+ }
+ const result = CardFilter.propertyThatMeetsFilterClause(filterClauseIsEmpty, [templateFilter])
+ expect(result.id).toEqual(filterClauseIsEmpty.propertyId)
+ expect(result.value).toBeFalsy()
+ })
+ test('should return filterClause propertyId with template, and isEmpty clause', () => {
+ const filterClauseIsEmpty = createFilterClause({propertyId: 'propertyId', condition: 'is', values: []})
+ const templateFilter: IPropertyTemplate = {
+ id: filterClauseIsEmpty.propertyId,
+ name: 'template',
+ type: 'createdBy',
+ options: [],
+ }
+ const result = CardFilter.propertyThatMeetsFilterClause(filterClauseIsEmpty, [templateFilter])
+ expect(result.id).toEqual(filterClauseIsEmpty.propertyId)
+ expect(result.value).toBeFalsy()
+ })
+ })
describe('verify propertiesThatMeetFilterGroup method', () => {
test('should return {} with undefined filterGroup', () => {
const result = CardFilter.propertiesThatMeetFilterGroup(undefined, [])
diff --git a/webapp/src/cardFilter.ts b/webapp/src/cardFilter.ts
index 4bd41e39c..17a0bd4f0 100644
--- a/webapp/src/cardFilter.ts
+++ b/webapp/src/cardFilter.ts
@@ -1,12 +1,35 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
+import {DateUtils} from 'react-day-picker'
+
+import {DateProperty} from './properties/date/date'
+
import {IPropertyTemplate} from './blocks/board'
import {Card} from './blocks/card'
import {FilterClause} from './blocks/filterClause'
import {FilterGroup, isAFilterGroupInstance} from './blocks/filterGroup'
import {Utils} from './utils'
+const halfDay = 12 * 60 * 60 * 1000
+
class CardFilter {
+ static createDatePropertyFromString(initialValue: string): DateProperty {
+ let dateProperty: DateProperty = {}
+ if (initialValue) {
+ const singleDate = new Date(Number(initialValue))
+ if (singleDate && DateUtils.isDate(singleDate)) {
+ dateProperty.from = singleDate.getTime()
+ } else {
+ try {
+ dateProperty = JSON.parse(initialValue)
+ } catch {
+ //Don't do anything, return empty dateProperty
+ }
+ }
+ }
+ return dateProperty
+ }
+
static applyFilterGroup(filterGroup: FilterGroup, templates: readonly IPropertyTemplate[], cards: Card[]): Card[] {
return cards.filter((card) => this.isFilterGroupMet(filterGroup, templates, card))
}
@@ -48,6 +71,25 @@ class CardFilter {
if (filter.propertyId === 'title') {
value = card.title.toLowerCase()
}
+ const template = templates.find((o) => o.id === filter.propertyId)
+ let dateValue: DateProperty | undefined
+ if (template?.type === 'date') {
+ dateValue = this.createDatePropertyFromString(value as string)
+ }
+ if (!value && template) {
+ if (template.type === 'createdBy') {
+ value = card.createdBy
+ } else if (template.type === 'updatedBy') {
+ value = card.modifiedBy
+ } else if (template && template.type === 'createdTime') {
+ value = card.createAt.toString()
+ dateValue = this.createDatePropertyFromString(value as string)
+ } else if (template && template.type === 'updatedTime') {
+ value = card.updateAt.toString()
+ dateValue = this.createDatePropertyFromString(value as string)
+ }
+ }
+
switch (filter.condition) {
case 'includes': {
if (filter.values?.length < 1) {
@@ -77,6 +119,23 @@ class CardFilter {
if (filter.values.length === 0) {
return true
}
+ if (dateValue !== undefined) {
+ const numericFilter = parseInt(filter.values[0], 10)
+ if (template && (template.type === 'createdTime' || template.type === 'updatedTime')) {
+ // createdTime and updatedTime include the time
+ // So to check if create and/or updated "is" date.
+ // Need to add and subtract 12 hours and check range
+ if (dateValue.from) {
+ return dateValue.from > (numericFilter - halfDay) && dateValue.from < (numericFilter + halfDay)
+ }
+ return false
+ }
+
+ if (dateValue.from && dateValue.to) {
+ return dateValue.from <= numericFilter && dateValue.to >= numericFilter
+ }
+ return dateValue.from === numericFilter
+ }
return filter.values[0]?.toLowerCase() === value
}
case 'contains': {
@@ -115,6 +174,50 @@ class CardFilter {
}
return !(value as string || '').endsWith(filter.values[0]?.toLowerCase())
}
+ case 'isBefore': {
+ if (filter.values.length === 0) {
+ return true
+ }
+ if (dateValue !== undefined) {
+ const numericFilter = parseInt(filter.values[0], 10)
+ if (template && (template.type === 'createdTime' || template.type === 'updatedTime')) {
+ // createdTime and updatedTime include the time
+ // So to check if create and/or updated "isBefore" date.
+ // Need to subtract 12 hours to filter
+ if (dateValue.from) {
+ return dateValue.from < (numericFilter - halfDay)
+ }
+ return false
+ }
+
+ return dateValue.from ? dateValue.from < numericFilter : false
+ }
+ return false
+ }
+ case 'isAfter': {
+ if (filter.values.length === 0) {
+ return true
+ }
+ if (dateValue !== undefined) {
+ const numericFilter = parseInt(filter.values[0], 10)
+ if (template && (template.type === 'createdTime' || template.type === 'updatedTime')) {
+ // createdTime and updatedTime include the time
+ // So to check if create and/or updated "isAfter" date.
+ // Need to add 12 hours to filter
+ if (dateValue.from) {
+ return dateValue.from > (numericFilter + halfDay)
+ }
+ return false
+ }
+
+ if (dateValue.to) {
+ return dateValue.to > numericFilter
+ }
+ return dateValue.from ? dateValue.from > numericFilter : false
+ }
+ return false
+ }
+
default: {
Utils.assertFailure(`Invalid filter condition ${filter.condition}`)
}
@@ -161,6 +264,10 @@ class CardFilter {
return {id: filterClause.propertyId}
}
+ if (template.type === 'createdBy' || template.type === 'updatedBy') {
+ return {id: filterClause.propertyId}
+ }
+
switch (filterClause.condition) {
case 'includes': {
if (filterClause.values.length < 1) {
@@ -187,7 +294,7 @@ class CardFilter {
return {id: filterClause.propertyId}
}
default: {
- Utils.assertFailure(`Unexpected filter condition: ${filterClause.condition}`)
+ // Handle filter clause that cannot be set
return {id: filterClause.propertyId}
}
}
diff --git a/webapp/src/components/__snapshots__/cardDialog.test.tsx.snap b/webapp/src/components/__snapshots__/cardDialog.test.tsx.snap
index 29b94a6cc..defa8ebfd 100644
--- a/webapp/src/components/__snapshots__/cardDialog.test.tsx.snap
+++ b/webapp/src/components/__snapshots__/cardDialog.test.tsx.snap
@@ -3,7 +3,7 @@
exports[`components/cardDialog already following card 1`] = `
+
+
+