Merge branch 'main' into status-property
This commit is contained in:
commit
ec6ea0497b
55 changed files with 2315 additions and 928 deletions
|
@ -12,136 +12,69 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
code.sajari.com/docconv v1.3.5 // indirect
|
||||
github.com/JalfResi/justext v0.0.0-20221106200834-be571e3e3052 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.1.1 // indirect
|
||||
github.com/BurntSushi/toml v1.2.0 // indirect
|
||||
github.com/Masterminds/squirrel v1.5.2 // indirect
|
||||
github.com/PuerkitoBio/goquery v1.8.0 // indirect
|
||||
github.com/RoaringBitmap/roaring v1.2.1 // indirect
|
||||
github.com/advancedlogic/GoOse v0.0.0-20210820140952-9d5822d4a625 // indirect
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.1 // indirect
|
||||
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de // indirect
|
||||
github.com/avct/uasurfer v0.0.0-20191028135549-26b5daa857f1 // indirect
|
||||
github.com/aws/aws-sdk-go v1.44.138 // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.3.3 // indirect
|
||||
github.com/blang/semver v3.5.1+incompatible // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/blevesearch/bleve/v2 v2.3.6-0.20221111171245-56dc9b25507e // indirect
|
||||
github.com/blevesearch/bleve_index_api v1.0.5 // indirect
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
|
||||
github.com/blevesearch/gtreap v0.1.1 // indirect
|
||||
github.com/blevesearch/mmap-go v1.0.4 // indirect
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.1.4 // indirect
|
||||
github.com/blevesearch/segment v0.9.0 // indirect
|
||||
github.com/blevesearch/snowballstem v0.9.0 // indirect
|
||||
github.com/blevesearch/upsidedown_store_api v1.0.1 // indirect
|
||||
github.com/blevesearch/vellum v1.0.9 // indirect
|
||||
github.com/blevesearch/zapx/v11 v11.3.7 // indirect
|
||||
github.com/blevesearch/zapx/v12 v12.3.7 // indirect
|
||||
github.com/blevesearch/zapx/v13 v13.3.7 // indirect
|
||||
github.com/blevesearch/zapx/v14 v14.3.7 // indirect
|
||||
github.com/blevesearch/zapx/v15 v15.3.7 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
|
||||
github.com/dgryski/dgoogauth v0.0.0-20190221195224-5a805980a5f3 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/disintegration/imaging v1.6.2 // indirect
|
||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
|
||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||
github.com/dyatlov/go-opengraph/opengraph v0.0.0-20220524092352-606d7b1e5f8a // indirect
|
||||
github.com/fatih/color v1.13.0 // indirect
|
||||
github.com/fatih/set v0.2.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.3 // indirect
|
||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/getsentry/sentry-go v0.15.0 // indirect
|
||||
github.com/gigawattio/window v0.0.0-20180317192513-0f5467e35573 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
|
||||
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
||||
github.com/go-resty/resty/v2 v2.7.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
||||
github.com/golang-migrate/migrate/v4 v4.15.2 // indirect
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/gopherjs/gopherjs v1.17.2 // indirect
|
||||
github.com/gorilla/css v1.0.0 // indirect
|
||||
github.com/gorilla/handlers v1.5.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/graph-gophers/graphql-go v1.4.0 // indirect
|
||||
github.com/h2non/go-is-svg v0.0.0-20160927212452-35e8c4b0612c // indirect
|
||||
github.com/hashicorp/go-hclog v1.3.1 // indirect
|
||||
github.com/hashicorp/go-plugin v1.4.6 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/jaytaylor/html2text v0.0.0-20211105163654-bc68cce691ba // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/jmoiron/sqlx v1.3.5 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/klauspost/compress v1.15.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.1 // indirect
|
||||
github.com/klauspost/pgzip v1.2.5 // indirect
|
||||
github.com/krolaw/zipstream v0.0.0-20180621105154-0a2661891f94 // indirect
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
||||
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 // indirect
|
||||
github.com/levigross/exp-html v0.0.0-20120902181939-8df60c69a8f5 // indirect
|
||||
github.com/lib/pq v1.10.7 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404 // indirect
|
||||
github.com/mattermost/ldap v0.0.0-20201202150706-ee0e6284187d // indirect
|
||||
github.com/mattermost/logr/v2 v2.0.15 // indirect
|
||||
github.com/mattermost/morph v1.0.5-0.20221115094356-4c18a75b1f5e // indirect
|
||||
github.com/mattermost/rsc v0.0.0-20160330161541-bbaefb05eaa0 // indirect
|
||||
github.com/mattermost/squirrel v0.2.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
||||
github.com/mholt/archiver/v3 v3.5.1 // indirect
|
||||
github.com/microcosm-cc/bluemonday v1.0.21 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/minio/minio-go/v7 v7.0.43 // indirect
|
||||
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/mschoch/smat v0.2.0 // indirect
|
||||
github.com/nwaples/rardecode v1.1.3 // indirect
|
||||
github.com/oklog/run v1.1.0 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/oov/psd v0.0.0-20220121172623-5db5eafcecbb // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/otiai10/gosseract/v2 v2.4.0 // indirect
|
||||
github.com/pborman/uuid v1.2.1 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/philhofer/fwd v1.1.1 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.17 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.12.1 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.33.0 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/reflog/dateconstraints v0.2.1 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||
github.com/richardlehane/mscfb v1.0.4 // indirect
|
||||
github.com/richardlehane/msoleps v1.0.3 // indirect
|
||||
github.com/rivo/uniseg v0.4.3 // indirect
|
||||
github.com/rs/cors v1.8.2 // indirect
|
||||
github.com/rs/xid v1.4.0 // indirect
|
||||
github.com/rudderlabs/analytics-go v3.3.3+incompatible // indirect
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd // indirect
|
||||
github.com/segmentio/backo-go v1.0.1 // indirect
|
||||
github.com/sergi/go-diff v1.2.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||
|
@ -150,31 +83,19 @@ require (
|
|||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.10.1 // indirect
|
||||
github.com/splitio/go-client/v6 v6.2.1 // indirect
|
||||
github.com/splitio/go-split-commons/v3 v3.1.0 // indirect
|
||||
github.com/splitio/go-toolkit/v4 v4.2.0 // indirect
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
github.com/subosito/gotenv v1.2.0 // indirect
|
||||
github.com/throttled/throttled v2.2.5+incompatible // indirect
|
||||
github.com/tidwall/gjson v1.14.3 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tinylib/msgp v1.1.6 // indirect
|
||||
github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect
|
||||
github.com/uber/jaeger-lib v2.4.1+incompatible // indirect
|
||||
github.com/ulikunitz/xz v0.5.10 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
github.com/wiggin77/merror v1.0.4 // indirect
|
||||
github.com/wiggin77/srslog v1.0.1 // indirect
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
|
||||
github.com/yuin/goldmark v1.5.3 // indirect
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
go.uber.org/atomic v1.10.0 // indirect
|
||||
golang.org/x/crypto v0.2.0 // indirect
|
||||
golang.org/x/image v0.1.0 // indirect
|
||||
golang.org/x/mod v0.7.0 // indirect
|
||||
golang.org/x/net v0.2.0 // indirect
|
||||
golang.org/x/sync v0.1.0 // indirect
|
||||
|
@ -184,9 +105,7 @@ require (
|
|||
google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1 // indirect
|
||||
google.golang.org/grpc v1.50.1 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/mail.v2 v2.3.1 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -6,7 +6,7 @@
|
|||
"support_url": "https://github.com/mattermost/focalboard/issues",
|
||||
"release_notes_url": "https://github.com/mattermost/focalboard/releases",
|
||||
"icon_path": "assets/starter-template-icon.svg",
|
||||
"version": "7.8.0",
|
||||
"version": "7.9.0",
|
||||
"min_server_version": "7.2.0",
|
||||
"server": {
|
||||
"executables": {
|
||||
|
|
|
@ -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) {
|
||||
|
|
5
mattermost-plugin/server/manifest.go
generated
5
mattermost-plugin/server/manifest.go
generated
|
@ -20,7 +20,7 @@ const manifestStr = `
|
|||
"support_url": "https://github.com/mattermost/focalboard/issues",
|
||||
"release_notes_url": "https://github.com/mattermost/focalboard/releases",
|
||||
"icon_path": "assets/starter-template-icon.svg",
|
||||
"version": "7.8.0",
|
||||
"version": "7.9.0",
|
||||
"min_server_version": "7.2.0",
|
||||
"server": {
|
||||
"executables": {
|
||||
|
@ -45,7 +45,8 @@ const manifestStr = `
|
|||
"type": "bool",
|
||||
"help_text": "This allows board editors to share boards that can be accessed by anyone with the link.",
|
||||
"placeholder": "",
|
||||
"default": false
|
||||
"default": false,
|
||||
"hosting": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
447
server/api/compliance.go
Normal file
447
server/api/compliance.go
Normal file
|
@ -0,0 +1,447 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/mattermost/focalboard/server/model"
|
||||
|
||||
mm_model "github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||
)
|
||||
|
||||
const (
|
||||
complianceDefaultPage = "0"
|
||||
complianceDefaultPerPage = "60"
|
||||
)
|
||||
|
||||
func (a *API) registerComplianceRoutes(r *mux.Router) {
|
||||
// Compliance APIs
|
||||
r.HandleFunc("/admin/boards", a.sessionRequired(a.handleGetBoardsForCompliance)).Methods("GET")
|
||||
r.HandleFunc("/admin/boards_history", a.sessionRequired(a.handleGetBoardsComplianceHistory)).Methods("GET")
|
||||
r.HandleFunc("/admin/blocks_history", a.sessionRequired(a.handleGetBlocksComplianceHistory)).Methods("GET")
|
||||
}
|
||||
|
||||
func (a *API) handleGetBoardsForCompliance(w http.ResponseWriter, r *http.Request) {
|
||||
// swagger:operation GET /admin/boards getBoardsForCompliance
|
||||
//
|
||||
// Returns boards for a specific team, or all teams.
|
||||
//
|
||||
// Requires a license that includes Compliance feature. Caller must have `manage_system` permissions.
|
||||
//
|
||||
// ---
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: team_id
|
||||
// in: query
|
||||
// description: Team ID. If empty then boards across all teams are included.
|
||||
// required: false
|
||||
// type: string
|
||||
// - name: page
|
||||
// in: query
|
||||
// description: The page to select (default=0)
|
||||
// required: false
|
||||
// type: integer
|
||||
// - name: per_page
|
||||
// in: query
|
||||
// description: Number of boards to return per page(default=60)
|
||||
// required: false
|
||||
// type: integer
|
||||
// security:
|
||||
// - BearerAuth: []
|
||||
// responses:
|
||||
// '200':
|
||||
// description: success
|
||||
// schema:
|
||||
// type: object
|
||||
// items:
|
||||
// "$ref": "#/definitions/BoardsComplianceResponse"
|
||||
// default:
|
||||
// description: internal error
|
||||
// schema:
|
||||
// "$ref": "#/definitions/ErrorResponse"
|
||||
|
||||
query := r.URL.Query()
|
||||
teamID := query.Get("team_id")
|
||||
strPage := query.Get("page")
|
||||
strPerPage := query.Get("per_page")
|
||||
|
||||
// check for permission `manage_system`
|
||||
userID := getUserID(r)
|
||||
if !a.permissions.HasPermissionTo(userID, mm_model.PermissionManageSystem) {
|
||||
a.errorResponse(w, r, model.NewErrUnauthorized("access denied Compliance Export getAllBoards"))
|
||||
return
|
||||
}
|
||||
|
||||
// check for valid license feature: compliance
|
||||
license := a.app.GetLicense()
|
||||
if license == nil || !(*license.Features.Compliance) {
|
||||
a.errorResponse(w, r, model.NewErrNotImplemented("insufficient license Compliance Export getAllBoards"))
|
||||
return
|
||||
}
|
||||
|
||||
// check for valid team if specified
|
||||
if teamID != "" {
|
||||
_, err := a.app.GetTeam(teamID)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, model.NewErrBadRequest("invalid team id: "+teamID))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if strPage == "" {
|
||||
strPage = complianceDefaultPage
|
||||
}
|
||||
if strPerPage == "" {
|
||||
strPerPage = complianceDefaultPerPage
|
||||
}
|
||||
page, err := strconv.Atoi(strPage)
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("invalid `page` parameter: %s", err)
|
||||
a.errorResponse(w, r, model.NewErrBadRequest(message))
|
||||
return
|
||||
}
|
||||
perPage, err := strconv.Atoi(strPerPage)
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("invalid `per_page` parameter: %s", err)
|
||||
a.errorResponse(w, r, model.NewErrBadRequest(message))
|
||||
return
|
||||
}
|
||||
|
||||
opts := model.QueryBoardsForComplianceOptions{
|
||||
TeamID: teamID,
|
||||
Page: page,
|
||||
PerPage: perPage,
|
||||
}
|
||||
|
||||
boards, more, err := a.app.GetBoardsForCompliance(opts)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
a.logger.Debug("GetBoardsForCompliance",
|
||||
mlog.String("teamID", teamID),
|
||||
mlog.Int("boardsCount", len(boards)),
|
||||
mlog.Bool("hasNext", more),
|
||||
)
|
||||
|
||||
response := model.BoardsComplianceResponse{
|
||||
HasNext: more,
|
||||
Results: boards,
|
||||
}
|
||||
data, err := json.Marshal(response)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
jsonBytesResponse(w, http.StatusOK, data)
|
||||
}
|
||||
|
||||
func (a *API) handleGetBoardsComplianceHistory(w http.ResponseWriter, r *http.Request) {
|
||||
// swagger:operation GET /admin/boards_history getBoardsComplianceHistory
|
||||
//
|
||||
// Returns boards histories for a specific team, or all teams.
|
||||
//
|
||||
// Requires a license that includes Compliance feature. Caller must have `manage_system` permissions.
|
||||
//
|
||||
// ---
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: modified_since
|
||||
// in: query
|
||||
// description: Filters for boards modified since timestamp; Unix time in milliseconds
|
||||
// required: true
|
||||
// type: integer
|
||||
// - name: include_deleted
|
||||
// in: query
|
||||
// description: When true then deleted boards are included. Default=false
|
||||
// required: false
|
||||
// type: boolean
|
||||
// - name: team_id
|
||||
// in: query
|
||||
// description: Team ID. If empty then board histories across all teams are included
|
||||
// required: false
|
||||
// type: string
|
||||
// - name: page
|
||||
// in: query
|
||||
// description: The page to select (default=0)
|
||||
// required: false
|
||||
// type: integer
|
||||
// - name: per_page
|
||||
// in: query
|
||||
// description: Number of board histories to return per page (default=60)
|
||||
// required: false
|
||||
// type: integer
|
||||
// security:
|
||||
// - BearerAuth: []
|
||||
// responses:
|
||||
// '200':
|
||||
// description: success
|
||||
// schema:
|
||||
// type: object
|
||||
// items:
|
||||
// "$ref": "#/definitions/BoardsComplianceHistoryResponse"
|
||||
// default:
|
||||
// description: internal error
|
||||
// schema:
|
||||
// "$ref": "#/definitions/ErrorResponse"
|
||||
|
||||
query := r.URL.Query()
|
||||
strModifiedSince := query.Get("modified_since") // required, everything else optional
|
||||
includeDeleted := query.Get("include_deleted") == "true"
|
||||
strPage := query.Get("page")
|
||||
strPerPage := query.Get("per_page")
|
||||
teamID := query.Get("team_id")
|
||||
|
||||
if strModifiedSince == "" {
|
||||
a.errorResponse(w, r, model.NewErrBadRequest("`modified_since` parameter required"))
|
||||
return
|
||||
}
|
||||
|
||||
// check for permission `manage_system`
|
||||
userID := getUserID(r)
|
||||
if !a.permissions.HasPermissionTo(userID, mm_model.PermissionManageSystem) {
|
||||
a.errorResponse(w, r, model.NewErrUnauthorized("access denied Compliance Export getBoardsHistory"))
|
||||
return
|
||||
}
|
||||
|
||||
// check for valid license feature: compliance
|
||||
license := a.app.GetLicense()
|
||||
if license == nil || !(*license.Features.Compliance) {
|
||||
a.errorResponse(w, r, model.NewErrNotImplemented("insufficient license Compliance Export getBoardsHistory"))
|
||||
return
|
||||
}
|
||||
|
||||
// check for valid team if specified
|
||||
if teamID != "" {
|
||||
_, err := a.app.GetTeam(teamID)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, model.NewErrBadRequest("invalid team id: "+teamID))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if strPage == "" {
|
||||
strPage = complianceDefaultPage
|
||||
}
|
||||
if strPerPage == "" {
|
||||
strPerPage = complianceDefaultPerPage
|
||||
}
|
||||
page, err := strconv.Atoi(strPage)
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("invalid `page` parameter: %s", err)
|
||||
a.errorResponse(w, r, model.NewErrBadRequest(message))
|
||||
return
|
||||
}
|
||||
perPage, err := strconv.Atoi(strPerPage)
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("invalid `per_page` parameter: %s", err)
|
||||
a.errorResponse(w, r, model.NewErrBadRequest(message))
|
||||
return
|
||||
}
|
||||
modifiedSince, err := strconv.ParseInt(strModifiedSince, 10, 64)
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("invalid `modified_since` parameter: %s", err)
|
||||
a.errorResponse(w, r, model.NewErrBadRequest(message))
|
||||
return
|
||||
}
|
||||
|
||||
opts := model.QueryBoardsComplianceHistoryOptions{
|
||||
ModifiedSince: modifiedSince,
|
||||
IncludeDeleted: includeDeleted,
|
||||
TeamID: teamID,
|
||||
Page: page,
|
||||
PerPage: perPage,
|
||||
}
|
||||
|
||||
boards, more, err := a.app.GetBoardsComplianceHistory(opts)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
a.logger.Debug("GetBoardsComplianceHistory",
|
||||
mlog.String("teamID", teamID),
|
||||
mlog.Int("boardsCount", len(boards)),
|
||||
mlog.Bool("hasNext", more),
|
||||
)
|
||||
|
||||
response := model.BoardsComplianceHistoryResponse{
|
||||
HasNext: more,
|
||||
Results: boards,
|
||||
}
|
||||
data, err := json.Marshal(response)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
jsonBytesResponse(w, http.StatusOK, data)
|
||||
}
|
||||
|
||||
func (a *API) handleGetBlocksComplianceHistory(w http.ResponseWriter, r *http.Request) {
|
||||
// swagger:operation GET /admin/blocks_history getBlocksComplianceHistory
|
||||
//
|
||||
// Returns block histories for a specific team, specific board, or all teams and boards.
|
||||
//
|
||||
// Requires a license that includes Compliance feature. Caller must have `manage_system` permissions.
|
||||
//
|
||||
// ---
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: modified_since
|
||||
// in: query
|
||||
// description: Filters for boards modified since timestamp; Unix time in milliseconds
|
||||
// required: true
|
||||
// type: integer
|
||||
// - name: include_deleted
|
||||
// in: query
|
||||
// description: When true then deleted boards are included. Default=false
|
||||
// required: false
|
||||
// type: boolean
|
||||
// - name: team_id
|
||||
// in: query
|
||||
// description: Team ID. If empty then block histories across all teams are included
|
||||
// required: false
|
||||
// type: string
|
||||
// - name: board_id
|
||||
// in: query
|
||||
// description: Board ID. If empty then block histories for all boards are included
|
||||
// required: false
|
||||
// type: string
|
||||
// - name: page
|
||||
// in: query
|
||||
// description: The page to select (default=0)
|
||||
// required: false
|
||||
// type: integer
|
||||
// - name: per_page
|
||||
// in: query
|
||||
// description: Number of block histories to return per page (default=60)
|
||||
// required: false
|
||||
// type: integer
|
||||
// security:
|
||||
// - BearerAuth: []
|
||||
// responses:
|
||||
// '200':
|
||||
// description: success
|
||||
// schema:
|
||||
// type: object
|
||||
// items:
|
||||
// "$ref": "#/definitions/BlocksComplianceHistoryResponse"
|
||||
// default:
|
||||
// description: internal error
|
||||
// schema:
|
||||
// "$ref": "#/definitions/ErrorResponse"
|
||||
|
||||
query := r.URL.Query()
|
||||
strModifiedSince := query.Get("modified_since") // required, everything else optional
|
||||
includeDeleted := query.Get("include_deleted") == "true"
|
||||
strPage := query.Get("page")
|
||||
strPerPage := query.Get("per_page")
|
||||
teamID := query.Get("team_id")
|
||||
boardID := query.Get("board_id")
|
||||
|
||||
if strModifiedSince == "" {
|
||||
a.errorResponse(w, r, model.NewErrBadRequest("`modified_since` parameter required"))
|
||||
return
|
||||
}
|
||||
|
||||
// check for permission `manage_system`
|
||||
userID := getUserID(r)
|
||||
if !a.permissions.HasPermissionTo(userID, mm_model.PermissionManageSystem) {
|
||||
a.errorResponse(w, r, model.NewErrUnauthorized("access denied Compliance Export getBlocksHistory"))
|
||||
return
|
||||
}
|
||||
|
||||
// check for valid license feature: compliance
|
||||
license := a.app.GetLicense()
|
||||
if license == nil || !(*license.Features.Compliance) {
|
||||
a.errorResponse(w, r, model.NewErrNotImplemented("insufficient license Compliance Export getBlocksHistory"))
|
||||
return
|
||||
}
|
||||
|
||||
// check for valid team if specified
|
||||
if teamID != "" {
|
||||
_, err := a.app.GetTeam(teamID)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, model.NewErrBadRequest("invalid team id: "+teamID))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// check for valid board if specified
|
||||
if boardID != "" {
|
||||
_, err := a.app.GetBoard(boardID)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, model.NewErrBadRequest("invalid board id: "+boardID))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if strPage == "" {
|
||||
strPage = complianceDefaultPage
|
||||
}
|
||||
if strPerPage == "" {
|
||||
strPerPage = complianceDefaultPerPage
|
||||
}
|
||||
page, err := strconv.Atoi(strPage)
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("invalid `page` parameter: %s", err)
|
||||
a.errorResponse(w, r, model.NewErrBadRequest(message))
|
||||
return
|
||||
}
|
||||
perPage, err := strconv.Atoi(strPerPage)
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("invalid `per_page` parameter: %s", err)
|
||||
a.errorResponse(w, r, model.NewErrBadRequest(message))
|
||||
return
|
||||
}
|
||||
modifiedSince, err := strconv.ParseInt(strModifiedSince, 10, 64)
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("invalid `modified_since` parameter: %s", err)
|
||||
a.errorResponse(w, r, model.NewErrBadRequest(message))
|
||||
return
|
||||
}
|
||||
|
||||
opts := model.QueryBlocksComplianceHistoryOptions{
|
||||
ModifiedSince: modifiedSince,
|
||||
IncludeDeleted: includeDeleted,
|
||||
TeamID: teamID,
|
||||
BoardID: boardID,
|
||||
Page: page,
|
||||
PerPage: perPage,
|
||||
}
|
||||
|
||||
blocks, more, err := a.app.GetBlocksComplianceHistory(opts)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
a.logger.Debug("GetBlocksComplianceHistory",
|
||||
mlog.String("teamID", teamID),
|
||||
mlog.String("boardID", boardID),
|
||||
mlog.Int("blocksCount", len(blocks)),
|
||||
mlog.Bool("hasNext", more),
|
||||
)
|
||||
|
||||
response := model.BlocksComplianceHistoryResponse{
|
||||
HasNext: more,
|
||||
Results: blocks,
|
||||
}
|
||||
data, err := json.Marshal(response)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
jsonBytesResponse(w, http.StatusOK, data)
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
|
|
15
server/app/compliance.go
Normal file
15
server/app/compliance.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package app
|
||||
|
||||
import "github.com/mattermost/focalboard/server/model"
|
||||
|
||||
func (a *App) GetBoardsForCompliance(opts model.QueryBoardsForComplianceOptions) ([]*model.Board, bool, error) {
|
||||
return a.store.GetBoardsForCompliance(opts)
|
||||
}
|
||||
|
||||
func (a *App) GetBoardsComplianceHistory(opts model.QueryBoardsComplianceHistoryOptions) ([]*model.BoardHistory, bool, error) {
|
||||
return a.store.GetBoardsComplianceHistory(opts)
|
||||
}
|
||||
|
||||
func (a *App) GetBlocksComplianceHistory(opts model.QueryBlocksComplianceHistoryOptions) ([]*model.BlockHistory, bool, error) {
|
||||
return a.store.GetBlocksComplianceHistory(opts)
|
||||
}
|
|
@ -241,6 +241,15 @@ func (a *App) ImportBoardJSONL(r io.Reader, opt model.ImportArchiveOptions) (str
|
|||
|
||||
// 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,
|
||||
|
|
|
@ -47,9 +47,8 @@ func TestApp_ImportArchive(t *testing.T) {
|
|||
|
||||
th.Store.EXPECT().CreateBoardsAndBlocks(gomock.AssignableToTypeOf(&model.BoardsAndBlocks{}), "user").Return(babs, nil)
|
||||
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{}, 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{
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
||||
|
@ -987,6 +988,61 @@ 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 {
|
||||
|
|
|
@ -65,7 +65,6 @@ require (
|
|||
github.com/minio/md5-simd v1.1.2 // 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
|
||||
|
|
213
server/go.sum
213
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=
|
||||
|
@ -407,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=
|
||||
|
@ -428,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=
|
||||
|
@ -675,8 +671,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-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
|
@ -685,8 +680,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-plugin v1.4.8/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
|
@ -703,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=
|
||||
|
@ -811,14 +804,12 @@ 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=
|
||||
|
@ -850,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=
|
||||
|
@ -899,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=
|
||||
|
@ -910,8 +900,8 @@ 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=
|
||||
|
@ -920,7 +910,6 @@ github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vq
|
|||
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=
|
||||
|
@ -936,8 +925,7 @@ 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=
|
||||
|
@ -945,7 +933,6 @@ github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy
|
|||
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=
|
||||
|
@ -1139,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=
|
||||
|
@ -1154,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=
|
||||
|
@ -1194,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=
|
||||
|
@ -1238,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=
|
||||
|
@ -1250,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=
|
||||
|
@ -1260,15 +1245,14 @@ 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=
|
||||
|
@ -1297,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=
|
||||
|
@ -1379,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=
|
||||
|
@ -1419,8 +1402,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/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
|
@ -1471,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=
|
||||
|
@ -1702,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=
|
||||
|
@ -1716,7 +1696,6 @@ 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=
|
||||
|
@ -1726,6 +1705,7 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
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=
|
||||
|
@ -1743,8 +1723,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/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
|
@ -1850,8 +1830,8 @@ golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8T
|
|||
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=
|
||||
|
@ -1980,8 +1960,7 @@ 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=
|
||||
|
@ -2020,8 +1999,7 @@ 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=
|
||||
|
@ -2038,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=
|
||||
|
@ -2057,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=
|
||||
|
@ -2147,24 +2123,6 @@ 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=
|
||||
|
@ -2172,54 +2130,6 @@ 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=
|
||||
|
@ -2238,57 +2148,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=
|
||||
|
@ -2298,15 +2157,10 @@ 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=
|
||||
|
@ -2317,9 +2171,6 @@ 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=
|
||||
|
@ -2329,9 +2180,7 @@ 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=
|
||||
|
@ -2339,9 +2188,7 @@ 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=
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -29,6 +29,7 @@ const (
|
|||
user1Username = "user1"
|
||||
user2Username = "user2"
|
||||
password = "Pa$$word"
|
||||
testTeamID = "team-id"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -457,6 +458,16 @@ func (th *TestHelper) CreateBoard(teamID string, boardType model.BoardType) *mod
|
|||
return board
|
||||
}
|
||||
|
||||
func (th *TestHelper) CreateBoards(teamID string, boardType model.BoardType, count int) []*model.Board {
|
||||
boards := make([]*model.Board, 0, count)
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
board := th.CreateBoard(teamID, boardType)
|
||||
boards = append(boards, board)
|
||||
}
|
||||
return boards
|
||||
}
|
||||
|
||||
func (th *TestHelper) CreateCategory(category model.Category) *model.Category {
|
||||
cat, resp := th.Client.CreateCategory(category)
|
||||
th.CheckOK(resp)
|
||||
|
|
360
server/integrationtests/compliance_test.go
Normal file
360
server/integrationtests/compliance_test.go
Normal file
|
@ -0,0 +1,360 @@
|
|||
package integrationtests
|
||||
|
||||
import (
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/focalboard/server/model"
|
||||
"github.com/mattermost/focalboard/server/utils"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
OneHour int64 = 360000
|
||||
OneDay int64 = OneHour * 24
|
||||
OneYear int64 = OneDay * 365
|
||||
)
|
||||
|
||||
func setupTestHelperForCompliance(t *testing.T, complianceLicense bool) (*TestHelper, Clients) {
|
||||
os.Setenv("FOCALBOARD_UNIT_TESTING_COMPLIANCE", strconv.FormatBool(complianceLicense))
|
||||
|
||||
th := SetupTestHelperPluginMode(t)
|
||||
clients := setupClients(th)
|
||||
|
||||
th.Client = clients.TeamMember
|
||||
th.Client2 = clients.TeamMember
|
||||
|
||||
return th, clients
|
||||
}
|
||||
|
||||
func TestGetBoardsForCompliance(t *testing.T) {
|
||||
t.Run("missing Features.Compliance license should fail", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, false)
|
||||
defer th.TearDown()
|
||||
|
||||
_ = th.CreateBoards(testTeamID, model.BoardTypeOpen, 2)
|
||||
|
||||
bcr, resp := clients.Admin.GetBoardsForCompliance(testTeamID, 0, 0)
|
||||
|
||||
th.CheckNotImplemented(resp)
|
||||
require.Nil(t, bcr)
|
||||
})
|
||||
|
||||
t.Run("a non authenticated user should be rejected", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
_ = th.CreateBoards(testTeamID, model.BoardTypeOpen, 2)
|
||||
th.Logout(th.Client)
|
||||
|
||||
bcr, resp := clients.Anon.GetBoardsForCompliance(testTeamID, 0, 0)
|
||||
|
||||
th.CheckUnauthorized(resp)
|
||||
require.Nil(t, bcr)
|
||||
})
|
||||
|
||||
t.Run("a user without manage_system permission should be rejected", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
_ = th.CreateBoards(testTeamID, model.BoardTypeOpen, 2)
|
||||
|
||||
bcr, resp := clients.TeamMember.GetBoardsForCompliance(testTeamID, 0, 0)
|
||||
|
||||
th.CheckUnauthorized(resp)
|
||||
require.Nil(t, bcr)
|
||||
})
|
||||
|
||||
t.Run("good call", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
const count = 10
|
||||
_ = th.CreateBoards(testTeamID, model.BoardTypeOpen, count)
|
||||
|
||||
bcr, resp := clients.Admin.GetBoardsForCompliance(testTeamID, 0, 0)
|
||||
th.CheckOK(resp)
|
||||
require.False(t, bcr.HasNext)
|
||||
require.Len(t, bcr.Results, count)
|
||||
})
|
||||
|
||||
t.Run("pagination", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
const count = 20
|
||||
const perPage = 3
|
||||
_ = th.CreateBoards(testTeamID, model.BoardTypeOpen, count)
|
||||
|
||||
boards := make([]*model.Board, 0, count)
|
||||
page := 0
|
||||
for {
|
||||
bcr, resp := clients.Admin.GetBoardsForCompliance(testTeamID, page, perPage)
|
||||
page++
|
||||
th.CheckOK(resp)
|
||||
boards = append(boards, bcr.Results...)
|
||||
if !bcr.HasNext {
|
||||
break
|
||||
}
|
||||
}
|
||||
require.Len(t, boards, count)
|
||||
require.Equal(t, int(math.Floor((count/perPage)+1)), page)
|
||||
})
|
||||
|
||||
t.Run("invalid teamID", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
_ = th.CreateBoards(testTeamID, model.BoardTypeOpen, 2)
|
||||
|
||||
bcr, resp := clients.Admin.GetBoardsForCompliance(utils.NewID(utils.IDTypeTeam), 0, 0)
|
||||
|
||||
th.CheckBadRequest(resp)
|
||||
require.Nil(t, bcr)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetBoardsComplianceHistory(t *testing.T) {
|
||||
t.Run("missing Features.Compliance license should fail", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, false)
|
||||
defer th.TearDown()
|
||||
|
||||
_ = th.CreateBoards(testTeamID, model.BoardTypeOpen, 2)
|
||||
|
||||
bchr, resp := clients.Admin.GetBoardsComplianceHistory(utils.GetMillis()-OneDay, true, testTeamID, 0, 0)
|
||||
|
||||
th.CheckNotImplemented(resp)
|
||||
require.Nil(t, bchr)
|
||||
})
|
||||
|
||||
t.Run("a non authenticated user should be rejected", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
_ = th.CreateBoards(testTeamID, model.BoardTypeOpen, 2)
|
||||
th.Logout(th.Client)
|
||||
|
||||
bchr, resp := clients.Anon.GetBoardsComplianceHistory(utils.GetMillis()-OneDay, true, testTeamID, 0, 0)
|
||||
|
||||
th.CheckUnauthorized(resp)
|
||||
require.Nil(t, bchr)
|
||||
})
|
||||
|
||||
t.Run("a user without manage_system permission should be rejected", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
_ = th.CreateBoards(testTeamID, model.BoardTypeOpen, 2)
|
||||
|
||||
bchr, resp := clients.TeamMember.GetBoardsComplianceHistory(utils.GetMillis()-OneDay, true, testTeamID, 0, 0)
|
||||
|
||||
th.CheckUnauthorized(resp)
|
||||
require.Nil(t, bchr)
|
||||
})
|
||||
|
||||
t.Run("good call, exclude deleted", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
const count = 10
|
||||
boards := th.CreateBoards(testTeamID, model.BoardTypeOpen, count)
|
||||
|
||||
deleted, resp := th.Client.DeleteBoard(boards[0].ID)
|
||||
th.CheckOK(resp)
|
||||
require.True(t, deleted)
|
||||
|
||||
deleted, resp = th.Client.DeleteBoard(boards[1].ID)
|
||||
th.CheckOK(resp)
|
||||
require.True(t, deleted)
|
||||
|
||||
bchr, resp := clients.Admin.GetBoardsComplianceHistory(utils.GetMillis()-OneDay, false, testTeamID, 0, 0)
|
||||
th.CheckOK(resp)
|
||||
require.False(t, bchr.HasNext)
|
||||
require.Len(t, bchr.Results, count-2) // two boards deleted
|
||||
})
|
||||
|
||||
t.Run("good call, include deleted", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
const count = 10
|
||||
boards := th.CreateBoards(testTeamID, model.BoardTypeOpen, count)
|
||||
|
||||
deleted, resp := th.Client.DeleteBoard(boards[0].ID)
|
||||
th.CheckOK(resp)
|
||||
require.True(t, deleted)
|
||||
|
||||
deleted, resp = th.Client.DeleteBoard(boards[1].ID)
|
||||
th.CheckOK(resp)
|
||||
require.True(t, deleted)
|
||||
|
||||
bchr, resp := clients.Admin.GetBoardsComplianceHistory(utils.GetMillis()-OneDay, true, testTeamID, 0, 0)
|
||||
th.CheckOK(resp)
|
||||
require.False(t, bchr.HasNext)
|
||||
require.Len(t, bchr.Results, count+2) // both deleted boards have 2 history records each
|
||||
})
|
||||
|
||||
t.Run("pagination", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
const count = 20
|
||||
const perPage = 3
|
||||
_ = th.CreateBoards(testTeamID, model.BoardTypeOpen, count)
|
||||
|
||||
boardHistory := make([]*model.BoardHistory, 0, count)
|
||||
page := 0
|
||||
for {
|
||||
bchr, resp := clients.Admin.GetBoardsComplianceHistory(utils.GetMillis()-OneDay, true, testTeamID, page, perPage)
|
||||
page++
|
||||
th.CheckOK(resp)
|
||||
boardHistory = append(boardHistory, bchr.Results...)
|
||||
if !bchr.HasNext {
|
||||
break
|
||||
}
|
||||
}
|
||||
require.Len(t, boardHistory, count)
|
||||
require.Equal(t, int(math.Floor((count/perPage)+1)), page)
|
||||
})
|
||||
|
||||
t.Run("invalid teamID", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
_ = th.CreateBoards(testTeamID, model.BoardTypeOpen, 2)
|
||||
|
||||
bchr, resp := clients.Admin.GetBoardsComplianceHistory(utils.GetMillis()-OneDay, true, utils.NewID(utils.IDTypeTeam), 0, 0)
|
||||
|
||||
th.CheckBadRequest(resp)
|
||||
require.Nil(t, bchr)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetBlocksComplianceHistory(t *testing.T) {
|
||||
t.Run("missing Features.Compliance license should fail", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, false)
|
||||
defer th.TearDown()
|
||||
|
||||
board, _ := th.CreateBoardAndCards(testTeamID, model.BoardTypeOpen, 2)
|
||||
|
||||
bchr, resp := clients.Admin.GetBlocksComplianceHistory(utils.GetMillis()-OneDay, true, testTeamID, board.ID, 0, 0)
|
||||
|
||||
th.CheckNotImplemented(resp)
|
||||
require.Nil(t, bchr)
|
||||
})
|
||||
|
||||
t.Run("a non authenticated user should be rejected", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
board, _ := th.CreateBoardAndCards(testTeamID, model.BoardTypeOpen, 2)
|
||||
|
||||
bchr, resp := clients.Anon.GetBlocksComplianceHistory(utils.GetMillis()-OneDay, true, testTeamID, board.ID, 0, 0)
|
||||
|
||||
th.CheckUnauthorized(resp)
|
||||
require.Nil(t, bchr)
|
||||
})
|
||||
|
||||
t.Run("a user without manage_system permission should be rejected", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
board, _ := th.CreateBoardAndCards(testTeamID, model.BoardTypeOpen, 2)
|
||||
|
||||
bchr, resp := clients.TeamMember.GetBlocksComplianceHistory(utils.GetMillis()-OneDay, true, testTeamID, board.ID, 0, 0)
|
||||
|
||||
th.CheckUnauthorized(resp)
|
||||
require.Nil(t, bchr)
|
||||
})
|
||||
|
||||
t.Run("good call, exclude deleted", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
const count = 10
|
||||
board, cards := th.CreateBoardAndCards(testTeamID, model.BoardTypeOpen, count)
|
||||
|
||||
deleted, resp := th.Client.DeleteBlock(board.ID, cards[0].ID, true)
|
||||
th.CheckOK(resp)
|
||||
require.True(t, deleted)
|
||||
|
||||
deleted, resp = th.Client.DeleteBlock(board.ID, cards[1].ID, true)
|
||||
th.CheckOK(resp)
|
||||
require.True(t, deleted)
|
||||
|
||||
bchr, resp := clients.Admin.GetBlocksComplianceHistory(utils.GetMillis()-OneDay, false, testTeamID, board.ID, 0, 0)
|
||||
th.CheckOK(resp)
|
||||
require.False(t, bchr.HasNext)
|
||||
require.Len(t, bchr.Results, count-2) // 2 blocks deleted
|
||||
})
|
||||
|
||||
t.Run("good call, include deleted", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
const count = 10
|
||||
board, cards := th.CreateBoardAndCards(testTeamID, model.BoardTypeOpen, count)
|
||||
|
||||
deleted, resp := th.Client.DeleteBlock(board.ID, cards[0].ID, true)
|
||||
th.CheckOK(resp)
|
||||
require.True(t, deleted)
|
||||
|
||||
deleted, resp = th.Client.DeleteBlock(board.ID, cards[1].ID, true)
|
||||
th.CheckOK(resp)
|
||||
require.True(t, deleted)
|
||||
|
||||
bchr, resp := clients.Admin.GetBlocksComplianceHistory(utils.GetMillis()-OneDay, true, testTeamID, board.ID, 0, 0)
|
||||
th.CheckOK(resp)
|
||||
require.False(t, bchr.HasNext)
|
||||
require.Len(t, bchr.Results, count+2) // both deleted boards have 2 history records each
|
||||
})
|
||||
|
||||
t.Run("pagination", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
const count = 20
|
||||
const perPage = 3
|
||||
board, _ := th.CreateBoardAndCards(testTeamID, model.BoardTypeOpen, count)
|
||||
|
||||
blockHistory := make([]*model.BlockHistory, 0, count)
|
||||
page := 0
|
||||
for {
|
||||
bchr, resp := clients.Admin.GetBlocksComplianceHistory(utils.GetMillis()-OneDay, true, testTeamID, board.ID, page, perPage)
|
||||
page++
|
||||
th.CheckOK(resp)
|
||||
blockHistory = append(blockHistory, bchr.Results...)
|
||||
if !bchr.HasNext {
|
||||
break
|
||||
}
|
||||
}
|
||||
require.Len(t, blockHistory, count)
|
||||
require.Equal(t, int(math.Floor((count/perPage)+1)), page)
|
||||
})
|
||||
|
||||
t.Run("invalid teamID", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
board, _ := th.CreateBoardAndCards(testTeamID, model.BoardTypeOpen, 2)
|
||||
|
||||
bchr, resp := clients.Admin.GetBlocksComplianceHistory(utils.GetMillis()-OneDay, true, utils.NewID(utils.IDTypeTeam), board.ID, 0, 0)
|
||||
|
||||
th.CheckBadRequest(resp)
|
||||
require.Nil(t, bchr)
|
||||
})
|
||||
|
||||
t.Run("invalid boardID", func(t *testing.T) {
|
||||
th, clients := setupTestHelperForCompliance(t, true)
|
||||
defer th.TearDown()
|
||||
|
||||
_, _ = th.CreateBoardAndCards(testTeamID, model.BoardTypeOpen, 2)
|
||||
|
||||
bchr, resp := clients.Admin.GetBlocksComplianceHistory(utils.GetMillis()-OneDay, true, testTeamID, utils.NewID(utils.IDTypeBoard), 0, 0)
|
||||
|
||||
th.CheckBadRequest(resp)
|
||||
require.Nil(t, bchr)
|
||||
})
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
57
server/integrationtests/work_template_test.go
Normal file
57
server/integrationtests/work_template_test.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package integrationtests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// This test is there to guarantee that the board templates needed for
|
||||
// the work template are present in the default templates.
|
||||
// If this fails, you might need to sync with the channels team.
|
||||
func TestGetTemplatesForWorkTemplate(t *testing.T) {
|
||||
// map[name]trackingTemplateId
|
||||
knownInWorkTemplates := map[string]string{
|
||||
"Company Goals & OKRs": "7ba22ccfdfac391d63dea5c4b8cde0de",
|
||||
"Competitive Analysis": "06f4bff367a7c2126fab2380c9dec23c",
|
||||
"Content Calendar": "c75fbd659d2258b5183af2236d176ab4",
|
||||
"Meeting Agenda ": "54fcf9c610f0ac5e4c522c0657c90602",
|
||||
"Personal Goals ": "7f32dc8d2ae008cfe56554e9363505cc",
|
||||
"Personal Tasls ": "dfb70c146a4584b8a21837477c7b5431",
|
||||
"Project Tasks ": "a4ec399ab4f2088b1051c3cdf1dde4c3",
|
||||
"Roadmap ": "b728c6ca730e2cfc229741c5a4712b65",
|
||||
"Sales Pipeline CRM": "ecc250bb7dff0fe02247f1110f097544",
|
||||
"Sprint Planner ": "99b74e26d2f5d0a9b346d43c0a7bfb09",
|
||||
"Team Retrospective": "e4f03181c4ced8edd4d53d33d569a086",
|
||||
"User Research Sessions": "6c345c7f50f6833f78b7d0f08ce450a3",
|
||||
}
|
||||
th := SetupTestHelper(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
err := th.Server.App().InitTemplates()
|
||||
require.NoError(t, err, "InitTemplates should not fail")
|
||||
|
||||
rBoards, resp := th.Client.GetTemplatesForTeam("0")
|
||||
th.CheckOK(resp)
|
||||
require.NotNil(t, rBoards)
|
||||
|
||||
trackingTemplateIDs := []string{}
|
||||
for _, board := range rBoards {
|
||||
property, _ := board.GetPropertyString("trackingTemplateId")
|
||||
if property != "" {
|
||||
trackingTemplateIDs = append(trackingTemplateIDs, property)
|
||||
}
|
||||
}
|
||||
|
||||
// make sure all known templates are in trackingTemplateIds
|
||||
for name, ttID := range knownInWorkTemplates {
|
||||
found := false
|
||||
for _, trackingTemplateID := range trackingTemplateIDs {
|
||||
if trackingTemplateID == ttID {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
require.True(t, found, "trackingTemplateId %s for %s not found", ttID, name)
|
||||
}
|
||||
}
|
|
@ -199,6 +199,14 @@ type QueryBoardHistoryOptions struct {
|
|||
Descending bool // if true then the records are sorted by insert_at in descending order
|
||||
}
|
||||
|
||||
// QueryBlockHistoryOptions are query options that can be passed to GetBlockHistory.
|
||||
type QueryBlockHistoryChildOptions struct {
|
||||
BeforeUpdateAt int64 // if non-zero then filter for records with update_at less than BeforeUpdateAt
|
||||
AfterUpdateAt int64 // if non-zero then filter for records with update_at greater than AfterUpdateAt
|
||||
Page int // page number to select when paginating
|
||||
PerPage int // number of blocks per page (default=-1, meaning unlimited)
|
||||
}
|
||||
|
||||
func StampModificationMetadata(userID string, blocks []*Block, auditRec *audit.Record) {
|
||||
if userID == SingleUser {
|
||||
userID = ""
|
||||
|
|
|
@ -14,13 +14,16 @@ import (
|
|||
type BlockType string
|
||||
|
||||
const (
|
||||
TypeUnknown = "unknown"
|
||||
TypeBoard = "board"
|
||||
TypeCard = "card"
|
||||
TypeView = "view"
|
||||
TypeText = "text"
|
||||
TypeComment = "comment"
|
||||
TypeImage = "image"
|
||||
TypeUnknown = "unknown"
|
||||
TypeBoard = "board"
|
||||
TypeCard = "card"
|
||||
TypeView = "view"
|
||||
TypeText = "text"
|
||||
TypeCheckbox = "checkbox"
|
||||
TypeComment = "comment"
|
||||
TypeImage = "image"
|
||||
TypeAttachment = "attachment"
|
||||
TypeDivider = "divider"
|
||||
)
|
||||
|
||||
func (bt BlockType) String() string {
|
||||
|
@ -38,10 +41,16 @@ func BlockTypeFromString(s string) (BlockType, error) {
|
|||
return TypeView, nil
|
||||
case "text":
|
||||
return TypeText, nil
|
||||
case "checkbox":
|
||||
return TypeCheckbox, nil
|
||||
case "comment":
|
||||
return TypeComment, nil
|
||||
case "image":
|
||||
return TypeImage, nil
|
||||
case "attachment":
|
||||
return TypeAttachment, nil
|
||||
case "divider":
|
||||
return TypeDivider, nil
|
||||
}
|
||||
return TypeUnknown, ErrInvalidBlockType{s}
|
||||
}
|
||||
|
@ -55,8 +64,10 @@ func BlockType2IDType(blockType BlockType) utils.IDType {
|
|||
return utils.IDTypeCard
|
||||
case TypeView:
|
||||
return utils.IDTypeView
|
||||
case TypeText, TypeComment:
|
||||
case TypeText, TypeCheckbox, TypeComment, TypeDivider:
|
||||
return utils.IDTypeBlock
|
||||
case TypeImage, TypeAttachment:
|
||||
return utils.IDTypeAttachment
|
||||
}
|
||||
return utils.IDTypeNone
|
||||
}
|
||||
|
|
88
server/model/compliance.go
Normal file
88
server/model/compliance.go
Normal file
|
@ -0,0 +1,88 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
package model
|
||||
|
||||
// BaordsComplianceResponse is the response body to a request for boards.
|
||||
// swagger:model
|
||||
type BoardsComplianceResponse struct {
|
||||
// True if there is a next page for pagination
|
||||
// required: true
|
||||
HasNext bool `json:"hasNext"`
|
||||
|
||||
// The array of board records.
|
||||
// required: true
|
||||
Results []*Board `json:"results"`
|
||||
}
|
||||
|
||||
// BoardsComplianceHistoryResponse is the response body to a request for boards history.
|
||||
// swagger:model
|
||||
type BoardsComplianceHistoryResponse struct {
|
||||
// True if there is a next page for pagination
|
||||
// required: true
|
||||
HasNext bool `json:"hasNext"`
|
||||
|
||||
// The array of BoardHistory records.
|
||||
// required: true
|
||||
Results []*BoardHistory `json:"results"`
|
||||
}
|
||||
|
||||
// BlocksComplianceHistoryResponse is the response body to a request for blocks history.
|
||||
// swagger:model
|
||||
type BlocksComplianceHistoryResponse struct {
|
||||
// True if there is a next page for pagination
|
||||
// required: true
|
||||
HasNext bool `json:"hasNext"`
|
||||
|
||||
// The array of BlockHistory records.
|
||||
// required: true
|
||||
Results []*BlockHistory `json:"results"`
|
||||
}
|
||||
|
||||
// BoardHistory provides information about the history of a board.
|
||||
// swagger:model
|
||||
type BoardHistory struct {
|
||||
ID string `json:"id"`
|
||||
TeamID string `json:"teamId"`
|
||||
IsDeleted bool `json:"isDeleted"`
|
||||
DescendantLastUpdateAt int64 `json:"descendantLastUpdateAt"`
|
||||
DescendantFirstUpdateAt int64 `json:"descendantFirstUpdateAt"`
|
||||
CreatedBy string `json:"createdBy"`
|
||||
LastModifiedBy string `json:"lastModifiedBy"`
|
||||
}
|
||||
|
||||
// BlockHistory provides information about the history of a block.
|
||||
// swagger:model
|
||||
type BlockHistory struct {
|
||||
ID string `json:"id"`
|
||||
TeamID string `json:"teamId"`
|
||||
BoardID string `json:"boardId"`
|
||||
Type string `json:"type"`
|
||||
IsDeleted bool `json:"isDeleted"`
|
||||
LastUpdateAt int64 `json:"lastUpdateAt"`
|
||||
FirstUpdateAt int64 `json:"firstUpdateAt"`
|
||||
CreatedBy string `json:"createdBy"`
|
||||
LastModifiedBy string `json:"lastModifiedBy"`
|
||||
}
|
||||
|
||||
type QueryBoardsForComplianceOptions struct {
|
||||
TeamID string // if not empty then filter for specific team, otherwise all teams are included
|
||||
Page int // page number to select when paginating
|
||||
PerPage int // number of blocks per page (default=60)
|
||||
}
|
||||
|
||||
type QueryBoardsComplianceHistoryOptions struct {
|
||||
ModifiedSince int64 // if non-zero then filter for records with update_at greater than ModifiedSince
|
||||
IncludeDeleted bool // if true then deleted blocks are included
|
||||
TeamID string // if not empty then filter for specific team, otherwise all teams are included
|
||||
Page int // page number to select when paginating
|
||||
PerPage int // number of blocks per page (default=60)
|
||||
}
|
||||
|
||||
type QueryBlocksComplianceHistoryOptions struct {
|
||||
ModifiedSince int64 // if non-zero then filter for records with update_at greater than ModifiedSince
|
||||
IncludeDeleted bool // if true then deleted blocks are included
|
||||
TeamID string // if not empty then filter for specific team, otherwise all teams are included
|
||||
BoardID string // if not empty then filter for specific board, otherwise all boards are included
|
||||
Page int // page number to select when paginating
|
||||
PerPage int // number of blocks per page (default=60)
|
||||
}
|
50
server/model/mocks/propValueResolverMock.go
Normal file
50
server/model/mocks/propValueResolverMock.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/mattermost/focalboard/server/model (interfaces: PropValueResolver)
|
||||
|
||||
// Package mocks is a generated GoMock package.
|
||||
package mocks
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
model "github.com/mattermost/focalboard/server/model"
|
||||
)
|
||||
|
||||
// MockPropValueResolver is a mock of PropValueResolver interface.
|
||||
type MockPropValueResolver struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockPropValueResolverMockRecorder
|
||||
}
|
||||
|
||||
// MockPropValueResolverMockRecorder is the mock recorder for MockPropValueResolver.
|
||||
type MockPropValueResolverMockRecorder struct {
|
||||
mock *MockPropValueResolver
|
||||
}
|
||||
|
||||
// NewMockPropValueResolver creates a new mock instance.
|
||||
func NewMockPropValueResolver(ctrl *gomock.Controller) *MockPropValueResolver {
|
||||
mock := &MockPropValueResolver{ctrl: ctrl}
|
||||
mock.recorder = &MockPropValueResolverMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockPropValueResolver) EXPECT() *MockPropValueResolverMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// GetUserByID mocks base method.
|
||||
func (m *MockPropValueResolver) GetUserByID(arg0 string) (*model.User, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetUserByID", arg0)
|
||||
ret0, _ := ret[0].(*model.User)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetUserByID indicates an expected call of GetUserByID.
|
||||
func (mr *MockPropValueResolverMockRecorder) GetUserByID(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserByID", reflect.TypeOf((*MockPropValueResolver)(nil).GetUserByID), arg0)
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
|
||||
type AppAPI interface {
|
||||
GetBlockHistory(blockID string, opts model.QueryBlockHistoryOptions) ([]*model.Block, error)
|
||||
GetSubTree2(boardID, blockID string, opts model.QuerySubtreeOptions) ([]*model.Block, error)
|
||||
GetBlockHistoryNewestChildren(parentID string, opts model.QueryBlockHistoryChildOptions) ([]*model.Block, bool, error)
|
||||
GetBoardAndCardByID(blockID string) (board *model.Board, card *model.Block, err error)
|
||||
|
||||
GetUserByID(userID string) (*model.User, error)
|
||||
|
|
|
@ -149,10 +149,10 @@ func (dg *diffGenerator) generateDiffsForCard(card *model.Block, schema model.Pr
|
|||
}
|
||||
|
||||
// fetch all card content blocks that were updated after last notify
|
||||
opts := model.QuerySubtreeOptions{
|
||||
opts := model.QueryBlockHistoryChildOptions{
|
||||
AfterUpdateAt: dg.lastNotifyAt,
|
||||
}
|
||||
blocks, err := dg.store.GetSubTree2(card.BoardID, card.ID, opts)
|
||||
blocks, _, err := dg.store.GetBlockHistoryNewestChildren(card.ID, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get subtree for card %s: %w", card.ID, err)
|
||||
}
|
||||
|
|
|
@ -246,7 +246,7 @@ func appendCommentChanges(fields []*mm_model.SlackAttachmentField, cardDiff *Dif
|
|||
msg = child.NewBlock.Title
|
||||
}
|
||||
|
||||
if child.NewBlock == nil && child.OldBlock != nil {
|
||||
if (child.NewBlock == nil || child.NewBlock.DeleteAt != 0) && child.OldBlock != nil {
|
||||
// deleted comment
|
||||
format = "~~`%s`~~"
|
||||
msg = stripNewlines(child.OldBlock.Title)
|
||||
|
@ -266,36 +266,73 @@ func appendCommentChanges(fields []*mm_model.SlackAttachmentField, cardDiff *Dif
|
|||
|
||||
func appendContentChanges(fields []*mm_model.SlackAttachmentField, cardDiff *Diff, logger mlog.LoggerIFace) []*mm_model.SlackAttachmentField {
|
||||
for _, child := range cardDiff.Diffs {
|
||||
if child.BlockType != model.TypeComment {
|
||||
var newTitle, oldTitle string
|
||||
if child.OldBlock != nil {
|
||||
oldTitle = child.OldBlock.Title
|
||||
}
|
||||
if child.NewBlock != nil {
|
||||
newTitle = child.NewBlock.Title
|
||||
}
|
||||
var opAdd, opDelete bool
|
||||
var opString string
|
||||
|
||||
// only strip newlines when modifying or deleting
|
||||
if child.OldBlock != nil && child.NewBlock == nil {
|
||||
newTitle = stripNewlines(newTitle)
|
||||
switch {
|
||||
case child.OldBlock == nil && child.NewBlock != nil:
|
||||
opAdd = true
|
||||
opString = "added" // TODO: localize when i18n added to server
|
||||
case child.NewBlock == nil || child.NewBlock.DeleteAt != 0:
|
||||
opDelete = true
|
||||
opString = "deleted"
|
||||
default:
|
||||
opString = "modified"
|
||||
}
|
||||
|
||||
var newTitle, oldTitle string
|
||||
if child.OldBlock != nil {
|
||||
oldTitle = child.OldBlock.Title
|
||||
}
|
||||
if child.NewBlock != nil {
|
||||
newTitle = child.NewBlock.Title
|
||||
}
|
||||
|
||||
switch child.BlockType {
|
||||
case model.TypeDivider, model.TypeComment:
|
||||
// do nothing
|
||||
continue
|
||||
case model.TypeImage:
|
||||
if newTitle == "" {
|
||||
newTitle = "An image was " + opString + "." // TODO: localize when i18n added to server
|
||||
}
|
||||
oldTitle = ""
|
||||
case model.TypeAttachment:
|
||||
if newTitle == "" {
|
||||
newTitle = "A file attachment was " + opString + "." // TODO: localize when i18n added to server
|
||||
}
|
||||
oldTitle = ""
|
||||
default:
|
||||
if !opAdd {
|
||||
if opDelete {
|
||||
newTitle = ""
|
||||
}
|
||||
// only strip newlines when modifying or deleting
|
||||
oldTitle = stripNewlines(oldTitle)
|
||||
newTitle = stripNewlines(newTitle)
|
||||
}
|
||||
|
||||
if newTitle == oldTitle {
|
||||
continue
|
||||
}
|
||||
|
||||
markdown := generateMarkdownDiff(oldTitle, newTitle, logger)
|
||||
if markdown == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
fields = append(fields, &mm_model.SlackAttachmentField{
|
||||
Short: false,
|
||||
Title: "Description",
|
||||
Value: markdown,
|
||||
})
|
||||
}
|
||||
|
||||
logger.Debug("appendContentChanges",
|
||||
mlog.String("type", string(child.BlockType)),
|
||||
mlog.String("opString", opString),
|
||||
mlog.String("oldTitle", oldTitle),
|
||||
mlog.String("newTitle", newTitle),
|
||||
)
|
||||
|
||||
markdown := generateMarkdownDiff(oldTitle, newTitle, logger)
|
||||
if markdown == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
fields = append(fields, &mm_model.SlackAttachmentField{
|
||||
Short: false,
|
||||
Title: "Description",
|
||||
Value: markdown,
|
||||
})
|
||||
}
|
||||
return fields
|
||||
}
|
||||
|
|
|
@ -200,7 +200,7 @@ func (n *notifier) notifySubscribers(hint *model.NotificationHint) error {
|
|||
}
|
||||
|
||||
opts := DiffConvOpts{
|
||||
Language: "en", // TODO: use correct language with i18n available on server.
|
||||
Language: "en", // TODO: use correct language when i18n is available on server.
|
||||
MakeCardLink: func(block *model.Block, board *model.Board, card *model.Block) string {
|
||||
return fmt.Sprintf("[%s](%s)", block.Title, utils.MakeCardLink(n.serverRoot, board.TeamID, board.ID, card.ID))
|
||||
},
|
||||
|
|
|
@ -5,6 +5,8 @@ package notifysubscriptions
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/focalboard/server/model"
|
||||
|
@ -73,6 +75,16 @@ func (b *Backend) Name() string {
|
|||
}
|
||||
|
||||
func (b *Backend) getBlockUpdateFreq(blockType model.BlockType) time.Duration {
|
||||
// check for env variable override
|
||||
sFreq := os.Getenv("MM_BOARDS_NOTIFY_FREQ_SECONDS")
|
||||
if sFreq != "" && sFreq != "0" {
|
||||
if freq, err := strconv.ParseInt(sFreq, 10, 64); err != nil {
|
||||
b.logger.Error("Environment variable MM_BOARDS_NOTIFY_FREQ_SECONDS invalid (ignoring)", mlog.Err(err))
|
||||
} else {
|
||||
return time.Second * time.Duration(freq)
|
||||
}
|
||||
}
|
||||
|
||||
switch blockType {
|
||||
case model.TypeCard:
|
||||
return time.Second * time.Duration(b.notifyFreqCardSeconds)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/mattermost/focalboard/server/utils"
|
||||
|
||||
|
@ -42,27 +43,31 @@ func (s *SQLStore) timestampToCharField(name string, as string) string {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *SQLStore) blockFields() []string {
|
||||
func (s *SQLStore) blockFields(tableAlias string) []string {
|
||||
if tableAlias != "" && !strings.HasSuffix(tableAlias, ".") {
|
||||
tableAlias += "."
|
||||
}
|
||||
|
||||
return []string{
|
||||
"id",
|
||||
"parent_id",
|
||||
"created_by",
|
||||
"modified_by",
|
||||
s.escapeField("schema"),
|
||||
"type",
|
||||
"title",
|
||||
"COALESCE(fields, '{}')",
|
||||
s.timestampToCharField("insert_at", "insertAt"),
|
||||
"create_at",
|
||||
"update_at",
|
||||
"delete_at",
|
||||
"COALESCE(board_id, '0')",
|
||||
tableAlias + "id",
|
||||
tableAlias + "parent_id",
|
||||
tableAlias + "created_by",
|
||||
tableAlias + "modified_by",
|
||||
tableAlias + s.escapeField("schema"),
|
||||
tableAlias + "type",
|
||||
tableAlias + "title",
|
||||
"COALESCE(" + tableAlias + "fields, '{}')",
|
||||
s.timestampToCharField(tableAlias+"insert_at", "insertAt"),
|
||||
tableAlias + "create_at",
|
||||
tableAlias + "update_at",
|
||||
tableAlias + "delete_at",
|
||||
"COALESCE(" + tableAlias + "board_id, '0')",
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SQLStore) getBlocks(db sq.BaseRunner, opts model.QueryBlocksOptions) ([]*model.Block, error) {
|
||||
query := s.getQueryBuilder(db).
|
||||
Select(s.blockFields()...).
|
||||
Select(s.blockFields("")...).
|
||||
From(s.tablePrefix + "blocks")
|
||||
|
||||
if opts.BoardID != "" {
|
||||
|
@ -115,7 +120,7 @@ func (s *SQLStore) getBlocksWithParent(db sq.BaseRunner, boardID, parentID strin
|
|||
|
||||
func (s *SQLStore) getBlocksByIDs(db sq.BaseRunner, ids []string) ([]*model.Block, error) {
|
||||
query := s.getQueryBuilder(db).
|
||||
Select(s.blockFields()...).
|
||||
Select(s.blockFields("")...).
|
||||
From(s.tablePrefix + "blocks").
|
||||
Where(sq.Eq{"id": ids})
|
||||
|
||||
|
@ -150,7 +155,7 @@ func (s *SQLStore) getBlocksWithType(db sq.BaseRunner, boardID, blockType string
|
|||
// getSubTree2 returns blocks within 2 levels of the given blockID.
|
||||
func (s *SQLStore) getSubTree2(db sq.BaseRunner, boardID string, blockID string, opts model.QuerySubtreeOptions) ([]*model.Block, error) {
|
||||
query := s.getQueryBuilder(db).
|
||||
Select(s.blockFields()...).
|
||||
Select(s.blockFields("")...).
|
||||
From(s.tablePrefix + "blocks").
|
||||
Where(sq.Or{sq.Eq{"id": blockID}, sq.Eq{"parent_id": blockID}}).
|
||||
Where(sq.Eq{"board_id": boardID}).
|
||||
|
@ -550,7 +555,7 @@ func (s *SQLStore) getBoardCount(db sq.BaseRunner) (int64, error) {
|
|||
|
||||
func (s *SQLStore) getBlock(db sq.BaseRunner, blockID string) (*model.Block, error) {
|
||||
query := s.getQueryBuilder(db).
|
||||
Select(s.blockFields()...).
|
||||
Select(s.blockFields("")...).
|
||||
From(s.tablePrefix + "blocks").
|
||||
Where(sq.Eq{"id": blockID})
|
||||
|
||||
|
@ -580,7 +585,7 @@ func (s *SQLStore) getBlockHistory(db sq.BaseRunner, blockID string, opts model.
|
|||
}
|
||||
|
||||
query := s.getQueryBuilder(db).
|
||||
Select(s.blockFields()...).
|
||||
Select(s.blockFields("")...).
|
||||
From(s.tablePrefix + "blocks_history").
|
||||
Where(sq.Eq{"id": blockID}).
|
||||
OrderBy("insert_at " + order + ", update_at" + order)
|
||||
|
@ -614,7 +619,7 @@ func (s *SQLStore) getBlockHistoryDescendants(db sq.BaseRunner, boardID string,
|
|||
}
|
||||
|
||||
query := s.getQueryBuilder(db).
|
||||
Select(s.blockFields()...).
|
||||
Select(s.blockFields("")...).
|
||||
From(s.tablePrefix + "blocks_history").
|
||||
Where(sq.Eq{"board_id": boardID}).
|
||||
OrderBy("insert_at " + order + ", update_at" + order)
|
||||
|
@ -641,6 +646,83 @@ func (s *SQLStore) getBlockHistoryDescendants(db sq.BaseRunner, boardID string,
|
|||
return s.blocksFromRows(rows)
|
||||
}
|
||||
|
||||
// getBlockHistoryNewestChildren returns the newest (latest) version child blocks for the
|
||||
// specified parent from the blocks_history table. This includes any deleted children.
|
||||
func (s *SQLStore) getBlockHistoryNewestChildren(db sq.BaseRunner, parentID string, opts model.QueryBlockHistoryChildOptions) ([]*model.Block, bool, error) {
|
||||
// as we're joining 2 queries, we need to avoid numbered
|
||||
// placeholders until the join is done, so we use the default
|
||||
// question mark placeholder here
|
||||
builder := s.getQueryBuilder(db).PlaceholderFormat(sq.Question)
|
||||
|
||||
sub := builder.
|
||||
Select("bh2.id", "MAX(bh2.insert_at) AS max_insert_at").
|
||||
From(s.tablePrefix + "blocks_history AS bh2").
|
||||
Where(sq.Eq{"bh2.parent_id": parentID}).
|
||||
GroupBy("bh2.id")
|
||||
|
||||
if opts.AfterUpdateAt != 0 {
|
||||
sub = sub.Where(sq.Gt{"bh2.update_at": opts.AfterUpdateAt})
|
||||
}
|
||||
|
||||
if opts.BeforeUpdateAt != 0 {
|
||||
sub = sub.Where(sq.Lt{"bh2.update_at": opts.BeforeUpdateAt})
|
||||
}
|
||||
|
||||
subQuery, subArgs, err := sub.ToSql()
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("getBlockHistoryNewestChildren unable to generate subquery: %w", err)
|
||||
}
|
||||
|
||||
query := s.getQueryBuilder(db).
|
||||
Select(s.blockFields("bh")...).
|
||||
From(s.tablePrefix+"blocks_history AS bh").
|
||||
InnerJoin("("+subQuery+") AS sub ON bh.id=sub.id AND bh.insert_at=sub.max_insert_at", subArgs...)
|
||||
|
||||
if opts.Page != 0 {
|
||||
query = query.Offset(uint64(opts.Page * opts.PerPage))
|
||||
}
|
||||
|
||||
if opts.PerPage > 0 {
|
||||
// limit+1 to detect if more records available
|
||||
query = query.Limit(uint64(opts.PerPage + 1))
|
||||
}
|
||||
|
||||
sql, args, err := query.ToSql()
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("getBlockHistoryNewestChildren unable to generate sql: %w", err)
|
||||
}
|
||||
|
||||
// if we're using postgres or sqlite, we need to replace the
|
||||
// question mark placeholder with the numbered dollar one, now
|
||||
// that the full query is built
|
||||
if s.dbType == model.PostgresDBType || s.dbType == model.SqliteDBType {
|
||||
var rErr error
|
||||
sql, rErr = sq.Dollar.ReplacePlaceholders(sql)
|
||||
if rErr != nil {
|
||||
return nil, false, fmt.Errorf("getBlockHistoryNewestChildren unable to replace sql placeholders: %w", rErr)
|
||||
}
|
||||
}
|
||||
|
||||
rows, err := db.Query(sql, args...)
|
||||
if err != nil {
|
||||
s.logger.Error(`getBlockHistoryNewestChildren ERROR`, mlog.Err(err))
|
||||
return nil, false, err
|
||||
}
|
||||
defer s.CloseRows(rows)
|
||||
|
||||
blocks, err := s.blocksFromRows(rows)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
hasMore := false
|
||||
if opts.PerPage > 0 && len(blocks) > opts.PerPage {
|
||||
blocks = blocks[:opts.PerPage]
|
||||
hasMore = true
|
||||
}
|
||||
return blocks, hasMore, nil
|
||||
}
|
||||
|
||||
// getBoardAndCardByID returns the first parent of type `card` and first parent of type `board` for the block specified by ID.
|
||||
// `board` and/or `card` may return nil without error if the block does not belong to a board or card.
|
||||
func (s *SQLStore) getBoardAndCardByID(db sq.BaseRunner, blockID string) (board *model.Board, card *model.Block, err error) {
|
||||
|
|
|
@ -17,41 +17,31 @@ import (
|
|||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||
)
|
||||
|
||||
func boardFields(prefix string) []string {
|
||||
fields := []string{
|
||||
"id",
|
||||
"team_id",
|
||||
"COALESCE(channel_id, '')",
|
||||
"COALESCE(created_by, '')",
|
||||
"modified_by",
|
||||
"type",
|
||||
"minimum_role",
|
||||
"title",
|
||||
"description",
|
||||
"icon",
|
||||
"show_description",
|
||||
"is_template",
|
||||
"template_version",
|
||||
"COALESCE(properties, '{}')",
|
||||
"COALESCE(card_properties, '[]')",
|
||||
"create_at",
|
||||
"update_at",
|
||||
"delete_at",
|
||||
func boardFields(tableAlias string) []string {
|
||||
if tableAlias != "" && !strings.HasSuffix(tableAlias, ".") {
|
||||
tableAlias += "."
|
||||
}
|
||||
|
||||
if prefix == "" {
|
||||
return fields
|
||||
return []string{
|
||||
tableAlias + "id",
|
||||
tableAlias + "team_id",
|
||||
"COALESCE(" + tableAlias + "channel_id, '')",
|
||||
"COALESCE(" + tableAlias + "created_by, '')",
|
||||
tableAlias + "modified_by",
|
||||
tableAlias + "type",
|
||||
tableAlias + "minimum_role",
|
||||
tableAlias + "title",
|
||||
tableAlias + "description",
|
||||
tableAlias + "icon",
|
||||
tableAlias + "show_description",
|
||||
tableAlias + "is_template",
|
||||
tableAlias + "template_version",
|
||||
"COALESCE(" + tableAlias + "properties, '{}')",
|
||||
"COALESCE(" + tableAlias + "card_properties, '[]')",
|
||||
tableAlias + "create_at",
|
||||
tableAlias + "update_at",
|
||||
tableAlias + "delete_at",
|
||||
}
|
||||
|
||||
prefixedFields := make([]string, len(fields))
|
||||
for i, field := range fields {
|
||||
if strings.HasPrefix(field, "COALESCE(") {
|
||||
prefixedFields[i] = strings.Replace(field, "COALESCE(", "COALESCE("+prefix, 1)
|
||||
} else {
|
||||
prefixedFields[i] = prefix + field
|
||||
}
|
||||
}
|
||||
return prefixedFields
|
||||
}
|
||||
|
||||
func boardHistoryFields() []string {
|
||||
|
|
241
server/services/store/sqlstore/compliance.go
Normal file
241
server/services/store/sqlstore/compliance.go
Normal file
|
@ -0,0 +1,241 @@
|
|||
package sqlstore
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
"github.com/mattermost/focalboard/server/model"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||
)
|
||||
|
||||
func (s *SQLStore) getBoardsForCompliance(db sq.BaseRunner, opts model.QueryBoardsForComplianceOptions) ([]*model.Board, bool, error) {
|
||||
query := s.getQueryBuilder(db).
|
||||
Select(boardFields("b.")...).
|
||||
From(s.tablePrefix + "boards as b")
|
||||
|
||||
if opts.TeamID != "" {
|
||||
query = query.Where(sq.Eq{"b.team_id": opts.TeamID})
|
||||
}
|
||||
|
||||
if opts.Page != 0 {
|
||||
query = query.Offset(uint64(opts.Page * opts.PerPage))
|
||||
}
|
||||
|
||||
if opts.PerPage > 0 {
|
||||
// N+1 to check if there's a next page for pagination
|
||||
query = query.Limit(uint64(opts.PerPage) + 1)
|
||||
}
|
||||
|
||||
rows, err := query.Query()
|
||||
if err != nil {
|
||||
s.logger.Error(`GetBoardsForCompliance ERROR`, mlog.Err(err))
|
||||
return nil, false, err
|
||||
}
|
||||
defer s.CloseRows(rows)
|
||||
|
||||
boards, err := s.boardsFromRows(rows)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
var hasMore bool
|
||||
if opts.PerPage > 0 && len(boards) > opts.PerPage {
|
||||
boards = boards[0:opts.PerPage]
|
||||
hasMore = true
|
||||
}
|
||||
return boards, hasMore, nil
|
||||
}
|
||||
|
||||
func (s *SQLStore) getBoardsComplianceHistory(db sq.BaseRunner, opts model.QueryBoardsComplianceHistoryOptions) ([]*model.BoardHistory, bool, error) {
|
||||
queryDescendentLastUpdate := s.getQueryBuilder(db).
|
||||
Select("MAX(blk1.update_at)").
|
||||
From(s.tablePrefix + "blocks_history as blk1").
|
||||
Where("blk1.board_id=bh.id")
|
||||
|
||||
if !opts.IncludeDeleted {
|
||||
queryDescendentLastUpdate.Where(sq.Eq{"blk1.delete_at": 0})
|
||||
}
|
||||
|
||||
sqlDescendentLastUpdate, _, _ := queryDescendentLastUpdate.ToSql()
|
||||
|
||||
queryDescendentFirstUpdate := s.getQueryBuilder(db).
|
||||
Select("MIN(blk2.update_at)").
|
||||
From(s.tablePrefix + "blocks_history as blk2").
|
||||
Where("blk2.board_id=bh.id")
|
||||
|
||||
if !opts.IncludeDeleted {
|
||||
queryDescendentFirstUpdate.Where(sq.Eq{"blk2.delete_at": 0})
|
||||
}
|
||||
|
||||
sqlDescendentFirstUpdate, _, _ := queryDescendentFirstUpdate.ToSql()
|
||||
|
||||
query := s.getQueryBuilder(db).
|
||||
Select(
|
||||
"bh.id",
|
||||
"bh.team_id",
|
||||
"CASE WHEN bh.delete_at=0 THEN false ELSE true END AS isDeleted",
|
||||
"COALESCE(("+sqlDescendentLastUpdate+"),0) as decendentLastUpdateAt",
|
||||
"COALESCE(("+sqlDescendentFirstUpdate+"),0) as decendentFirstUpdateAt",
|
||||
"bh.created_by",
|
||||
"bh.modified_by",
|
||||
).
|
||||
From(s.tablePrefix + "boards_history as bh")
|
||||
|
||||
if !opts.IncludeDeleted {
|
||||
// filtering out deleted boards; join with boards table to ensure no history
|
||||
// for deleted boards are returned. Deleted boards won't exist in boards table.
|
||||
query = query.Join(s.tablePrefix + "boards as b ON b.id=bh.id")
|
||||
}
|
||||
|
||||
query = query.Where(sq.Gt{"bh.update_at": opts.ModifiedSince}).
|
||||
GroupBy("bh.id", "bh.team_id", "bh.delete_at", "bh.created_by", "bh.modified_by").
|
||||
OrderBy("decendentLastUpdateAt desc", "bh.id")
|
||||
|
||||
if opts.TeamID != "" {
|
||||
query = query.Where(sq.Eq{"bh.team_id": opts.TeamID})
|
||||
}
|
||||
|
||||
if opts.Page != 0 {
|
||||
query = query.Offset(uint64(opts.Page * opts.PerPage))
|
||||
}
|
||||
|
||||
if opts.PerPage > 0 {
|
||||
// N+1 to check if there's a next page for pagination
|
||||
query = query.Limit(uint64(opts.PerPage) + 1)
|
||||
}
|
||||
|
||||
rows, err := query.Query()
|
||||
if err != nil {
|
||||
s.logger.Error(`GetBoardsComplianceHistory ERROR`, mlog.Err(err))
|
||||
return nil, false, err
|
||||
}
|
||||
defer s.CloseRows(rows)
|
||||
|
||||
history, err := s.boardsHistoryFromRows(rows)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
var hasMore bool
|
||||
if opts.PerPage > 0 && len(history) > opts.PerPage {
|
||||
history = history[0:opts.PerPage]
|
||||
hasMore = true
|
||||
}
|
||||
return history, hasMore, nil
|
||||
}
|
||||
|
||||
func (s *SQLStore) getBlocksComplianceHistory(db sq.BaseRunner, opts model.QueryBlocksComplianceHistoryOptions) ([]*model.BlockHistory, bool, error) {
|
||||
query := s.getQueryBuilder(db).
|
||||
Select(
|
||||
"bh.id",
|
||||
"brd.team_id",
|
||||
"bh.board_id",
|
||||
"bh.type",
|
||||
"CASE WHEN bh.delete_at=0 THEN false ELSE true END AS isDeleted",
|
||||
"max(bh.update_at) as lastUpdateAt",
|
||||
"min(bh.update_at) as firstUpdateAt",
|
||||
"bh.created_by",
|
||||
"bh.modified_by",
|
||||
).
|
||||
From(s.tablePrefix + "blocks_history as bh").
|
||||
Join(s.tablePrefix + "boards_history as brd on brd.id=bh.board_id")
|
||||
|
||||
if !opts.IncludeDeleted {
|
||||
// filtering out deleted blocks; join with blocks table to ensure no history
|
||||
// for deleted blocks are returned. Deleted blocks won't exist in blocks table.
|
||||
query = query.Join(s.tablePrefix + "blocks as b ON b.id=bh.id")
|
||||
}
|
||||
|
||||
query = query.Where(sq.Gt{"bh.update_at": opts.ModifiedSince}).
|
||||
GroupBy("bh.id", "brd.team_id", "bh.board_id", "bh.type", "bh.delete_at", "bh.created_by", "bh.modified_by").
|
||||
OrderBy("lastUpdateAt desc", "bh.id")
|
||||
|
||||
if opts.TeamID != "" {
|
||||
query = query.Where(sq.Eq{"brd.team_id": opts.TeamID})
|
||||
}
|
||||
|
||||
if opts.BoardID != "" {
|
||||
query = query.Where(sq.Eq{"bh.board_id": opts.BoardID})
|
||||
}
|
||||
|
||||
if opts.Page != 0 {
|
||||
query = query.Offset(uint64(opts.Page * opts.PerPage))
|
||||
}
|
||||
|
||||
if opts.PerPage > 0 {
|
||||
// N+1 to check if there's a next page for pagination
|
||||
query = query.Limit(uint64(opts.PerPage) + 1)
|
||||
}
|
||||
|
||||
rows, err := query.Query()
|
||||
if err != nil {
|
||||
s.logger.Error(`GetBlocksComplianceHistory ERROR`, mlog.Err(err))
|
||||
return nil, false, err
|
||||
}
|
||||
defer s.CloseRows(rows)
|
||||
|
||||
history, err := s.blocksHistoryFromRows(rows)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
var hasMore bool
|
||||
if opts.PerPage > 0 && len(history) > opts.PerPage {
|
||||
history = history[0:opts.PerPage]
|
||||
hasMore = true
|
||||
}
|
||||
return history, hasMore, nil
|
||||
}
|
||||
|
||||
func (s *SQLStore) boardsHistoryFromRows(rows *sql.Rows) ([]*model.BoardHistory, error) {
|
||||
history := []*model.BoardHistory{}
|
||||
|
||||
for rows.Next() {
|
||||
boardHistory := &model.BoardHistory{}
|
||||
|
||||
err := rows.Scan(
|
||||
&boardHistory.ID,
|
||||
&boardHistory.TeamID,
|
||||
&boardHistory.IsDeleted,
|
||||
&boardHistory.DescendantLastUpdateAt,
|
||||
&boardHistory.DescendantFirstUpdateAt,
|
||||
&boardHistory.CreatedBy,
|
||||
&boardHistory.LastModifiedBy,
|
||||
)
|
||||
if err != nil {
|
||||
s.logger.Error("boardsHistoryFromRows scan error", mlog.Err(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
history = append(history, boardHistory)
|
||||
}
|
||||
return history, nil
|
||||
}
|
||||
|
||||
func (s *SQLStore) blocksHistoryFromRows(rows *sql.Rows) ([]*model.BlockHistory, error) {
|
||||
history := []*model.BlockHistory{}
|
||||
|
||||
for rows.Next() {
|
||||
blockHistory := &model.BlockHistory{}
|
||||
|
||||
err := rows.Scan(
|
||||
&blockHistory.ID,
|
||||
&blockHistory.TeamID,
|
||||
&blockHistory.BoardID,
|
||||
&blockHistory.Type,
|
||||
&blockHistory.IsDeleted,
|
||||
&blockHistory.LastUpdateAt,
|
||||
&blockHistory.FirstUpdateAt,
|
||||
&blockHistory.CreatedBy,
|
||||
&blockHistory.LastModifiedBy,
|
||||
)
|
||||
if err != nil {
|
||||
s.logger.Error("blocksHistoryFromRows scan error", mlog.Err(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
history = append(history, blockHistory)
|
||||
}
|
||||
return history, nil
|
||||
}
|
|
@ -143,11 +143,6 @@ func (s *SQLStore) CreateUser(user *model.User) (*model.User, error) {
|
|||
|
||||
}
|
||||
|
||||
func (s *SQLStore) DBVersion() string {
|
||||
return s.dBVersion()
|
||||
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
|
|
|
@ -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() string {
|
||||
func (s *SQLStore) DBVersion() string {
|
||||
var version string
|
||||
var row *sql.Row
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
@ -173,6 +174,11 @@ type Store interface {
|
|||
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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
294
server/services/store/storetests/compliance.go
Normal file
294
server/services/store/storetests/compliance.go
Normal file
|
@ -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))
|
||||
})
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
4
webapp/package-lock.json
generated
4
webapp/package-lock.json
generated
|
@ -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",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "focalboard",
|
||||
"version": "7.8.0",
|
||||
"version": "7.9.0",
|
||||
"private": true,
|
||||
"description": "",
|
||||
"scripts": {
|
||||
|
|
|
@ -240,6 +240,20 @@ exports[`components/cardDialog limited card shows hidden view (no toolbar) 1`] =
|
|||
<div
|
||||
class="toolbar--right"
|
||||
>
|
||||
<div
|
||||
class="d-flex"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
>
|
||||
<i
|
||||
class="CompassIcon icon-paperclip"
|
||||
/>
|
||||
<span>
|
||||
Attach
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
aria-label="Close dialog"
|
||||
title="Close dialog"
|
||||
|
|
|
@ -159,7 +159,7 @@ const CardDialog = (props: Props): JSX.Element => {
|
|||
const attachmentBlock = createAttachmentBlock(uploadingBlock)
|
||||
attachmentBlock.isUploading = true
|
||||
dispatch(updateAttachments([attachmentBlock]))
|
||||
if (attachment.size > clientConfig.maxFileSize) {
|
||||
if (attachment.size > clientConfig.maxFileSize && Utils.isFocalboardPlugin()) {
|
||||
removeUploadingAttachment(uploadingBlock)
|
||||
sendFlashMessage({content: intl.formatMessage({id: 'AttachmentBlock.failed', defaultMessage: 'Unable to upload the file. Attachment size limit reached.'}), severity: 'normal'})
|
||||
} else {
|
||||
|
@ -262,7 +262,10 @@ const CardDialog = (props: Props): JSX.Element => {
|
|||
</>
|
||||
)
|
||||
|
||||
return (<>{attachBtn()}{following ? unfollowBtn : followBtn}</>)
|
||||
if (!isTemplate && Utils.isFocalboardPlugin() && !card?.limited) {
|
||||
return (<>{attachBtn()}{following ? unfollowBtn : followBtn}</>)
|
||||
}
|
||||
return (<>{attachBtn()}</>)
|
||||
}
|
||||
|
||||
const followingCards = useAppSelector(getUserBlockSubscriptionList)
|
||||
|
@ -276,7 +279,7 @@ const CardDialog = (props: Props): JSX.Element => {
|
|||
className='cardDialog'
|
||||
onClose={props.onClose}
|
||||
toolsMenu={!props.readonly && !card?.limited && menu}
|
||||
toolbar={!isTemplate && Utils.isFocalboardPlugin() && !card?.limited && toolbar}
|
||||
toolbar={toolbar}
|
||||
>
|
||||
{isTemplate &&
|
||||
<div className='banner'>
|
||||
|
|
|
@ -10,7 +10,7 @@ exports[`components/sidebar/GlobalHeader header menu should match snapshot 1`] =
|
|||
/>
|
||||
<a
|
||||
class="GlobalHeaderComponent__button help-button"
|
||||
href="https://www.focalboard.com/fwlink/doc-boards.html?v=7.8.0"
|
||||
href="https://www.focalboard.com/fwlink/doc-boards.html?v=7.9.0"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
|
|
|
@ -30,7 +30,7 @@ const Entry = (props: EntryComponentProps): ReactElement => {
|
|||
/>
|
||||
<div className={theme?.mentionSuggestionsEntryText}>
|
||||
{mention.name}
|
||||
{BotBadge && <BotBadge show={mention.is_bot}/>}
|
||||
{BotBadge && mention.is_bot && <BotBadge/>}
|
||||
<GuestBadge show={mention.is_guest}/>
|
||||
</div>
|
||||
<div className={theme?.mentionSuggestionsEntryText}>
|
||||
|
|
|
@ -51,9 +51,9 @@ exports[`components/sidebarSidebar dont show hidden boards 1`] = `
|
|||
>
|
||||
<div
|
||||
class="version"
|
||||
title="v7.8.0"
|
||||
title="v7.9.0"
|
||||
>
|
||||
v7.8.0
|
||||
v7.9.0
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -252,9 +252,9 @@ exports[`components/sidebarSidebar should assign default category if current boa
|
|||
>
|
||||
<div
|
||||
class="version"
|
||||
title="v7.8.0"
|
||||
title="v7.9.0"
|
||||
>
|
||||
v7.8.0
|
||||
v7.9.0
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -508,9 +508,9 @@ exports[`components/sidebarSidebar shouldnt do any category assignment is board
|
|||
>
|
||||
<div
|
||||
class="version"
|
||||
title="v7.8.0"
|
||||
title="v7.9.0"
|
||||
>
|
||||
v7.8.0
|
||||
v7.9.0
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -919,9 +919,9 @@ exports[`components/sidebarSidebar sidebar hidden 1`] = `
|
|||
>
|
||||
<div
|
||||
class="version"
|
||||
title="v7.8.0"
|
||||
title="v7.9.0"
|
||||
>
|
||||
v7.8.0
|
||||
v7.9.0
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1213,9 +1213,9 @@ exports[`components/sidebarSidebar some categories hidden 1`] = `
|
|||
>
|
||||
<div
|
||||
class="version"
|
||||
title="v7.8.0"
|
||||
title="v7.9.0"
|
||||
>
|
||||
v7.8.0
|
||||
v7.9.0
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -103,6 +103,8 @@ const TableRow = (props: Props) => {
|
|||
} else if (template && template.type === 'updatedBy') {
|
||||
groupValue = card.modifiedBy
|
||||
}
|
||||
} else if (Array.isArray(groupValue)) {
|
||||
groupValue = groupValue[0]
|
||||
}
|
||||
if (collapsedOptionIds.indexOf(groupValue) > -1) {
|
||||
className += ' hidden'
|
||||
|
|
|
@ -36,8 +36,8 @@ class Constants {
|
|||
static readonly titleColumnId = '__title'
|
||||
static readonly badgesColumnId = '__badges'
|
||||
|
||||
static readonly versionString = '7.8.0'
|
||||
static readonly versionDisplayString = 'Feb 2023'
|
||||
static readonly versionString = '7.9.0'
|
||||
static readonly versionDisplayString = 'Mar 2023'
|
||||
|
||||
static readonly archiveHelpPage = 'https://docs.mattermost.com/boards/migrate-to-boards.html'
|
||||
static readonly imports = [
|
||||
|
|
|
@ -665,19 +665,39 @@ class Mutator {
|
|||
const newBlockIDs: string[] = []
|
||||
|
||||
if (propertyTemplate.type !== newType) {
|
||||
if (propertyTemplate.type === 'select' || propertyTemplate.type === 'multiSelect') { // If the old type was either select or multiselect
|
||||
const isNewTypeSelectOrMulti = newType === 'select' || newType === 'multiSelect'
|
||||
const isNewTypeSelectOrMulti = newType === 'select' || newType === 'multiSelect'
|
||||
const isNewTypePersonOrMulti = newType === 'person' || newType === 'multiPerson'
|
||||
|
||||
const isOldTypeSelectOrMulti = propertyTemplate.type === 'select' || propertyTemplate.type === 'multiSelect'
|
||||
const isOldTypePersonOrMulti = propertyTemplate.type === 'person' || propertyTemplate.type === 'multiPerson'
|
||||
|
||||
// If the old type was either select/multiselect or person/multiperson
|
||||
if (isOldTypeSelectOrMulti || isOldTypePersonOrMulti) {
|
||||
for (const card of cards) {
|
||||
const oldValue = Array.isArray(card.fields.properties[propertyTemplate.id]) ? (card.fields.properties[propertyTemplate.id].length > 0 && card.fields.properties[propertyTemplate.id][0]) : card.fields.properties[propertyTemplate.id]
|
||||
// if array get first value, if exists
|
||||
const oldValue = Array.isArray(card.fields.properties[propertyTemplate.id]) ? (card.fields.properties[propertyTemplate.id].length > 0 && card.fields.properties[propertyTemplate.id][0] as string) : card.fields.properties[propertyTemplate.id] as string
|
||||
if (oldValue) {
|
||||
const newValue = isNewTypeSelectOrMulti ? propertyTemplate.options.find((o) => o.id === oldValue)?.id : propertyTemplate.options.find((o) => o.id === oldValue)?.value
|
||||
let newValue: string | undefined
|
||||
if (isOldTypePersonOrMulti) {
|
||||
if (isNewTypePersonOrMulti) {
|
||||
newValue = oldValue
|
||||
}
|
||||
} else if (isNewTypeSelectOrMulti) {
|
||||
if (isOldTypeSelectOrMulti) {
|
||||
newValue = propertyTemplate.options.find((o) => o.id === oldValue)?.id
|
||||
} else {
|
||||
newValue = propertyTemplate.options.find((o) => o.id === oldValue)?.value
|
||||
}
|
||||
}
|
||||
const newCard = createCard(card)
|
||||
|
||||
if (newValue) {
|
||||
newCard.fields.properties[propertyTemplate.id] = newType === 'multiSelect' ? [newValue] : newValue
|
||||
if (newType === 'multiSelect' || newType === 'multiPerson') {
|
||||
newCard.fields.properties[propertyTemplate.id] = [newValue]
|
||||
} else {
|
||||
newCard.fields.properties[propertyTemplate.id] = newValue
|
||||
}
|
||||
} else {
|
||||
// This was an invalid select option, so delete it
|
||||
// This was an invalid select option or old person id, so delete it
|
||||
delete newCard.fields.properties[propertyTemplate.id]
|
||||
}
|
||||
|
||||
|
@ -690,7 +710,7 @@ class Mutator {
|
|||
newTemplate.options = propertyTemplate.options
|
||||
}
|
||||
}
|
||||
} else if (newType === 'select' || newType === 'multiSelect') { // if the new type is either select or multiselect
|
||||
} else if (isNewTypeSelectOrMulti) { // if the new type is either select or multiselect - old type is other
|
||||
// Map values to new template option IDs
|
||||
for (const card of cards) {
|
||||
const oldValue = card.fields.properties[propertyTemplate.id] as string
|
||||
|
@ -708,6 +728,18 @@ class Mutator {
|
|||
const newCard = createCard(card)
|
||||
newCard.fields.properties[propertyTemplate.id] = newType === 'multiSelect' ? [option.id] : option.id
|
||||
|
||||
newBlocks.push(newCard)
|
||||
newBlockIDs.push(newCard.id)
|
||||
oldBlocks.push(card)
|
||||
}
|
||||
}
|
||||
} else if (isNewTypePersonOrMulti) { // if the new type is either person or multiperson - old type is other
|
||||
// Clear old values
|
||||
for (const card of cards) {
|
||||
const oldValue = card.fields.properties[propertyTemplate.id] as string
|
||||
if (oldValue) {
|
||||
const newCard = createCard(card)
|
||||
delete newCard.fields.properties[propertyTemplate.id]
|
||||
newBlocks.push(newCard)
|
||||
newBlockIDs.push(newCard.id)
|
||||
oldBlocks.push(card)
|
||||
|
|
|
@ -85,7 +85,7 @@ const ConfirmPerson = (props: PropertyProps): JSX.Element => {
|
|||
setConfirmAddUser(null)
|
||||
await mutator.createBoardMember(newMember)
|
||||
|
||||
if (userIDs) {
|
||||
if (propertyTemplate.type === 'multiPerson') {
|
||||
await mutator.changePropertyValue(board.id, card, propertyTemplate.id, [...userIDs, newMember.userId])
|
||||
} else {
|
||||
await mutator.changePropertyValue(board.id, card, propertyTemplate.id, newMember.userId)
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
@import '../../styles/z-index';
|
||||
|
||||
.status-dropdown-menu {
|
||||
> .Menu {
|
||||
position: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.Menu {
|
||||
.Menu:not(#statusDropdownMenu) {
|
||||
@include z-index(menu);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -179,7 +173,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.Menu,
|
||||
.Menu:not(#statusDropdownMenu),
|
||||
.SubMenuOption .SubMenu {
|
||||
@media screen and (max-width: 430px) {
|
||||
position: fixed;
|
||||
|
|
Loading…
Reference in a new issue