Ldap groups phase1 (#9752)
* Initial models, API, app, and persistence of groups and group syncing. * Consistent letter casing in ldif. * Moves group-specific migrations into func. * Adds API endpoint to retrieve LDAP groups (and associated MM groups) one tree level at a time. * Adds mattermost group id to SCIMGroup (if available). * Splits user and group creation so that memberOf works. Returns users from ldap interface. * Updates method name. * Returns users IDs instead of User. * Removes non-essential group data. * MM-11807: Add GroupFilter to LDAP config. (#9513) * MM-11807: Add GroupFilter to LDAP config. * Add diagnostic. * Adds new config option for using 'memberOf' overlay. * Adds API endpoint to link a group. * Removes debug statements. * Adds unlink group API endpoint. * Fix to LDAP API. Adds API method to client4 and app. * Adds some missing app methods. Renames API unexported func. * Fixes link/unlink API path to accept valid DNs. * Allow any character for DN portion of path. * Switches from DN to objectGUID or entryUUID as the remote identifier linking LDAP groups to MM groups. * Formatting. * Formatting. * Setting group name field to an ID for phase 1. * Adds an LDAP config field to Setting up configuration for local LDAP. * Changes to LDAP and GroupStore interfaces. * Draft of nesting groups in API response. * Removes unnecessary tree models. * Updates group membershipt create store method to also restore. * Adds new config to test config. * Accept AD format length. * Switches to SetUniqueTogether method. * Updates revert. * Tweaks to syncing queries . * Updates query for pending team and channel memberships. * Removes old GroupSyncableScanner usage. Some formatting and renaming. * Fixes bug setting syncable type in selecting paged. * Adds tests for syncables populator. * Only add users to teams and channels that are not deleted. * Renames method. * Updates test LDAP setup. * Removes memberof config stuff. * Renames. * Updates test data. * Fix for gofmt. * Adds missing license. * Adds missing teardowns. * Test fix. * Adds a cycle to the groups test data. * Changes API to return flat list. * Removes some unused interface and app methods. * Returns empty braces if results are empty. * Adds more LDAP test data. * Fix for test data error. * Adds error. * Moves test groups. * Adds OU for load test data. * Moves load test ou creation to load data. * Adds a new bool flag to SCIMGroups. * Removes SCIMGroup completely. * Removes FULL JOIN because it is not supported in MySQL. * Adds tests for sync queries; renames constant. * Bad merge fix. * Vet fix. * Returning OK on delete ldap group link * Removes foreign key constraints. * Adding total to the ldap getAllGroups api endpoint * Adds get group members page. * Removes pagination from groups syncables list API. * Adding syncable check now that foreign key constraint is removes. * Joins teams and channels to group syncables. * Adds group member count. * Adding GetAllChannels and SearchAllChannels for system admins only * Fix. * Test fix from pagination removal. * Orders groupmembers by createat. * Fixing search of all channels * Test fix after removing pagination. * JSON syntax error fix. * Changing tests (for now) pending investigation. * Adding GetAllChannels and SearchAllChannels tests for the store * Adding GetAllChannels and SearchAllChannels API tests * Omit empty JSON values of group syncables. * Fixing GetAllChannels and SearchAllChannels tests * Fixing GetAllChannels and SearchAllChannels store tests * Fixing GetAllChannels api tests * Adds 'LDAP groups' feature flag. (#9861) * Migrate new client functions to idiomatic error handling * Test fixes. * Simplification of groups api (#9860) * Simplification of groups api * Fixing RequireSyncableType * Test fix. * Update api4/group.go Co-Authored-By: mkraft <martinkraft@gmail.com> * Update api4/group.go Co-Authored-By: mkraft <martinkraft@gmail.com> * Update api4/group.go Co-Authored-By: mkraft <martinkraft@gmail.com> * Update api4/group.go Co-Authored-By: mkraft <martinkraft@gmail.com> * Update api4/group.go Co-Authored-By: mkraft <martinkraft@gmail.com> * Update api4/group.go Co-Authored-By: mkraft <martinkraft@gmail.com> * Update api4/group.go Co-Authored-By: mkraft <martinkraft@gmail.com> * Update api4/group.go Co-Authored-By: mkraft <martinkraft@gmail.com> * Update api4/group.go Co-Authored-By: mkraft <martinkraft@gmail.com> * Update api4/group.go Co-Authored-By: mkraft <martinkraft@gmail.com> * Fix copy/paste error. * Fix copy/paste error. * Adds missing return, changes to correct HTTP status code. * Adds missing return, changes status codes. * Check for license. * Renames variable for new signature. * Adds client method to get a group. * Adds client method and tests for PatchGroup. * Adds more API tests. * Adds groups API tests. * Adds client method and tests for getting group syncables. * Adds tests for patching group teams and channels. * Update to translations. * Removes test. * Fix incorrect conditional. * Removes unnecessary nil check. * Removes unnecessary return. * Updates comment, removes unused variable. * Uses consistent JSON unmarshal pattern. * Uses consistent JSON unmarshal pattern. * Moves const block. * Switches 'already linked' from error to success response. * Removes commented-out code. * Switched to status ok. * Add parens for readability. * Fix copy/paste error. * Unexport some structs. * Removes repeated validity check. * Return without attempting commit if there's a rollback. * Fix incorrect HTTP status code. * Update store/sqlstore/group_supplier.go Co-Authored-By: mkraft <martinkraft@gmail.com> * Adds utility methods for going from groupsyncable to groupteam and groupchannel. * Fixing george suggestions (#9911) * Test fix. * Adds QA data to VC with visualization. * Fixes typo in graph image. * Update display name when re-linking in case it has changed in LDAP. * Adds ability to configure group display name and unique identifier. (#9923) * Adds ability to configure group display name and unique identifier. * Adds some configs to confi-ldap make command. * Fix for move of session. * Exposes method for use by SAML package. * Switches GroupSyncableType from int to string. * Update Jenkins build files. * Removes unused variable assignment. * Removes old unnecessary early return. * Removes unnecessary variable. * Moves param parsing before license and permissions checks. * Removes old code. * Compares agains underlying error rather than error id. * Switches tests to assertions. * Adds more assertions. * Adds missing return. * Adds space after comma for added legibility. * Moves a view model to the api package. * Unexports method. * Uses id validator function. * Fix docker-compose flag. * Typo fix. * Moves index creation to supplier. * Removes bad merge. * Renames parameter. * Re-adds space. * Removes unnecessary transaction. * Escapes the Groups table name with backticks because it is a reserved keyword. * Fix roles cache bug * Removing unnecesiary deserializing function * Switches table name rather than custom SQL everywhere for Postgres without backticks. * Removes redundant check for sql.ErrNoRows. * Removes redundant check for sql.ErrNoRows. * Removes data integrity check and redundant nil conditional. * Removes redundant check for sql.ErrNoRows. * Removes unnecessary query. * Removes ID length validation from persistence tier. * Makes some supplier methods idempotent. * Removes some empty switch defaults. * Renames Group Type field to Source. * Fix for mistaken field name change. * Uses IsValidId function. * Removes comment. * Changes json key name. * Removes test because no longer validating user. * Moves model state validation to app layer. * Don't create Groups.CanLeave column until phase 2. * Removes state validation until properties are used in phase 2. * Removes duplicated check. * Removes state validation until properties are used in phase 2. * Removes some tests until phase 2. * Comment-out a bunch of test related to CanLeave. * Extra unmarshal validation check. Removes more code for CanLeave. * Removes tests for CanLeave. * Explict error msg. * Rewrite queries. * Changes index name. Adds index. * Removes assertion. * Adds experimental feature flag.
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
f8b87cbe2d
Коммит
6d30b21dd2
20
Makefile
20
Makefile
@@ -156,15 +156,14 @@ ifeq ($(BUILD_ENTERPRISE_READY),true)
|
|||||||
-e LDAP_ORGANISATION="Mattermost Test" \
|
-e LDAP_ORGANISATION="Mattermost Test" \
|
||||||
-e LDAP_DOMAIN="mm.test.com" \
|
-e LDAP_DOMAIN="mm.test.com" \
|
||||||
-e LDAP_ADMIN_PASSWORD="mostest" \
|
-e LDAP_ADMIN_PASSWORD="mostest" \
|
||||||
-d osixia/openldap:1.1.6 > /dev/null;\
|
-d osixia/openldap:1.2.2 > /dev/null;\
|
||||||
sleep 10; \
|
sleep 10; \
|
||||||
docker exec -ti mattermost-openldap bash -c 'echo -e "dn: ou=testusers,dc=mm,dc=test,dc=com\nobjectclass: organizationalunit" | ldapadd -x -D "cn=admin,dc=mm,dc=test,dc=com" -w mostest';\
|
docker cp tests/add-users.ldif mattermost-openldap:/add-users.ldif;\
|
||||||
docker exec -ti mattermost-openldap bash -c 'echo -e "dn: uid=test.one,ou=testusers,dc=mm,dc=test,dc=com\nobjectclass: iNetOrgPerson\nsn: User\ncn: Test1\nmail: success+testone@simulator.amazonses.com" | ldapadd -x -D "cn=admin,dc=mm,dc=test,dc=com" -w mostest';\
|
docker cp tests/add-groups.ldif mattermost-openldap:/add-groups.ldif;\
|
||||||
docker exec -ti mattermost-openldap bash -c 'ldappasswd -s Password1 -D "cn=admin,dc=mm,dc=test,dc=com" -x "uid=test.one,ou=testusers,dc=mm,dc=test,dc=com" -w mostest';\
|
docker cp tests/qa-data.ldif mattermost-openldap:/qa-data.ldif;\
|
||||||
docker exec -ti mattermost-openldap bash -c 'echo -e "dn: uid=test.two,ou=testusers,dc=mm,dc=test,dc=com\nobjectclass: iNetOrgPerson\nsn: User\ncn: Test2\nmail: success+testtwo@simulator.amazonses.com" | ldapadd -x -D "cn=admin,dc=mm,dc=test,dc=com" -w mostest';\
|
docker exec -ti mattermost-openldap bash -c 'ldapadd -x -D "cn=admin,dc=mm,dc=test,dc=com" -w mostest -f /add-users.ldif';\
|
||||||
docker exec -ti mattermost-openldap bash -c 'ldappasswd -s Password1 -D "cn=admin,dc=mm,dc=test,dc=com" -x "uid=test.two,ou=testusers,dc=mm,dc=test,dc=com" -w mostest';\
|
docker exec -ti mattermost-openldap bash -c 'ldapadd -x -D "cn=admin,dc=mm,dc=test,dc=com" -w mostest -f /add-groups.ldif';\
|
||||||
docker exec -ti mattermost-openldap bash -c 'echo -e "dn: cn=tgroup,ou=testusers,dc=mm,dc=test,dc=com\nobjectclass: groupOfUniqueNames\nuniqueMember: uid=test.one,ou=testusers,dc=mm,dc=test,dc=com" | ldapadd -x -D "cn=admin,dc=mm,dc=test,dc=com" -w mostest';\
|
elif [ $(shell docker ps | grep -ci mattermost-openldap) -eq 0 ]; then \
|
||||||
elif [ $(shell docker ps --no-trunc --quiet --filter name=^/mattermost-openldap$$ | wc -l) -eq 0 ]; then \
|
|
||||||
echo restarting mattermost-openldap; \
|
echo restarting mattermost-openldap; \
|
||||||
docker start mattermost-openldap > /dev/null; \
|
docker start mattermost-openldap > /dev/null; \
|
||||||
sleep 10; \
|
sleep 10; \
|
||||||
@@ -510,7 +509,7 @@ config-ldap: ## Configures LDAP.
|
|||||||
@echo Setting up configuration for local LDAP
|
@echo Setting up configuration for local LDAP
|
||||||
|
|
||||||
@sed -i'' -e 's|"LdapServer": ".*"|"LdapServer": "dockerhost"|g' config/config.json
|
@sed -i'' -e 's|"LdapServer": ".*"|"LdapServer": "dockerhost"|g' config/config.json
|
||||||
@sed -i'' -e 's|"BaseDN": ".*"|"BaseDN": "ou=testusers,dc=mm,dc=test,dc=com"|g' config/config.json
|
@sed -i'' -e 's|"BaseDN": ".*"|"BaseDN": "dc=mm,dc=test,dc=com"|g' config/config.json
|
||||||
@sed -i'' -e 's|"BindUsername": ".*"|"BindUsername": "cn=admin,dc=mm,dc=test,dc=com"|g' config/config.json
|
@sed -i'' -e 's|"BindUsername": ".*"|"BindUsername": "cn=admin,dc=mm,dc=test,dc=com"|g' config/config.json
|
||||||
@sed -i'' -e 's|"BindPassword": ".*"|"BindPassword": "mostest"|g' config/config.json
|
@sed -i'' -e 's|"BindPassword": ".*"|"BindPassword": "mostest"|g' config/config.json
|
||||||
@sed -i'' -e 's|"FirstNameAttribute": ".*"|"FirstNameAttribute": "cn"|g' config/config.json
|
@sed -i'' -e 's|"FirstNameAttribute": ".*"|"FirstNameAttribute": "cn"|g' config/config.json
|
||||||
@@ -519,6 +518,9 @@ config-ldap: ## Configures LDAP.
|
|||||||
@sed -i'' -e 's|"EmailAttribute": ".*"|"EmailAttribute": "mail"|g' config/config.json
|
@sed -i'' -e 's|"EmailAttribute": ".*"|"EmailAttribute": "mail"|g' config/config.json
|
||||||
@sed -i'' -e 's|"UsernameAttribute": ".*"|"UsernameAttribute": "uid"|g' config/config.json
|
@sed -i'' -e 's|"UsernameAttribute": ".*"|"UsernameAttribute": "uid"|g' config/config.json
|
||||||
@sed -i'' -e 's|"IdAttribute": ".*"|"IdAttribute": "uid"|g' config/config.json
|
@sed -i'' -e 's|"IdAttribute": ".*"|"IdAttribute": "uid"|g' config/config.json
|
||||||
|
@sed -i'' -e 's|"LoginIdAttribute": ".*"|"LoginIdAttribute": "uid"|g' config/config.json
|
||||||
|
@sed -i'' -e 's|"GroupDisplayNameAttribute": ".*"|"GroupDisplayNameAttribute": "cn"|g' config/config.json
|
||||||
|
@sed -i'' -e 's|"GroupIdAttribute": ".*"|"GroupIdAttribute": "entryUUID"|g' config/config.json
|
||||||
|
|
||||||
config-reset: ## Resets the config/config.json file to the default.
|
config-reset: ## Resets the config/config.json file to the default.
|
||||||
@echo Resetting configuration to default
|
@echo Resetting configuration to default
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ type Routes struct {
|
|||||||
ReactionByNameForPostForUser *mux.Router // 'api/v4/users/{user_id:[A-Za-z0-9]+}/posts/{post_id:[A-Za-z0-9]+}/reactions/{emoji_name:[A-Za-z0-9_-+]+}'
|
ReactionByNameForPostForUser *mux.Router // 'api/v4/users/{user_id:[A-Za-z0-9]+}/posts/{post_id:[A-Za-z0-9]+}/reactions/{emoji_name:[A-Za-z0-9_-+]+}'
|
||||||
|
|
||||||
TermsOfService *mux.Router // 'api/v4/terms_of_service
|
TermsOfService *mux.Router // 'api/v4/terms_of_service
|
||||||
|
Groups *mux.Router // 'api/v4/groups'
|
||||||
}
|
}
|
||||||
|
|
||||||
type API struct {
|
type API struct {
|
||||||
@@ -205,6 +206,7 @@ func Init(configservice configservice.ConfigService, globalOptionsFunc app.AppOp
|
|||||||
api.BaseRoutes.Image = api.BaseRoutes.ApiRoot.PathPrefix("/image").Subrouter()
|
api.BaseRoutes.Image = api.BaseRoutes.ApiRoot.PathPrefix("/image").Subrouter()
|
||||||
|
|
||||||
api.BaseRoutes.TermsOfService = api.BaseRoutes.ApiRoot.PathPrefix("/terms_of_service").Subrouter()
|
api.BaseRoutes.TermsOfService = api.BaseRoutes.ApiRoot.PathPrefix("/terms_of_service").Subrouter()
|
||||||
|
api.BaseRoutes.Groups = api.BaseRoutes.ApiRoot.PathPrefix("/groups").Subrouter()
|
||||||
|
|
||||||
api.InitUser()
|
api.InitUser()
|
||||||
api.InitTeam()
|
api.InitTeam()
|
||||||
@@ -234,6 +236,7 @@ func Init(configservice configservice.ConfigService, globalOptionsFunc app.AppOp
|
|||||||
api.InitScheme()
|
api.InitScheme()
|
||||||
api.InitImage()
|
api.InitImage()
|
||||||
api.InitTermsOfService()
|
api.InitTermsOfService()
|
||||||
|
api.InitGroup()
|
||||||
api.InitAction()
|
api.InitAction()
|
||||||
|
|
||||||
root.Handle("/api/v4/{anything:.*}", http.HandlerFunc(api.Handle404))
|
root.Handle("/api/v4/{anything:.*}", http.HandlerFunc(api.Handle404))
|
||||||
|
|||||||
@@ -11,8 +11,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (api *API) InitChannel() {
|
func (api *API) InitChannel() {
|
||||||
|
api.BaseRoutes.Channels.Handle("", api.ApiSessionRequired(getAllChannels)).Methods("GET")
|
||||||
api.BaseRoutes.Channels.Handle("", api.ApiSessionRequired(createChannel)).Methods("POST")
|
api.BaseRoutes.Channels.Handle("", api.ApiSessionRequired(createChannel)).Methods("POST")
|
||||||
api.BaseRoutes.Channels.Handle("/direct", api.ApiSessionRequired(createDirectChannel)).Methods("POST")
|
api.BaseRoutes.Channels.Handle("/direct", api.ApiSessionRequired(createDirectChannel)).Methods("POST")
|
||||||
|
api.BaseRoutes.Channels.Handle("/search", api.ApiSessionRequired(searchAllChannels)).Methods("POST")
|
||||||
api.BaseRoutes.Channels.Handle("/group", api.ApiSessionRequired(createGroupChannel)).Methods("POST")
|
api.BaseRoutes.Channels.Handle("/group", api.ApiSessionRequired(createGroupChannel)).Methods("POST")
|
||||||
api.BaseRoutes.Channels.Handle("/members/{user_id:[A-Za-z0-9]+}/view", api.ApiSessionRequired(viewChannel)).Methods("POST")
|
api.BaseRoutes.Channels.Handle("/members/{user_id:[A-Za-z0-9]+}/view", api.ApiSessionRequired(viewChannel)).Methods("POST")
|
||||||
api.BaseRoutes.Channels.Handle("/{channel_id:[A-Za-z0-9]+}/scheme", api.ApiSessionRequired(updateChannelScheme)).Methods("PUT")
|
api.BaseRoutes.Channels.Handle("/{channel_id:[A-Za-z0-9]+}/scheme", api.ApiSessionRequired(updateChannelScheme)).Methods("PUT")
|
||||||
@@ -490,6 +492,21 @@ func getPinnedPosts(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
w.Write([]byte(clientPostList.ToJson()))
|
w.Write([]byte(clientPostList.ToJson()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getAllChannels(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
|
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
channels, err := c.App.GetAllChannels(c.Params.Page, c.Params.PerPage, false)
|
||||||
|
if err != nil {
|
||||||
|
c.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write([]byte(channels.ToJson()))
|
||||||
|
}
|
||||||
|
|
||||||
func getPublicChannelsForTeam(c *Context, w http.ResponseWriter, r *http.Request) {
|
func getPublicChannelsForTeam(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
c.RequireTeamId()
|
c.RequireTeamId()
|
||||||
if c.Err != nil {
|
if c.Err != nil {
|
||||||
@@ -693,6 +710,31 @@ func searchChannelsForTeam(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
w.Write([]byte(channels.ToJson()))
|
w.Write([]byte(channels.ToJson()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func searchAllChannels(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
props := model.ChannelSearchFromJson(r.Body)
|
||||||
|
if props == nil {
|
||||||
|
c.SetInvalidParam("channel_search")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
|
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
includeDeleted := r.URL.Query().Get("include_deleted") == "true"
|
||||||
|
|
||||||
|
channels, err := c.App.SearchAllChannels(props.Term, includeDeleted)
|
||||||
|
if err != nil {
|
||||||
|
c.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't fill in channels props, since unused by client and potentially expensive.
|
||||||
|
|
||||||
|
w.Write([]byte(channels.ToJson()))
|
||||||
|
}
|
||||||
|
|
||||||
func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) {
|
func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
c.RequireChannelId()
|
c.RequireChannelId()
|
||||||
if c.Err != nil {
|
if c.Err != nil {
|
||||||
|
|||||||
@@ -772,6 +772,36 @@ func TestGetChannelsForTeamForUser(t *testing.T) {
|
|||||||
CheckNoError(t, resp)
|
CheckNoError(t, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetAllChannels(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
Client := th.Client
|
||||||
|
|
||||||
|
channels, resp := th.SystemAdminClient.GetAllChannels(0, 20, "")
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
|
// At least, all the not-deleted channels created during the InitBasic
|
||||||
|
require.True(t, len(*channels) >= 3)
|
||||||
|
for _, c := range *channels {
|
||||||
|
require.NotEqual(t, c.TeamId, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
channels, resp = th.SystemAdminClient.GetAllChannels(0, 10, "")
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
require.True(t, len(*channels) >= 3)
|
||||||
|
|
||||||
|
channels, resp = th.SystemAdminClient.GetAllChannels(1, 1, "")
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
require.Len(t, *channels, 1)
|
||||||
|
|
||||||
|
channels, resp = th.SystemAdminClient.GetAllChannels(10000, 10000, "")
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
require.Len(t, *channels, 0)
|
||||||
|
|
||||||
|
_, resp = Client.GetAllChannels(0, 20, "")
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
}
|
||||||
|
|
||||||
func TestSearchChannels(t *testing.T) {
|
func TestSearchChannels(t *testing.T) {
|
||||||
th := Setup().InitBasic()
|
th := Setup().InitBasic()
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
@@ -827,6 +857,37 @@ func TestSearchChannels(t *testing.T) {
|
|||||||
CheckNoError(t, resp)
|
CheckNoError(t, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSearchAllChannels(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
Client := th.Client
|
||||||
|
|
||||||
|
search := &model.ChannelSearch{Term: th.BasicChannel.Name}
|
||||||
|
|
||||||
|
channels, resp := th.SystemAdminClient.SearchAllChannels(search)
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
|
assert.Len(t, *channels, 1)
|
||||||
|
assert.Equal(t, (*channels)[0].Id, th.BasicChannel.Id)
|
||||||
|
|
||||||
|
search.Term = th.BasicPrivateChannel.Name
|
||||||
|
channels, resp = th.SystemAdminClient.SearchAllChannels(search)
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
|
assert.Len(t, *channels, 1)
|
||||||
|
assert.Equal(t, (*channels)[0].Id, th.BasicPrivateChannel.Id)
|
||||||
|
|
||||||
|
search.Term = ""
|
||||||
|
channels, resp = th.SystemAdminClient.SearchAllChannels(search)
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
// At least, all the not-deleted channels created during the InitBasic
|
||||||
|
assert.True(t, len(*channels) >= 3)
|
||||||
|
|
||||||
|
search.Term = th.BasicChannel.Name
|
||||||
|
_, resp = Client.SearchAllChannels(search)
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
}
|
||||||
|
|
||||||
func TestDeleteChannel(t *testing.T) {
|
func TestDeleteChannel(t *testing.T) {
|
||||||
th := Setup().InitBasic()
|
th := Setup().InitBasic()
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
@@ -2345,7 +2406,6 @@ func TestUpdateChannelScheme(t *testing.T) {
|
|||||||
|
|
||||||
// Test an invalid scheme scope.
|
// Test an invalid scheme scope.
|
||||||
_, resp = th.SystemAdminClient.UpdateChannelScheme(channel.Id, teamScheme.Id)
|
_, resp = th.SystemAdminClient.UpdateChannelScheme(channel.Id, teamScheme.Id)
|
||||||
fmt.Printf("resp: %+v\n", resp)
|
|
||||||
CheckBadRequestStatus(t, resp)
|
CheckBadRequestStatus(t, resp)
|
||||||
|
|
||||||
// Test that an unauthenticated user gets rejected.
|
// Test that an unauthenticated user gets rejected.
|
||||||
|
|||||||
433
api4/group.go
Обычный файл
433
api4/group.go
Обычный файл
@@ -0,0 +1,433 @@
|
|||||||
|
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package api4
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/mattermost/mattermost-server/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
groupMemberActionCreate = iota
|
||||||
|
groupMemberActionDelete
|
||||||
|
)
|
||||||
|
|
||||||
|
func (api *API) InitGroup() {
|
||||||
|
// GET /api/v4/groups/:group_id
|
||||||
|
api.BaseRoutes.Groups.Handle("/{group_id:[A-Za-z0-9]+}",
|
||||||
|
api.ApiSessionRequired(getGroup)).Methods("GET")
|
||||||
|
|
||||||
|
// PUT /api/v4/groups/:group_id/patch
|
||||||
|
api.BaseRoutes.Groups.Handle("/{group_id:[A-Za-z0-9]+}/patch",
|
||||||
|
api.ApiSessionRequired(patchGroup)).Methods("PUT")
|
||||||
|
|
||||||
|
// POST /api/v4/groups/:group_id/teams/:team_id/link
|
||||||
|
// POST /api/v4/groups/:group_id/channels/:channel_id/link
|
||||||
|
api.BaseRoutes.Groups.Handle("/{group_id:[A-Za-z0-9]+}/{syncable_type:teams|channels}/{syncable_id:[A-Za-z0-9]+}/link",
|
||||||
|
api.ApiSessionRequired(linkGroupSyncable)).Methods("POST")
|
||||||
|
|
||||||
|
// DELETE /api/v4/groups/:group_id/teams/:team_id/link
|
||||||
|
// DELETE /api/v4/groups/:group_id/channels/:channel_id/link
|
||||||
|
api.BaseRoutes.Groups.Handle("/{group_id:[A-Za-z0-9]+}/{syncable_type:teams|channels}/{syncable_id:[A-Za-z0-9]+}/link",
|
||||||
|
api.ApiSessionRequired(unlinkGroupSyncable)).Methods("DELETE")
|
||||||
|
|
||||||
|
// GET /api/v4/groups/:group_id/teams/:team_id
|
||||||
|
// GET /api/v4/groups/:group_id/channels/:channel_id
|
||||||
|
api.BaseRoutes.Groups.Handle("/{group_id:[A-Za-z0-9]+}/{syncable_type:teams|channels}/{syncable_id:[A-Za-z0-9]+}",
|
||||||
|
api.ApiSessionRequired(getGroupSyncable)).Methods("GET")
|
||||||
|
|
||||||
|
// GET /api/v4/groups/:group_id/teams
|
||||||
|
// GET /api/v4/groups/:group_id/channels
|
||||||
|
api.BaseRoutes.Groups.Handle("/{group_id:[A-Za-z0-9]+}/{syncable_type:teams|channels}",
|
||||||
|
api.ApiSessionRequired(getGroupSyncables)).Methods("GET")
|
||||||
|
|
||||||
|
// PUT /api/v4/groups/:group_id/teams/:team_id/patch
|
||||||
|
// PUT /api/v4/groups/:group_id/channels/:channel_id/patch
|
||||||
|
api.BaseRoutes.Groups.Handle("/{group_id:[A-Za-z0-9]+}/{syncable_type:teams|channels}/{syncable_id:[A-Za-z0-9]+}/patch",
|
||||||
|
api.ApiSessionRequired(patchGroupSyncable)).Methods("PUT")
|
||||||
|
|
||||||
|
// GET /api/v4/groups/:group_id/members?page=0&per_page=100
|
||||||
|
api.BaseRoutes.Groups.Handle("/{group_id:[A-Za-z0-9]+}/members",
|
||||||
|
api.ApiSessionRequired(getGroupMembers)).Methods("GET")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGroup(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
c.RequireGroupId()
|
||||||
|
if c.Err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.App.License() == nil || !*c.App.License().Features.LDAPGroups {
|
||||||
|
c.Err = model.NewAppError("Api4.getGroup", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
|
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
group, err := c.App.GetGroup(c.Params.GroupId)
|
||||||
|
if err != nil {
|
||||||
|
c.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b, marshalErr := json.Marshal(group)
|
||||||
|
if marshalErr != nil {
|
||||||
|
c.Err = model.NewAppError("Api4.getGroup", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func patchGroup(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
c.RequireGroupId()
|
||||||
|
if c.Err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
groupPatch := model.GroupPatchFromJson(r.Body)
|
||||||
|
if groupPatch == nil {
|
||||||
|
c.SetInvalidParam("group")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.App.License() == nil || !*c.App.License().Features.LDAPGroups {
|
||||||
|
c.Err = model.NewAppError("Api4.patchGroup", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
|
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
group, err := c.App.GetGroup(c.Params.GroupId)
|
||||||
|
if err != nil {
|
||||||
|
c.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
group.Patch(groupPatch)
|
||||||
|
|
||||||
|
group, err = c.App.UpdateGroup(group)
|
||||||
|
if err != nil {
|
||||||
|
c.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b, marshalErr := json.Marshal(group)
|
||||||
|
if marshalErr != nil {
|
||||||
|
c.Err = model.NewAppError("Api4.patchGroup", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func linkGroupSyncable(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
c.RequireGroupId()
|
||||||
|
if c.Err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.RequireSyncableId()
|
||||||
|
if c.Err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
syncableID := c.Params.SyncableId
|
||||||
|
|
||||||
|
c.RequireSyncableType()
|
||||||
|
if c.Err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
syncableType := c.Params.SyncableType
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
c.Err = model.NewAppError("Api4.createGroupSyncable", "api.io_error", nil, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var patch *model.GroupSyncablePatch
|
||||||
|
err = json.Unmarshal(body, &patch)
|
||||||
|
if err != nil || patch == nil {
|
||||||
|
c.SetInvalidParam(fmt.Sprintf("Group%s", syncableType.String()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.App.License() == nil || !*c.App.License().Features.LDAPGroups {
|
||||||
|
c.Err = model.NewAppError("Api4.createGroupSyncable", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
|
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
groupSyncable, appErr := c.App.GetGroupSyncable(c.Params.GroupId, syncableID, syncableType)
|
||||||
|
if appErr != nil && appErr.DetailedError != sql.ErrNoRows.Error() {
|
||||||
|
c.Err = appErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if groupSyncable == nil {
|
||||||
|
groupSyncable = &model.GroupSyncable{
|
||||||
|
GroupId: c.Params.GroupId,
|
||||||
|
SyncableId: syncableID,
|
||||||
|
Type: syncableType,
|
||||||
|
}
|
||||||
|
groupSyncable.Patch(patch)
|
||||||
|
groupSyncable, appErr = c.App.CreateGroupSyncable(groupSyncable)
|
||||||
|
if appErr != nil {
|
||||||
|
c.Err = appErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
groupSyncable.DeleteAt = 0
|
||||||
|
groupSyncable.Patch(patch)
|
||||||
|
groupSyncable, appErr = c.App.UpdateGroupSyncable(groupSyncable)
|
||||||
|
if appErr != nil {
|
||||||
|
c.Err = appErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
|
||||||
|
b, marshalErr := json.Marshal(groupSyncable)
|
||||||
|
if marshalErr != nil {
|
||||||
|
c.Err = model.NewAppError("Api4.createGroupSyncable", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGroupSyncable(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
c.RequireGroupId()
|
||||||
|
if c.Err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.RequireSyncableId()
|
||||||
|
if c.Err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
syncableID := c.Params.SyncableId
|
||||||
|
|
||||||
|
c.RequireSyncableType()
|
||||||
|
if c.Err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
syncableType := c.Params.SyncableType
|
||||||
|
|
||||||
|
if c.App.License() == nil || !*c.App.License().Features.LDAPGroups {
|
||||||
|
c.Err = model.NewAppError("Api4.getGroupSyncable", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
|
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
groupSyncable, err := c.App.GetGroupSyncable(c.Params.GroupId, syncableID, syncableType)
|
||||||
|
if err != nil {
|
||||||
|
c.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b, marshalErr := json.Marshal(groupSyncable)
|
||||||
|
if marshalErr != nil {
|
||||||
|
c.Err = model.NewAppError("Api4.getGroupSyncable", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGroupSyncables(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
c.RequireGroupId()
|
||||||
|
if c.Err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.RequireSyncableType()
|
||||||
|
if c.Err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
syncableType := c.Params.SyncableType
|
||||||
|
|
||||||
|
if c.App.License() == nil || !*c.App.License().Features.LDAPGroups {
|
||||||
|
c.Err = model.NewAppError("Api4.getGroupSyncables", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
|
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
groupSyncables, err := c.App.GetGroupSyncables(c.Params.GroupId, syncableType)
|
||||||
|
if err != nil {
|
||||||
|
c.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b, marshalErr := json.Marshal(groupSyncables)
|
||||||
|
if marshalErr != nil {
|
||||||
|
c.Err = model.NewAppError("Api4.getGroupSyncables", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func patchGroupSyncable(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
c.RequireGroupId()
|
||||||
|
if c.Err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.RequireSyncableId()
|
||||||
|
if c.Err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
syncableID := c.Params.SyncableId
|
||||||
|
|
||||||
|
c.RequireSyncableType()
|
||||||
|
if c.Err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
syncableType := c.Params.SyncableType
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
c.Err = model.NewAppError("Api4.patchGroupSyncable", "api.io_error", nil, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var patch *model.GroupSyncablePatch
|
||||||
|
err = json.Unmarshal(body, &patch)
|
||||||
|
if err != nil || patch == nil {
|
||||||
|
c.SetInvalidParam(fmt.Sprintf("Group[%s]Patch", syncableType.String()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.App.License() == nil || !*c.App.License().Features.LDAPGroups {
|
||||||
|
c.Err = model.NewAppError("Api4.patchGroupSyncable", "api.ldap_groups.license_error", nil, "",
|
||||||
|
http.StatusNotImplemented)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
|
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
groupSyncable, appErr := c.App.GetGroupSyncable(c.Params.GroupId, syncableID, syncableType)
|
||||||
|
if appErr != nil {
|
||||||
|
c.Err = appErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
groupSyncable.Patch(patch)
|
||||||
|
|
||||||
|
groupSyncable, appErr = c.App.UpdateGroupSyncable(groupSyncable)
|
||||||
|
if appErr != nil {
|
||||||
|
c.Err = appErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b, marshalErr := json.Marshal(groupSyncable)
|
||||||
|
if marshalErr != nil {
|
||||||
|
c.Err = model.NewAppError("Api4.patchGroupSyncable", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unlinkGroupSyncable(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
c.RequireGroupId()
|
||||||
|
if c.Err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.RequireSyncableId()
|
||||||
|
if c.Err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
syncableID := c.Params.SyncableId
|
||||||
|
|
||||||
|
c.RequireSyncableType()
|
||||||
|
if c.Err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
syncableType := c.Params.SyncableType
|
||||||
|
|
||||||
|
if c.App.License() == nil || !*c.App.License().Features.LDAPGroups {
|
||||||
|
c.Err = model.NewAppError("Api4.unlinkGroupSyncable", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
|
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := c.App.DeleteGroupSyncable(c.Params.GroupId, syncableID, syncableType)
|
||||||
|
if err != nil {
|
||||||
|
c.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnStatusOK(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGroupMembers(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
c.RequireGroupId()
|
||||||
|
if c.Err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.App.License() == nil || !*c.App.License().Features.LDAPGroups {
|
||||||
|
c.Err = model.NewAppError("Api4.getGroupMembers", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
|
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
members, count, err := c.App.GetGroupMemberUsersPage(c.Params.GroupId, c.Params.Page, c.Params.PerPage)
|
||||||
|
if err != nil {
|
||||||
|
c.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b, marshalErr := json.Marshal(struct {
|
||||||
|
Members []*model.User `json:"members"`
|
||||||
|
Count int `json:"total_member_count"`
|
||||||
|
}{
|
||||||
|
Members: members,
|
||||||
|
Count: count,
|
||||||
|
})
|
||||||
|
if marshalErr != nil {
|
||||||
|
c.Err = model.NewAppError("Api4.getGroupMembers", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(b)
|
||||||
|
}
|
||||||
622
api4/group_test.go
Обычный файл
622
api4/group_test.go
Обычный файл
@@ -0,0 +1,622 @@
|
|||||||
|
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package api4
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/mattermost/mattermost-server/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetGroup(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
id := model.NewId()
|
||||||
|
g, err := th.App.CreateGroup(&model.Group{
|
||||||
|
DisplayName: "dn_" + id,
|
||||||
|
Name: "name" + id,
|
||||||
|
Source: model.GroupSourceLdap,
|
||||||
|
Description: "description_" + id,
|
||||||
|
RemoteId: model.NewId(),
|
||||||
|
})
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
_, response := th.Client.GetGroup(g.Id, "")
|
||||||
|
CheckNotImplementedStatus(t, response)
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.GetGroup(g.Id, "")
|
||||||
|
CheckNotImplementedStatus(t, response)
|
||||||
|
|
||||||
|
th.App.SetLicense(model.NewTestLicense("ldap"))
|
||||||
|
|
||||||
|
group, response := th.SystemAdminClient.GetGroup(g.Id, "")
|
||||||
|
CheckNoError(t, response)
|
||||||
|
|
||||||
|
assert.Equal(t, g.DisplayName, group.DisplayName)
|
||||||
|
assert.Equal(t, g.Name, group.Name)
|
||||||
|
assert.Equal(t, g.Source, group.Source)
|
||||||
|
assert.Equal(t, g.Description, group.Description)
|
||||||
|
assert.Equal(t, g.RemoteId, group.RemoteId)
|
||||||
|
assert.Equal(t, g.CreateAt, group.CreateAt)
|
||||||
|
assert.Equal(t, g.UpdateAt, group.UpdateAt)
|
||||||
|
assert.Equal(t, g.DeleteAt, group.DeleteAt)
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.GetGroup(model.NewId(), "")
|
||||||
|
CheckNotFoundStatus(t, response)
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.GetGroup("12345", "")
|
||||||
|
CheckBadRequestStatus(t, response)
|
||||||
|
|
||||||
|
th.SystemAdminClient.Logout()
|
||||||
|
_, response = th.SystemAdminClient.GetGroup(group.Id, "")
|
||||||
|
CheckUnauthorizedStatus(t, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPatchGroup(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
id := model.NewId()
|
||||||
|
g, err := th.App.CreateGroup(&model.Group{
|
||||||
|
DisplayName: "dn_" + id,
|
||||||
|
Name: "name" + id,
|
||||||
|
Source: model.GroupSourceLdap,
|
||||||
|
Description: "description_" + id,
|
||||||
|
RemoteId: model.NewId(),
|
||||||
|
})
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
updateFmt := "%s_updated"
|
||||||
|
|
||||||
|
newName := fmt.Sprintf(updateFmt, g.Name)
|
||||||
|
newDisplayName := fmt.Sprintf(updateFmt, g.DisplayName)
|
||||||
|
newDescription := fmt.Sprintf(updateFmt, g.Description)
|
||||||
|
|
||||||
|
gp := &model.GroupPatch{
|
||||||
|
Name: &newName,
|
||||||
|
DisplayName: &newDisplayName,
|
||||||
|
Description: &newDescription,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, response := th.Client.PatchGroup(g.Id, gp)
|
||||||
|
CheckNotImplementedStatus(t, response)
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.PatchGroup(g.Id, gp)
|
||||||
|
CheckNotImplementedStatus(t, response)
|
||||||
|
|
||||||
|
th.App.SetLicense(model.NewTestLicense("ldap"))
|
||||||
|
|
||||||
|
group2, response := th.SystemAdminClient.PatchGroup(g.Id, gp)
|
||||||
|
CheckOKStatus(t, response)
|
||||||
|
|
||||||
|
group, response := th.SystemAdminClient.GetGroup(g.Id, "")
|
||||||
|
CheckNoError(t, response)
|
||||||
|
|
||||||
|
assert.Equal(t, *gp.DisplayName, group.DisplayName)
|
||||||
|
assert.Equal(t, *gp.DisplayName, group2.DisplayName)
|
||||||
|
assert.Equal(t, *gp.Name, group.Name)
|
||||||
|
assert.Equal(t, *gp.Name, group2.Name)
|
||||||
|
assert.Equal(t, *gp.Description, group.Description)
|
||||||
|
assert.Equal(t, *gp.Description, group2.Description)
|
||||||
|
|
||||||
|
assert.Equal(t, group2.UpdateAt, group.UpdateAt)
|
||||||
|
|
||||||
|
assert.Equal(t, g.Source, group.Source)
|
||||||
|
assert.Equal(t, g.Source, group2.Source)
|
||||||
|
assert.Equal(t, g.RemoteId, group.RemoteId)
|
||||||
|
assert.Equal(t, g.RemoteId, group2.RemoteId)
|
||||||
|
assert.Equal(t, g.CreateAt, group.CreateAt)
|
||||||
|
assert.Equal(t, g.CreateAt, group2.CreateAt)
|
||||||
|
assert.Equal(t, g.DeleteAt, group.DeleteAt)
|
||||||
|
assert.Equal(t, g.DeleteAt, group2.DeleteAt)
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.PatchGroup(model.NewId(), gp)
|
||||||
|
CheckNotFoundStatus(t, response)
|
||||||
|
|
||||||
|
th.SystemAdminClient.Logout()
|
||||||
|
_, response = th.SystemAdminClient.PatchGroup(group.Id, gp)
|
||||||
|
CheckUnauthorizedStatus(t, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLinkGroupTeam(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
id := model.NewId()
|
||||||
|
g, err := th.App.CreateGroup(&model.Group{
|
||||||
|
DisplayName: "dn_" + id,
|
||||||
|
Name: "name" + id,
|
||||||
|
Source: model.GroupSourceLdap,
|
||||||
|
Description: "description_" + id,
|
||||||
|
RemoteId: model.NewId(),
|
||||||
|
})
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
patch := &model.GroupSyncablePatch{
|
||||||
|
CanLeave: model.NewBool(true),
|
||||||
|
AutoAdd: model.NewBool(true),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, response := th.Client.LinkGroupSyncable(g.Id, th.BasicTeam.Id, model.GroupSyncableTypeTeam, patch)
|
||||||
|
CheckNotImplementedStatus(t, response)
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.LinkGroupSyncable(g.Id, th.BasicTeam.Id, model.GroupSyncableTypeTeam, patch)
|
||||||
|
CheckNotImplementedStatus(t, response)
|
||||||
|
|
||||||
|
th.App.SetLicense(model.NewTestLicense("ldap"))
|
||||||
|
|
||||||
|
groupTeam, response := th.SystemAdminClient.LinkGroupSyncable(g.Id, th.BasicTeam.Id, model.GroupSyncableTypeTeam, patch)
|
||||||
|
assert.Equal(t, http.StatusCreated, response.StatusCode)
|
||||||
|
assert.NotNil(t, groupTeam)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLinkGroupChannel(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
id := model.NewId()
|
||||||
|
g, err := th.App.CreateGroup(&model.Group{
|
||||||
|
DisplayName: "dn_" + id,
|
||||||
|
Name: "name" + id,
|
||||||
|
Source: model.GroupSourceLdap,
|
||||||
|
Description: "description_" + id,
|
||||||
|
RemoteId: model.NewId(),
|
||||||
|
})
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
patch := &model.GroupSyncablePatch{
|
||||||
|
CanLeave: model.NewBool(true),
|
||||||
|
AutoAdd: model.NewBool(true),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, response := th.Client.LinkGroupSyncable(g.Id, th.BasicChannel.Id, model.GroupSyncableTypeChannel, patch)
|
||||||
|
CheckNotImplementedStatus(t, response)
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.LinkGroupSyncable(g.Id, th.BasicChannel.Id, model.GroupSyncableTypeChannel, patch)
|
||||||
|
CheckNotImplementedStatus(t, response)
|
||||||
|
|
||||||
|
th.App.SetLicense(model.NewTestLicense("ldap"))
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.LinkGroupSyncable(g.Id, th.BasicChannel.Id, model.GroupSyncableTypeChannel, patch)
|
||||||
|
assert.Equal(t, http.StatusCreated, response.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnlinkGroupTeam(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
id := model.NewId()
|
||||||
|
g, err := th.App.CreateGroup(&model.Group{
|
||||||
|
DisplayName: "dn_" + id,
|
||||||
|
Name: "name" + id,
|
||||||
|
Source: model.GroupSourceLdap,
|
||||||
|
Description: "description_" + id,
|
||||||
|
RemoteId: model.NewId(),
|
||||||
|
})
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
patch := &model.GroupSyncablePatch{
|
||||||
|
CanLeave: model.NewBool(true),
|
||||||
|
AutoAdd: model.NewBool(true),
|
||||||
|
}
|
||||||
|
|
||||||
|
th.App.SetLicense(model.NewTestLicense("ldap"))
|
||||||
|
|
||||||
|
_, response := th.SystemAdminClient.LinkGroupSyncable(g.Id, th.BasicTeam.Id, model.GroupSyncableTypeTeam, patch)
|
||||||
|
assert.Equal(t, http.StatusCreated, response.StatusCode)
|
||||||
|
|
||||||
|
th.App.SetLicense(nil)
|
||||||
|
|
||||||
|
response = th.Client.UnlinkGroupSyncable(g.Id, th.BasicTeam.Id, model.GroupSyncableTypeTeam)
|
||||||
|
CheckNotImplementedStatus(t, response)
|
||||||
|
|
||||||
|
response = th.SystemAdminClient.UnlinkGroupSyncable(g.Id, th.BasicTeam.Id, model.GroupSyncableTypeTeam)
|
||||||
|
CheckNotImplementedStatus(t, response)
|
||||||
|
|
||||||
|
th.App.SetLicense(model.NewTestLicense("ldap"))
|
||||||
|
|
||||||
|
response = th.SystemAdminClient.UnlinkGroupSyncable(g.Id, th.BasicTeam.Id, model.GroupSyncableTypeTeam)
|
||||||
|
CheckOKStatus(t, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnlinkGroupChannel(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
id := model.NewId()
|
||||||
|
g, err := th.App.CreateGroup(&model.Group{
|
||||||
|
DisplayName: "dn_" + id,
|
||||||
|
Name: "name" + id,
|
||||||
|
Source: model.GroupSourceLdap,
|
||||||
|
Description: "description_" + id,
|
||||||
|
RemoteId: model.NewId(),
|
||||||
|
})
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
patch := &model.GroupSyncablePatch{
|
||||||
|
CanLeave: model.NewBool(true),
|
||||||
|
AutoAdd: model.NewBool(true),
|
||||||
|
}
|
||||||
|
|
||||||
|
th.App.SetLicense(model.NewTestLicense("ldap"))
|
||||||
|
|
||||||
|
_, response := th.SystemAdminClient.LinkGroupSyncable(g.Id, th.BasicChannel.Id, model.GroupSyncableTypeChannel, patch)
|
||||||
|
assert.Equal(t, http.StatusCreated, response.StatusCode)
|
||||||
|
|
||||||
|
th.App.SetLicense(nil)
|
||||||
|
|
||||||
|
response = th.Client.UnlinkGroupSyncable(g.Id, th.BasicChannel.Id, model.GroupSyncableTypeChannel)
|
||||||
|
CheckNotImplementedStatus(t, response)
|
||||||
|
|
||||||
|
response = th.SystemAdminClient.UnlinkGroupSyncable(g.Id, th.BasicChannel.Id, model.GroupSyncableTypeChannel)
|
||||||
|
CheckNotImplementedStatus(t, response)
|
||||||
|
|
||||||
|
th.App.SetLicense(model.NewTestLicense("ldap"))
|
||||||
|
|
||||||
|
response = th.SystemAdminClient.UnlinkGroupSyncable(g.Id, th.BasicChannel.Id, model.GroupSyncableTypeChannel)
|
||||||
|
CheckOKStatus(t, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetGroupTeam(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
id := model.NewId()
|
||||||
|
g, err := th.App.CreateGroup(&model.Group{
|
||||||
|
DisplayName: "dn_" + id,
|
||||||
|
Name: "name" + id,
|
||||||
|
Source: model.GroupSourceLdap,
|
||||||
|
Description: "description_" + id,
|
||||||
|
RemoteId: model.NewId(),
|
||||||
|
})
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
_, response := th.Client.GetGroupSyncable(g.Id, th.BasicTeam.Id, model.GroupSyncableTypeTeam, "")
|
||||||
|
CheckNotImplementedStatus(t, response)
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.GetGroupSyncable(g.Id, th.BasicTeam.Id, model.GroupSyncableTypeTeam, "")
|
||||||
|
CheckNotImplementedStatus(t, response)
|
||||||
|
|
||||||
|
th.App.SetLicense(model.NewTestLicense("ldap"))
|
||||||
|
|
||||||
|
patch := &model.GroupSyncablePatch{
|
||||||
|
CanLeave: model.NewBool(true),
|
||||||
|
AutoAdd: model.NewBool(true),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.LinkGroupSyncable(g.Id, th.BasicTeam.Id, model.GroupSyncableTypeTeam, patch)
|
||||||
|
assert.Equal(t, http.StatusCreated, response.StatusCode)
|
||||||
|
|
||||||
|
groupSyncable, response := th.SystemAdminClient.GetGroupSyncable(g.Id, th.BasicTeam.Id, model.GroupSyncableTypeTeam, "")
|
||||||
|
CheckOKStatus(t, response)
|
||||||
|
assert.NotNil(t, groupSyncable)
|
||||||
|
|
||||||
|
assert.Equal(t, g.Id, groupSyncable.GroupId)
|
||||||
|
assert.Equal(t, th.BasicTeam.Id, groupSyncable.SyncableId)
|
||||||
|
assert.Equal(t, *patch.AutoAdd, groupSyncable.AutoAdd)
|
||||||
|
// assert.Equal(t, *patch.CanLeave, groupSyncable.CanLeave) // TODO: Re-add this test in phase 2 of LDAP groups sync.
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.GetGroupSyncable(model.NewId(), th.BasicTeam.Id, model.GroupSyncableTypeTeam, "")
|
||||||
|
CheckNotFoundStatus(t, response)
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.GetGroupSyncable(g.Id, model.NewId(), model.GroupSyncableTypeTeam, "")
|
||||||
|
CheckNotFoundStatus(t, response)
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.GetGroupSyncable("asdfasdfe3", th.BasicTeam.Id, model.GroupSyncableTypeTeam, "")
|
||||||
|
CheckBadRequestStatus(t, response)
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.GetGroupSyncable(g.Id, "asdfasdfe3", model.GroupSyncableTypeTeam, "")
|
||||||
|
CheckBadRequestStatus(t, response)
|
||||||
|
|
||||||
|
th.SystemAdminClient.Logout()
|
||||||
|
_, response = th.SystemAdminClient.GetGroupSyncable(g.Id, th.BasicTeam.Id, model.GroupSyncableTypeTeam, "")
|
||||||
|
CheckUnauthorizedStatus(t, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetGroupChannel(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
id := model.NewId()
|
||||||
|
g, err := th.App.CreateGroup(&model.Group{
|
||||||
|
DisplayName: "dn_" + id,
|
||||||
|
Name: "name" + id,
|
||||||
|
Source: model.GroupSourceLdap,
|
||||||
|
Description: "description_" + id,
|
||||||
|
RemoteId: model.NewId(),
|
||||||
|
})
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
_, response := th.Client.GetGroupSyncable(g.Id, th.BasicChannel.Id, model.GroupSyncableTypeChannel, "")
|
||||||
|
CheckNotImplementedStatus(t, response)
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.GetGroupSyncable(g.Id, th.BasicChannel.Id, model.GroupSyncableTypeChannel, "")
|
||||||
|
CheckNotImplementedStatus(t, response)
|
||||||
|
|
||||||
|
th.App.SetLicense(model.NewTestLicense("ldap"))
|
||||||
|
|
||||||
|
patch := &model.GroupSyncablePatch{
|
||||||
|
CanLeave: model.NewBool(true),
|
||||||
|
AutoAdd: model.NewBool(true),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.LinkGroupSyncable(g.Id, th.BasicChannel.Id, model.GroupSyncableTypeChannel, patch)
|
||||||
|
assert.Equal(t, http.StatusCreated, response.StatusCode)
|
||||||
|
|
||||||
|
groupSyncable, response := th.SystemAdminClient.GetGroupSyncable(g.Id, th.BasicChannel.Id, model.GroupSyncableTypeChannel, "")
|
||||||
|
CheckOKStatus(t, response)
|
||||||
|
assert.NotNil(t, groupSyncable)
|
||||||
|
|
||||||
|
assert.Equal(t, g.Id, groupSyncable.GroupId)
|
||||||
|
assert.Equal(t, th.BasicChannel.Id, groupSyncable.SyncableId)
|
||||||
|
assert.Equal(t, *patch.AutoAdd, groupSyncable.AutoAdd)
|
||||||
|
// assert.Equal(t, *patch.CanLeave, groupSyncable.CanLeave) // TODO: Re-add this test in phase 2 of LDAP groups sync.
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.GetGroupSyncable(model.NewId(), th.BasicChannel.Id, model.GroupSyncableTypeChannel, "")
|
||||||
|
CheckNotFoundStatus(t, response)
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.GetGroupSyncable(g.Id, model.NewId(), model.GroupSyncableTypeChannel, "")
|
||||||
|
CheckNotFoundStatus(t, response)
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.GetGroupSyncable("asdfasdfe3", th.BasicChannel.Id, model.GroupSyncableTypeChannel, "")
|
||||||
|
CheckBadRequestStatus(t, response)
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.GetGroupSyncable(g.Id, "asdfasdfe3", model.GroupSyncableTypeChannel, "")
|
||||||
|
CheckBadRequestStatus(t, response)
|
||||||
|
|
||||||
|
th.SystemAdminClient.Logout()
|
||||||
|
_, response = th.SystemAdminClient.GetGroupSyncable(g.Id, th.BasicChannel.Id, model.GroupSyncableTypeChannel, "")
|
||||||
|
CheckUnauthorizedStatus(t, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetGroupTeams(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
id := model.NewId()
|
||||||
|
g, err := th.App.CreateGroup(&model.Group{
|
||||||
|
DisplayName: "dn_" + id,
|
||||||
|
Name: "name" + id,
|
||||||
|
Source: model.GroupSourceLdap,
|
||||||
|
Description: "description_" + id,
|
||||||
|
RemoteId: model.NewId(),
|
||||||
|
})
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
th.App.SetLicense(model.NewTestLicense("ldap"))
|
||||||
|
|
||||||
|
patch := &model.GroupSyncablePatch{
|
||||||
|
CanLeave: model.NewBool(true),
|
||||||
|
AutoAdd: model.NewBool(true),
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
team := th.CreateTeam()
|
||||||
|
_, response := th.SystemAdminClient.LinkGroupSyncable(g.Id, team.Id, model.GroupSyncableTypeTeam, patch)
|
||||||
|
assert.Equal(t, http.StatusCreated, response.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
th.App.SetLicense(nil)
|
||||||
|
|
||||||
|
_, response := th.Client.GetGroupSyncables(g.Id, model.GroupSyncableTypeTeam, "")
|
||||||
|
CheckNotImplementedStatus(t, response)
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.GetGroupSyncables(g.Id, model.GroupSyncableTypeTeam, "")
|
||||||
|
CheckNotImplementedStatus(t, response)
|
||||||
|
|
||||||
|
th.App.SetLicense(model.NewTestLicense("ldap"))
|
||||||
|
|
||||||
|
_, response = th.Client.GetGroupSyncables(g.Id, model.GroupSyncableTypeTeam, "")
|
||||||
|
assert.Equal(t, http.StatusForbidden, response.StatusCode)
|
||||||
|
|
||||||
|
groupSyncables, response := th.SystemAdminClient.GetGroupSyncables(g.Id, model.GroupSyncableTypeTeam, "")
|
||||||
|
CheckOKStatus(t, response)
|
||||||
|
|
||||||
|
assert.Len(t, groupSyncables, 10)
|
||||||
|
|
||||||
|
th.SystemAdminClient.Logout()
|
||||||
|
_, response = th.SystemAdminClient.GetGroupSyncables(g.Id, model.GroupSyncableTypeTeam, "")
|
||||||
|
CheckUnauthorizedStatus(t, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetGroupChannels(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
id := model.NewId()
|
||||||
|
g, err := th.App.CreateGroup(&model.Group{
|
||||||
|
DisplayName: "dn_" + id,
|
||||||
|
Name: "name" + id,
|
||||||
|
Source: model.GroupSourceLdap,
|
||||||
|
Description: "description_" + id,
|
||||||
|
RemoteId: model.NewId(),
|
||||||
|
})
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
th.App.SetLicense(model.NewTestLicense("ldap"))
|
||||||
|
|
||||||
|
patch := &model.GroupSyncablePatch{
|
||||||
|
CanLeave: model.NewBool(true),
|
||||||
|
AutoAdd: model.NewBool(true),
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
channel := th.CreatePublicChannel()
|
||||||
|
_, response := th.SystemAdminClient.LinkGroupSyncable(g.Id, channel.Id, model.GroupSyncableTypeChannel, patch)
|
||||||
|
assert.Equal(t, http.StatusCreated, response.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
th.App.SetLicense(nil)
|
||||||
|
|
||||||
|
_, response := th.Client.GetGroupSyncables(g.Id, model.GroupSyncableTypeChannel, "")
|
||||||
|
CheckNotImplementedStatus(t, response)
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.GetGroupSyncables(g.Id, model.GroupSyncableTypeChannel, "")
|
||||||
|
CheckNotImplementedStatus(t, response)
|
||||||
|
|
||||||
|
th.App.SetLicense(model.NewTestLicense("ldap"))
|
||||||
|
|
||||||
|
_, response = th.Client.GetGroupSyncables(g.Id, model.GroupSyncableTypeChannel, "")
|
||||||
|
assert.Equal(t, http.StatusForbidden, response.StatusCode)
|
||||||
|
|
||||||
|
groupSyncables, response := th.SystemAdminClient.GetGroupSyncables(g.Id, model.GroupSyncableTypeChannel, "")
|
||||||
|
CheckOKStatus(t, response)
|
||||||
|
|
||||||
|
assert.Len(t, groupSyncables, 10)
|
||||||
|
|
||||||
|
th.SystemAdminClient.Logout()
|
||||||
|
_, response = th.SystemAdminClient.GetGroupSyncables(g.Id, model.GroupSyncableTypeChannel, "")
|
||||||
|
CheckUnauthorizedStatus(t, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPatchGroupTeam(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
id := model.NewId()
|
||||||
|
g, err := th.App.CreateGroup(&model.Group{
|
||||||
|
DisplayName: "dn_" + id,
|
||||||
|
Name: "name" + id,
|
||||||
|
Source: model.GroupSourceLdap,
|
||||||
|
Description: "description_" + id,
|
||||||
|
RemoteId: model.NewId(),
|
||||||
|
})
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
patch := &model.GroupSyncablePatch{
|
||||||
|
CanLeave: model.NewBool(true),
|
||||||
|
AutoAdd: model.NewBool(true),
|
||||||
|
}
|
||||||
|
|
||||||
|
th.App.SetLicense(model.NewTestLicense("ldap"))
|
||||||
|
|
||||||
|
groupSyncable, response := th.SystemAdminClient.LinkGroupSyncable(g.Id, th.BasicTeam.Id, model.GroupSyncableTypeTeam, patch)
|
||||||
|
assert.Equal(t, http.StatusCreated, response.StatusCode)
|
||||||
|
assert.NotNil(t, groupSyncable)
|
||||||
|
// assert.True(t, groupSyncable.CanLeave) // TODO: Re-add this test in phase 2 of LDAP groups sync.
|
||||||
|
assert.True(t, groupSyncable.AutoAdd)
|
||||||
|
|
||||||
|
_, response = th.Client.PatchGroupSyncable(g.Id, th.BasicTeam.Id, model.GroupSyncableTypeTeam, patch)
|
||||||
|
assert.Equal(t, http.StatusForbidden, response.StatusCode)
|
||||||
|
|
||||||
|
th.App.SetLicense(nil)
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.PatchGroupSyncable(g.Id, th.BasicTeam.Id, model.GroupSyncableTypeTeam, patch)
|
||||||
|
CheckNotImplementedStatus(t, response)
|
||||||
|
|
||||||
|
th.App.SetLicense(model.NewTestLicense("ldap"))
|
||||||
|
|
||||||
|
patch.AutoAdd = model.NewBool(false)
|
||||||
|
groupSyncable, response = th.SystemAdminClient.PatchGroupSyncable(g.Id, th.BasicTeam.Id, model.GroupSyncableTypeTeam, patch)
|
||||||
|
CheckOKStatus(t, response)
|
||||||
|
assert.False(t, groupSyncable.AutoAdd)
|
||||||
|
|
||||||
|
assert.Equal(t, g.Id, groupSyncable.GroupId)
|
||||||
|
assert.Equal(t, th.BasicTeam.Id, groupSyncable.SyncableId)
|
||||||
|
assert.Equal(t, model.GroupSyncableTypeTeam, groupSyncable.Type)
|
||||||
|
|
||||||
|
// TODO: Re-add this test in phase 2 of LDAP groups sync.
|
||||||
|
// patch.CanLeave = model.NewBool(false)
|
||||||
|
// _, response = th.SystemAdminClient.PatchGroupSyncable(g.Id, th.BasicTeam.Id, model.GroupSyncableTypeTeam, patch)
|
||||||
|
// CheckBadRequestStatus(t, response)
|
||||||
|
|
||||||
|
patch.AutoAdd = model.NewBool(true)
|
||||||
|
groupSyncable, response = th.SystemAdminClient.PatchGroupSyncable(g.Id, th.BasicTeam.Id, model.GroupSyncableTypeTeam, patch)
|
||||||
|
CheckOKStatus(t, response)
|
||||||
|
assert.False(t, groupSyncable.CanLeave)
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.PatchGroupSyncable(model.NewId(), th.BasicTeam.Id, model.GroupSyncableTypeTeam, patch)
|
||||||
|
CheckNotFoundStatus(t, response)
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.PatchGroupSyncable(g.Id, model.NewId(), model.GroupSyncableTypeTeam, patch)
|
||||||
|
CheckNotFoundStatus(t, response)
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.PatchGroupSyncable("abc", th.BasicTeam.Id, model.GroupSyncableTypeTeam, patch)
|
||||||
|
CheckBadRequestStatus(t, response)
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.PatchGroupSyncable(g.Id, "abc", model.GroupSyncableTypeTeam, patch)
|
||||||
|
CheckBadRequestStatus(t, response)
|
||||||
|
|
||||||
|
th.SystemAdminClient.Logout()
|
||||||
|
_, response = th.SystemAdminClient.PatchGroupSyncable(g.Id, th.BasicTeam.Id, model.GroupSyncableTypeTeam, patch)
|
||||||
|
CheckUnauthorizedStatus(t, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPatchGroupChannel(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
id := model.NewId()
|
||||||
|
g, err := th.App.CreateGroup(&model.Group{
|
||||||
|
DisplayName: "dn_" + id,
|
||||||
|
Name: "name" + id,
|
||||||
|
Source: model.GroupSourceLdap,
|
||||||
|
Description: "description_" + id,
|
||||||
|
RemoteId: model.NewId(),
|
||||||
|
})
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
patch := &model.GroupSyncablePatch{
|
||||||
|
CanLeave: model.NewBool(true),
|
||||||
|
AutoAdd: model.NewBool(true),
|
||||||
|
}
|
||||||
|
|
||||||
|
th.App.SetLicense(model.NewTestLicense("ldap"))
|
||||||
|
|
||||||
|
groupSyncable, response := th.SystemAdminClient.LinkGroupSyncable(g.Id, th.BasicChannel.Id, model.GroupSyncableTypeChannel, patch)
|
||||||
|
assert.Equal(t, http.StatusCreated, response.StatusCode)
|
||||||
|
assert.NotNil(t, groupSyncable)
|
||||||
|
// assert.True(t, groupSyncable.CanLeave) // TODO: Re-add this test in phase 2 of LDAP groups sync.
|
||||||
|
assert.True(t, groupSyncable.AutoAdd)
|
||||||
|
|
||||||
|
_, response = th.Client.PatchGroupSyncable(g.Id, th.BasicChannel.Id, model.GroupSyncableTypeChannel, patch)
|
||||||
|
assert.Equal(t, http.StatusForbidden, response.StatusCode)
|
||||||
|
|
||||||
|
th.App.SetLicense(nil)
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.PatchGroupSyncable(g.Id, th.BasicChannel.Id, model.GroupSyncableTypeChannel, patch)
|
||||||
|
CheckNotImplementedStatus(t, response)
|
||||||
|
|
||||||
|
th.App.SetLicense(model.NewTestLicense("ldap"))
|
||||||
|
|
||||||
|
patch.AutoAdd = model.NewBool(false)
|
||||||
|
groupSyncable, response = th.SystemAdminClient.PatchGroupSyncable(g.Id, th.BasicChannel.Id, model.GroupSyncableTypeChannel, patch)
|
||||||
|
CheckOKStatus(t, response)
|
||||||
|
assert.False(t, groupSyncable.AutoAdd)
|
||||||
|
|
||||||
|
assert.Equal(t, g.Id, groupSyncable.GroupId)
|
||||||
|
assert.Equal(t, th.BasicChannel.Id, groupSyncable.SyncableId)
|
||||||
|
assert.Equal(t, model.GroupSyncableTypeChannel, groupSyncable.Type)
|
||||||
|
|
||||||
|
// TODO: Re-add this test in phase 2 of LDAP groups sync.
|
||||||
|
// patch.CanLeave = model.NewBool(false)
|
||||||
|
// _, response = th.SystemAdminClient.PatchGroupSyncable(g.Id, th.BasicChannel.Id, model.GroupSyncableTypeChannel, patch)
|
||||||
|
// CheckBadRequestStatus(t, response)
|
||||||
|
|
||||||
|
patch.AutoAdd = model.NewBool(true)
|
||||||
|
groupSyncable, response = th.SystemAdminClient.PatchGroupSyncable(g.Id, th.BasicChannel.Id, model.GroupSyncableTypeChannel, patch)
|
||||||
|
CheckOKStatus(t, response)
|
||||||
|
assert.False(t, groupSyncable.CanLeave)
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.PatchGroupSyncable(model.NewId(), th.BasicChannel.Id, model.GroupSyncableTypeChannel, patch)
|
||||||
|
CheckNotFoundStatus(t, response)
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.PatchGroupSyncable(g.Id, model.NewId(), model.GroupSyncableTypeChannel, patch)
|
||||||
|
CheckNotFoundStatus(t, response)
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.PatchGroupSyncable("abc", th.BasicChannel.Id, model.GroupSyncableTypeChannel, patch)
|
||||||
|
CheckBadRequestStatus(t, response)
|
||||||
|
|
||||||
|
_, response = th.SystemAdminClient.PatchGroupSyncable(g.Id, "abc", model.GroupSyncableTypeChannel, patch)
|
||||||
|
CheckBadRequestStatus(t, response)
|
||||||
|
|
||||||
|
th.SystemAdminClient.Logout()
|
||||||
|
_, response = th.SystemAdminClient.PatchGroupSyncable(g.Id, th.BasicChannel.Id, model.GroupSyncableTypeChannel, patch)
|
||||||
|
CheckUnauthorizedStatus(t, response)
|
||||||
|
}
|
||||||
174
api4/ldap.go
174
api4/ldap.go
@@ -4,14 +4,32 @@
|
|||||||
package api4
|
package api4
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/model"
|
"github.com/mattermost/mattermost-server/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type mixedUnlinkedGroup struct {
|
||||||
|
Id *string `json:"mattermost_group_id"`
|
||||||
|
DisplayName string `json:"name"`
|
||||||
|
RemoteId string `json:"primary_key"`
|
||||||
|
HasSyncables *bool `json:"has_syncables"`
|
||||||
|
}
|
||||||
|
|
||||||
func (api *API) InitLdap() {
|
func (api *API) InitLdap() {
|
||||||
api.BaseRoutes.LDAP.Handle("/sync", api.ApiSessionRequired(syncLdap)).Methods("POST")
|
api.BaseRoutes.LDAP.Handle("/sync", api.ApiSessionRequired(syncLdap)).Methods("POST")
|
||||||
api.BaseRoutes.LDAP.Handle("/test", api.ApiSessionRequired(testLdap)).Methods("POST")
|
api.BaseRoutes.LDAP.Handle("/test", api.ApiSessionRequired(testLdap)).Methods("POST")
|
||||||
|
|
||||||
|
// GET /api/v4/ldap/groups?page=0&per_page=1000
|
||||||
|
api.BaseRoutes.LDAP.Handle("/groups", api.ApiSessionRequired(getLdapGroups)).Methods("GET")
|
||||||
|
|
||||||
|
// POST /api/v4/ldap/groups/:remote_id/link
|
||||||
|
api.BaseRoutes.LDAP.Handle(`/groups/{remote_id}/link`, api.ApiSessionRequired(linkLdapGroup)).Methods("POST")
|
||||||
|
|
||||||
|
// DELETE /api/v4/ldap/groups/:remote_id/link
|
||||||
|
api.BaseRoutes.LDAP.Handle(`/groups/{remote_id}/link`, api.ApiSessionRequired(unlinkLdapGroup)).Methods("DELETE")
|
||||||
}
|
}
|
||||||
|
|
||||||
func syncLdap(c *Context, w http.ResponseWriter, r *http.Request) {
|
func syncLdap(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -38,3 +56,159 @@ func testLdap(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
ReturnStatusOK(w)
|
ReturnStatusOK(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getLdapGroups(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
|
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.App.License() == nil || !*c.App.License().Features.LDAPGroups {
|
||||||
|
c.Err = model.NewAppError("Api4.getLdapGroups", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
groups, total, err := c.App.GetAllLdapGroupsPage(c.Params.Page, c.Params.PerPage)
|
||||||
|
if err != nil {
|
||||||
|
c.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mugs := []*mixedUnlinkedGroup{}
|
||||||
|
for _, group := range groups {
|
||||||
|
mug := &mixedUnlinkedGroup{
|
||||||
|
DisplayName: group.DisplayName,
|
||||||
|
RemoteId: group.RemoteId,
|
||||||
|
}
|
||||||
|
if len(group.Id) == 26 {
|
||||||
|
mug.Id = &group.Id
|
||||||
|
mug.HasSyncables = &group.HasSyncables
|
||||||
|
}
|
||||||
|
mugs = append(mugs, mug)
|
||||||
|
}
|
||||||
|
|
||||||
|
b, marshalErr := json.Marshal(struct {
|
||||||
|
Count int `json:"count"`
|
||||||
|
Groups []*mixedUnlinkedGroup `json:"groups"`
|
||||||
|
}{Count: total, Groups: mugs})
|
||||||
|
if marshalErr != nil {
|
||||||
|
c.Err = model.NewAppError("Api4.getLdapGroups", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func linkLdapGroup(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
c.RequireRemoteId()
|
||||||
|
if c.Err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
|
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.App.License() == nil || !*c.App.License().Features.LDAPGroups {
|
||||||
|
c.Err = model.NewAppError("Api4.linkLdapGroup", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ldapGroup, err := c.App.GetLdapGroup(c.Params.RemoteId)
|
||||||
|
if err != nil {
|
||||||
|
c.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ldapGroup == nil {
|
||||||
|
c.Err = model.NewAppError("Api4.linkLdapGroup", "api.ldap_group.not_found", nil, "", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
group, err := c.App.GetGroupByRemoteID(ldapGroup.RemoteId, model.GroupSourceLdap)
|
||||||
|
if err != nil && err.DetailedError != sql.ErrNoRows.Error() {
|
||||||
|
c.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var status int
|
||||||
|
var newOrUpdatedGroup *model.Group
|
||||||
|
|
||||||
|
// Group has been previously linked
|
||||||
|
if group != nil {
|
||||||
|
if group.DeleteAt == 0 {
|
||||||
|
newOrUpdatedGroup = group
|
||||||
|
} else {
|
||||||
|
group.DeleteAt = 0
|
||||||
|
group.DisplayName = ldapGroup.DisplayName
|
||||||
|
group.RemoteId = ldapGroup.RemoteId
|
||||||
|
newOrUpdatedGroup, err = c.App.UpdateGroup(group)
|
||||||
|
if err != nil {
|
||||||
|
c.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
status = http.StatusOK
|
||||||
|
} else {
|
||||||
|
// Group has never been linked
|
||||||
|
//
|
||||||
|
// TODO: In a future phase of LDAP groups sync `Name` will be used for at-mentions and will be editable on
|
||||||
|
// the front-end so it will not have an initial value of `model.NewId()` but rather a slugified version of
|
||||||
|
// the LDAP group name with an appended duplicate-breaker.
|
||||||
|
newGroup := &model.Group{
|
||||||
|
Name: model.NewId(),
|
||||||
|
DisplayName: ldapGroup.DisplayName,
|
||||||
|
RemoteId: ldapGroup.RemoteId,
|
||||||
|
Source: model.GroupSourceLdap,
|
||||||
|
}
|
||||||
|
newOrUpdatedGroup, err = c.App.CreateGroup(newGroup)
|
||||||
|
if err != nil {
|
||||||
|
c.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
status = http.StatusCreated
|
||||||
|
}
|
||||||
|
|
||||||
|
b, marshalErr := json.Marshal(newOrUpdatedGroup)
|
||||||
|
if marshalErr != nil {
|
||||||
|
c.Err = model.NewAppError("Api4.linkLdapGroup", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(status)
|
||||||
|
w.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unlinkLdapGroup(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
c.RequireRemoteId()
|
||||||
|
if c.Err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
|
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.App.License() == nil || !*c.App.License().Features.LDAPGroups {
|
||||||
|
c.Err = model.NewAppError("Api4.unlinkLdapGroup", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
group, err := c.App.GetGroupByRemoteID(c.Params.RemoteId, model.GroupSourceLdap)
|
||||||
|
if err != nil {
|
||||||
|
c.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if group.DeleteAt == 0 {
|
||||||
|
_, err = c.App.DeleteGroup(group.Id)
|
||||||
|
if err != nil {
|
||||||
|
c.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnStatusOK(w)
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLdapTest(t *testing.T) {
|
func TestTestLdap(t *testing.T) {
|
||||||
th := Setup().InitBasic()
|
th := Setup().InitBasic()
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ func TestLdapTest(t *testing.T) {
|
|||||||
CheckNotImplementedStatus(t, resp)
|
CheckNotImplementedStatus(t, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLdapSync(t *testing.T) {
|
func TestSyncLdap(t *testing.T) {
|
||||||
th := Setup().InitBasic()
|
th := Setup().InitBasic()
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
|
|
||||||
@@ -28,3 +28,40 @@ func TestLdapSync(t *testing.T) {
|
|||||||
_, resp = th.Client.SyncLdap()
|
_, resp = th.Client.SyncLdap()
|
||||||
CheckForbiddenStatus(t, resp)
|
CheckForbiddenStatus(t, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetLdapGroups(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
_, resp := th.Client.GetLdapGroups()
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
|
||||||
|
_, resp = th.SystemAdminClient.GetLdapGroups()
|
||||||
|
CheckNotImplementedStatus(t, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLinkLdapGroup(t *testing.T) {
|
||||||
|
const entryUUID string = "foo"
|
||||||
|
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
_, resp := th.Client.LinkLdapGroup(entryUUID)
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
|
||||||
|
_, resp = th.SystemAdminClient.LinkLdapGroup(entryUUID)
|
||||||
|
CheckNotImplementedStatus(t, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnlinkLdapGroup(t *testing.T) {
|
||||||
|
const entryUUID string = "foo"
|
||||||
|
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
_, resp := th.Client.UnlinkLdapGroup(entryUUID)
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
|
||||||
|
_, resp = th.SystemAdminClient.UnlinkLdapGroup(entryUUID)
|
||||||
|
CheckNotImplementedStatus(t, resp)
|
||||||
|
}
|
||||||
|
|||||||
@@ -67,10 +67,6 @@ func getScheme(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getSchemes(c *Context, w http.ResponseWriter, r *http.Request) {
|
func getSchemes(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
if c.Err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -2203,7 +2203,6 @@ func TestUpdateTeamScheme(t *testing.T) {
|
|||||||
|
|
||||||
// Test an invalid scheme scope.
|
// Test an invalid scheme scope.
|
||||||
_, resp = th.SystemAdminClient.UpdateTeamScheme(team.Id, channelScheme.Id)
|
_, resp = th.SystemAdminClient.UpdateTeamScheme(team.Id, channelScheme.Id)
|
||||||
fmt.Printf("resp: %+v\n", resp)
|
|
||||||
CheckBadRequestStatus(t, resp)
|
CheckBadRequestStatus(t, resp)
|
||||||
|
|
||||||
// Test that an unauthenticated user gets rejected.
|
// Test that an unauthenticated user gets rejected.
|
||||||
|
|||||||
@@ -1116,6 +1116,14 @@ func (a *App) GetChannelsForUser(teamId string, userId string, includeDeleted bo
|
|||||||
return result.Data.(*model.ChannelList), nil
|
return result.Data.(*model.ChannelList), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) GetAllChannels(page, perPage int, includeDeleted bool) (*model.ChannelListWithTeamData, *model.AppError) {
|
||||||
|
result := <-a.Srv.Store.Channel().GetAllChannels(page*perPage, perPage, includeDeleted)
|
||||||
|
if result.Err != nil {
|
||||||
|
return nil, result.Err
|
||||||
|
}
|
||||||
|
return result.Data.(*model.ChannelListWithTeamData), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (a *App) GetDeletedChannels(teamId string, offset int, limit int) (*model.ChannelList, *model.AppError) {
|
func (a *App) GetDeletedChannels(teamId string, offset int, limit int) (*model.ChannelList, *model.AppError) {
|
||||||
result := <-a.Srv.Store.Channel().GetDeleted(teamId, offset, limit)
|
result := <-a.Srv.Store.Channel().GetDeleted(teamId, offset, limit)
|
||||||
if result.Err != nil {
|
if result.Err != nil {
|
||||||
@@ -1577,6 +1585,14 @@ func (a *App) AutocompleteChannelsForSearch(teamId string, userId string, term s
|
|||||||
return result.Data.(*model.ChannelList), nil
|
return result.Data.(*model.ChannelList), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) SearchAllChannels(term string, includeDeleted bool) (*model.ChannelListWithTeamData, *model.AppError) {
|
||||||
|
result := <-a.Srv.Store.Channel().SearchAllChannels(term, *a.Config().TeamSettings.ExperimentalViewArchivedChannels && includeDeleted)
|
||||||
|
if result.Err != nil {
|
||||||
|
return nil, result.Err
|
||||||
|
}
|
||||||
|
return result.Data.(*model.ChannelListWithTeamData), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (a *App) SearchChannels(teamId string, term string) (*model.ChannelList, *model.AppError) {
|
func (a *App) SearchChannels(teamId string, term string) (*model.ChannelList, *model.AppError) {
|
||||||
includeDeleted := *a.Config().TeamSettings.ExperimentalViewArchivedChannels
|
includeDeleted := *a.Config().TeamSettings.ExperimentalViewArchivedChannels
|
||||||
|
|
||||||
|
|||||||
@@ -264,6 +264,7 @@ func (a *App) trackConfig() {
|
|||||||
"experimental_enable_hardened_mode": *cfg.ServiceSettings.ExperimentalEnableHardenedMode,
|
"experimental_enable_hardened_mode": *cfg.ServiceSettings.ExperimentalEnableHardenedMode,
|
||||||
"enable_email_invitations": *cfg.ServiceSettings.EnableEmailInvitations,
|
"enable_email_invitations": *cfg.ServiceSettings.EnableEmailInvitations,
|
||||||
"experimental_channel_organization": *cfg.ServiceSettings.ExperimentalChannelOrganization,
|
"experimental_channel_organization": *cfg.ServiceSettings.ExperimentalChannelOrganization,
|
||||||
|
"experimental_ldap_group_sync": *cfg.ServiceSettings.ExperimentalLdapGroupSync,
|
||||||
})
|
})
|
||||||
|
|
||||||
a.SendDiagnostic(TRACK_CONFIG_TEAM, map[string]interface{}{
|
a.SendDiagnostic(TRACK_CONFIG_TEAM, map[string]interface{}{
|
||||||
@@ -418,25 +419,28 @@ func (a *App) trackConfig() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
a.SendDiagnostic(TRACK_CONFIG_LDAP, map[string]interface{}{
|
a.SendDiagnostic(TRACK_CONFIG_LDAP, map[string]interface{}{
|
||||||
"enable": *cfg.LdapSettings.Enable,
|
"enable": *cfg.LdapSettings.Enable,
|
||||||
"enable_sync": *cfg.LdapSettings.EnableSync,
|
"enable_sync": *cfg.LdapSettings.EnableSync,
|
||||||
"connection_security": *cfg.LdapSettings.ConnectionSecurity,
|
"connection_security": *cfg.LdapSettings.ConnectionSecurity,
|
||||||
"skip_certificate_verification": *cfg.LdapSettings.SkipCertificateVerification,
|
"skip_certificate_verification": *cfg.LdapSettings.SkipCertificateVerification,
|
||||||
"sync_interval_minutes": *cfg.LdapSettings.SyncIntervalMinutes,
|
"sync_interval_minutes": *cfg.LdapSettings.SyncIntervalMinutes,
|
||||||
"query_timeout": *cfg.LdapSettings.QueryTimeout,
|
"query_timeout": *cfg.LdapSettings.QueryTimeout,
|
||||||
"max_page_size": *cfg.LdapSettings.MaxPageSize,
|
"max_page_size": *cfg.LdapSettings.MaxPageSize,
|
||||||
"isdefault_first_name_attribute": isDefault(*cfg.LdapSettings.FirstNameAttribute, model.LDAP_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE),
|
"isdefault_first_name_attribute": isDefault(*cfg.LdapSettings.FirstNameAttribute, model.LDAP_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE),
|
||||||
"isdefault_last_name_attribute": isDefault(*cfg.LdapSettings.LastNameAttribute, model.LDAP_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE),
|
"isdefault_last_name_attribute": isDefault(*cfg.LdapSettings.LastNameAttribute, model.LDAP_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE),
|
||||||
"isdefault_email_attribute": isDefault(*cfg.LdapSettings.EmailAttribute, model.LDAP_SETTINGS_DEFAULT_EMAIL_ATTRIBUTE),
|
"isdefault_email_attribute": isDefault(*cfg.LdapSettings.EmailAttribute, model.LDAP_SETTINGS_DEFAULT_EMAIL_ATTRIBUTE),
|
||||||
"isdefault_username_attribute": isDefault(*cfg.LdapSettings.UsernameAttribute, model.LDAP_SETTINGS_DEFAULT_USERNAME_ATTRIBUTE),
|
"isdefault_username_attribute": isDefault(*cfg.LdapSettings.UsernameAttribute, model.LDAP_SETTINGS_DEFAULT_USERNAME_ATTRIBUTE),
|
||||||
"isdefault_nickname_attribute": isDefault(*cfg.LdapSettings.NicknameAttribute, model.LDAP_SETTINGS_DEFAULT_NICKNAME_ATTRIBUTE),
|
"isdefault_nickname_attribute": isDefault(*cfg.LdapSettings.NicknameAttribute, model.LDAP_SETTINGS_DEFAULT_NICKNAME_ATTRIBUTE),
|
||||||
"isdefault_id_attribute": isDefault(*cfg.LdapSettings.IdAttribute, model.LDAP_SETTINGS_DEFAULT_ID_ATTRIBUTE),
|
"isdefault_id_attribute": isDefault(*cfg.LdapSettings.IdAttribute, model.LDAP_SETTINGS_DEFAULT_ID_ATTRIBUTE),
|
||||||
"isdefault_position_attribute": isDefault(*cfg.LdapSettings.PositionAttribute, model.LDAP_SETTINGS_DEFAULT_POSITION_ATTRIBUTE),
|
"isdefault_position_attribute": isDefault(*cfg.LdapSettings.PositionAttribute, model.LDAP_SETTINGS_DEFAULT_POSITION_ATTRIBUTE),
|
||||||
"isdefault_login_id_attribute": isDefault(*cfg.LdapSettings.LoginIdAttribute, ""),
|
"isdefault_login_id_attribute": isDefault(*cfg.LdapSettings.LoginIdAttribute, ""),
|
||||||
"isdefault_login_field_name": isDefault(*cfg.LdapSettings.LoginFieldName, model.LDAP_SETTINGS_DEFAULT_LOGIN_FIELD_NAME),
|
"isdefault_login_field_name": isDefault(*cfg.LdapSettings.LoginFieldName, model.LDAP_SETTINGS_DEFAULT_LOGIN_FIELD_NAME),
|
||||||
"isdefault_login_button_color": isDefault(*cfg.LdapSettings.LoginButtonColor, ""),
|
"isdefault_login_button_color": isDefault(*cfg.LdapSettings.LoginButtonColor, ""),
|
||||||
"isdefault_login_button_border_color": isDefault(*cfg.LdapSettings.LoginButtonBorderColor, ""),
|
"isdefault_login_button_border_color": isDefault(*cfg.LdapSettings.LoginButtonBorderColor, ""),
|
||||||
"isdefault_login_button_text_color": isDefault(*cfg.LdapSettings.LoginButtonTextColor, ""),
|
"isdefault_login_button_text_color": isDefault(*cfg.LdapSettings.LoginButtonTextColor, ""),
|
||||||
|
"isempty_group_filter": isDefault(*cfg.LdapSettings.GroupFilter, ""),
|
||||||
|
"isdefault_group_display_name_attribute": isDefault(*cfg.LdapSettings.GroupDisplayNameAttribute, model.LDAP_SETTINGS_DEFAULT_GROUP_DISPLAY_NAME_ATTRIBUTE),
|
||||||
|
"isdefault_group_id_attribute": isDefault(*cfg.LdapSettings.GroupIdAttribute, model.LDAP_SETTINGS_DEFAULT_GROUP_ID_ATTRIBUTE),
|
||||||
})
|
})
|
||||||
|
|
||||||
a.SendDiagnostic(TRACK_CONFIG_COMPLIANCE, map[string]interface{}{
|
a.SendDiagnostic(TRACK_CONFIG_COMPLIANCE, map[string]interface{}{
|
||||||
|
|||||||
150
app/group.go
Обычный файл
150
app/group.go
Обычный файл
@@ -0,0 +1,150 @@
|
|||||||
|
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mattermost/mattermost-server/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *App) GetGroup(id string) (*model.Group, *model.AppError) {
|
||||||
|
result := <-a.Srv.Store.Group().Get(id)
|
||||||
|
if result.Err != nil {
|
||||||
|
return nil, result.Err
|
||||||
|
}
|
||||||
|
return result.Data.(*model.Group), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) GetGroupByRemoteID(remoteID string, groupSource model.GroupSource) (*model.Group, *model.AppError) {
|
||||||
|
result := <-a.Srv.Store.Group().GetByRemoteID(remoteID, groupSource)
|
||||||
|
if result.Err != nil {
|
||||||
|
return nil, result.Err
|
||||||
|
}
|
||||||
|
return result.Data.(*model.Group), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) GetGroupsBySource(groupSource model.GroupSource) ([]*model.Group, *model.AppError) {
|
||||||
|
result := <-a.Srv.Store.Group().GetAllBySource(groupSource)
|
||||||
|
if result.Err != nil {
|
||||||
|
return nil, result.Err
|
||||||
|
}
|
||||||
|
return result.Data.([]*model.Group), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) CreateGroup(group *model.Group) (*model.Group, *model.AppError) {
|
||||||
|
result := <-a.Srv.Store.Group().Create(group)
|
||||||
|
if result.Err != nil {
|
||||||
|
return nil, result.Err
|
||||||
|
}
|
||||||
|
return result.Data.(*model.Group), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) UpdateGroup(group *model.Group) (*model.Group, *model.AppError) {
|
||||||
|
result := <-a.Srv.Store.Group().Update(group)
|
||||||
|
if result.Err != nil {
|
||||||
|
return nil, result.Err
|
||||||
|
}
|
||||||
|
return result.Data.(*model.Group), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) DeleteGroup(groupID string) (*model.Group, *model.AppError) {
|
||||||
|
result := <-a.Srv.Store.Group().Delete(groupID)
|
||||||
|
if result.Err != nil {
|
||||||
|
return nil, result.Err
|
||||||
|
}
|
||||||
|
return result.Data.(*model.Group), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) GetGroupMemberUsers(groupID string) ([]*model.User, *model.AppError) {
|
||||||
|
result := <-a.Srv.Store.Group().GetMemberUsers(groupID)
|
||||||
|
if result.Err != nil {
|
||||||
|
return nil, result.Err
|
||||||
|
}
|
||||||
|
return result.Data.([]*model.User), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) GetGroupMemberUsersPage(groupID string, page int, perPage int) ([]*model.User, int, *model.AppError) {
|
||||||
|
result := <-a.Srv.Store.Group().GetMemberUsersPage(groupID, page, perPage)
|
||||||
|
if result.Err != nil {
|
||||||
|
return nil, 0, result.Err
|
||||||
|
}
|
||||||
|
members := result.Data.([]*model.User)
|
||||||
|
result = <-a.Srv.Store.Group().GetMemberCount(groupID)
|
||||||
|
if result.Err != nil {
|
||||||
|
return nil, 0, result.Err
|
||||||
|
}
|
||||||
|
count := int(result.Data.(int64))
|
||||||
|
return members, count, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) CreateOrRestoreGroupMember(groupID string, userID string) (*model.GroupMember, *model.AppError) {
|
||||||
|
result := <-a.Srv.Store.Group().CreateOrRestoreMember(groupID, userID)
|
||||||
|
if result.Err != nil {
|
||||||
|
return nil, result.Err
|
||||||
|
}
|
||||||
|
return result.Data.(*model.GroupMember), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) DeleteGroupMember(groupID string, userID string) (*model.GroupMember, *model.AppError) {
|
||||||
|
result := <-a.Srv.Store.Group().DeleteMember(groupID, userID)
|
||||||
|
if result.Err != nil {
|
||||||
|
return nil, result.Err
|
||||||
|
}
|
||||||
|
return result.Data.(*model.GroupMember), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) CreateGroupSyncable(groupSyncable *model.GroupSyncable) (*model.GroupSyncable, *model.AppError) {
|
||||||
|
result := <-a.Srv.Store.Group().CreateGroupSyncable(groupSyncable)
|
||||||
|
if result.Err != nil {
|
||||||
|
return nil, result.Err
|
||||||
|
}
|
||||||
|
return result.Data.(*model.GroupSyncable), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) GetGroupSyncable(groupID string, syncableID string, syncableType model.GroupSyncableType) (*model.GroupSyncable, *model.AppError) {
|
||||||
|
result := <-a.Srv.Store.Group().GetGroupSyncable(groupID, syncableID, syncableType)
|
||||||
|
if result.Err != nil {
|
||||||
|
return nil, result.Err
|
||||||
|
}
|
||||||
|
return result.Data.(*model.GroupSyncable), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) GetGroupSyncables(groupID string, syncableType model.GroupSyncableType) ([]*model.GroupSyncable, *model.AppError) {
|
||||||
|
result := <-a.Srv.Store.Group().GetAllGroupSyncablesByGroupId(groupID, syncableType)
|
||||||
|
if result.Err != nil {
|
||||||
|
return nil, result.Err
|
||||||
|
}
|
||||||
|
return result.Data.([]*model.GroupSyncable), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) UpdateGroupSyncable(groupSyncable *model.GroupSyncable) (*model.GroupSyncable, *model.AppError) {
|
||||||
|
result := <-a.Srv.Store.Group().UpdateGroupSyncable(groupSyncable)
|
||||||
|
if result.Err != nil {
|
||||||
|
return nil, result.Err
|
||||||
|
}
|
||||||
|
return result.Data.(*model.GroupSyncable), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) DeleteGroupSyncable(groupID string, syncableID string, syncableType model.GroupSyncableType) (*model.GroupSyncable, *model.AppError) {
|
||||||
|
result := <-a.Srv.Store.Group().DeleteGroupSyncable(groupID, syncableID, syncableType)
|
||||||
|
if result.Err != nil {
|
||||||
|
return nil, result.Err
|
||||||
|
}
|
||||||
|
return result.Data.(*model.GroupSyncable), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) PendingAutoAddTeamMembers(minGroupMembersCreateAt int64) ([]*model.UserTeamIDPair, *model.AppError) {
|
||||||
|
result := <-a.Srv.Store.Group().PendingAutoAddTeamMembers(minGroupMembersCreateAt)
|
||||||
|
if result.Err != nil {
|
||||||
|
return nil, result.Err
|
||||||
|
}
|
||||||
|
return result.Data.([]*model.UserTeamIDPair), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) PendingAutoAddChannelMembers(minGroupMembersCreateAt int64) ([]*model.UserChannelIDPair, *model.AppError) {
|
||||||
|
result := <-a.Srv.Store.Group().PendingAutoAddChannelMembers(minGroupMembersCreateAt)
|
||||||
|
if result.Err != nil {
|
||||||
|
return nil, result.Err
|
||||||
|
}
|
||||||
|
return result.Data.([]*model.UserChannelIDPair), nil
|
||||||
|
}
|
||||||
223
app/group_test.go
Обычный файл
223
app/group_test.go
Обычный файл
@@ -0,0 +1,223 @@
|
|||||||
|
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mattermost/mattermost-server/model"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetGroup(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
group := th.CreateGroup()
|
||||||
|
|
||||||
|
group, err := th.App.GetGroup(group.Id)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.NotNil(t, group)
|
||||||
|
|
||||||
|
group, err = th.App.GetGroup(model.NewId())
|
||||||
|
require.NotNil(t, err)
|
||||||
|
require.Nil(t, group)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetGroupByRemoteID(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
group := th.CreateGroup()
|
||||||
|
|
||||||
|
g, err := th.App.GetGroupByRemoteID(group.RemoteId, model.GroupSourceLdap)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.NotNil(t, g)
|
||||||
|
|
||||||
|
g, err = th.App.GetGroupByRemoteID(model.NewId(), model.GroupSourceLdap)
|
||||||
|
require.NotNil(t, err)
|
||||||
|
require.Nil(t, g)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetGroupsByType(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
th.CreateGroup()
|
||||||
|
th.CreateGroup()
|
||||||
|
th.CreateGroup()
|
||||||
|
|
||||||
|
groups, err := th.App.GetGroupsBySource(model.GroupSourceLdap)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.NotEmpty(t, groups)
|
||||||
|
|
||||||
|
groups, err = th.App.GetGroupsBySource(model.GroupSource("blah"))
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.Empty(t, groups)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateGroup(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
id := model.NewId()
|
||||||
|
group := &model.Group{
|
||||||
|
DisplayName: "dn_" + id,
|
||||||
|
Name: "name" + id,
|
||||||
|
Source: model.GroupSourceLdap,
|
||||||
|
RemoteId: model.NewId(),
|
||||||
|
}
|
||||||
|
|
||||||
|
g, err := th.App.CreateGroup(group)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.NotNil(t, g)
|
||||||
|
|
||||||
|
g, err = th.App.CreateGroup(group)
|
||||||
|
require.NotNil(t, err)
|
||||||
|
require.Nil(t, g)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateGroup(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
group := th.CreateGroup()
|
||||||
|
group.DisplayName = model.NewId()
|
||||||
|
|
||||||
|
g, err := th.App.UpdateGroup(group)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.NotNil(t, g)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeleteGroup(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
group := th.CreateGroup()
|
||||||
|
|
||||||
|
g, err := th.App.DeleteGroup(group.Id)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.NotNil(t, g)
|
||||||
|
|
||||||
|
g, err = th.App.DeleteGroup(group.Id)
|
||||||
|
require.NotNil(t, err)
|
||||||
|
require.Nil(t, g)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateOrRestoreGroupMember(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
group := th.CreateGroup()
|
||||||
|
|
||||||
|
g, err := th.App.CreateOrRestoreGroupMember(group.Id, th.BasicUser.Id)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.NotNil(t, g)
|
||||||
|
|
||||||
|
g, err = th.App.CreateOrRestoreGroupMember(group.Id, th.BasicUser.Id)
|
||||||
|
require.NotNil(t, err)
|
||||||
|
require.Nil(t, g)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeleteGroupMember(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
group := th.CreateGroup()
|
||||||
|
groupMember, err := th.App.CreateOrRestoreGroupMember(group.Id, th.BasicUser.Id)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.NotNil(t, groupMember)
|
||||||
|
|
||||||
|
groupMember, err = th.App.DeleteGroupMember(groupMember.GroupId, groupMember.UserId)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.NotNil(t, groupMember)
|
||||||
|
|
||||||
|
groupMember, err = th.App.DeleteGroupMember(groupMember.GroupId, groupMember.UserId)
|
||||||
|
require.NotNil(t, err)
|
||||||
|
require.Nil(t, groupMember)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateGroupSyncable(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
group := th.CreateGroup()
|
||||||
|
groupSyncable := &model.GroupSyncable{
|
||||||
|
GroupId: group.Id,
|
||||||
|
CanLeave: true,
|
||||||
|
AutoAdd: false,
|
||||||
|
SyncableId: th.BasicTeam.Id,
|
||||||
|
Type: model.GroupSyncableTypeTeam,
|
||||||
|
}
|
||||||
|
|
||||||
|
gs, err := th.App.CreateGroupSyncable(groupSyncable)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.NotNil(t, gs)
|
||||||
|
|
||||||
|
gs, err = th.App.CreateGroupSyncable(groupSyncable)
|
||||||
|
require.NotNil(t, err)
|
||||||
|
require.Nil(t, gs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetGroupSyncable(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
group := th.CreateGroup()
|
||||||
|
groupSyncable := &model.GroupSyncable{
|
||||||
|
GroupId: group.Id,
|
||||||
|
CanLeave: true,
|
||||||
|
AutoAdd: false,
|
||||||
|
SyncableId: th.BasicTeam.Id,
|
||||||
|
Type: model.GroupSyncableTypeTeam,
|
||||||
|
}
|
||||||
|
|
||||||
|
gs, err := th.App.CreateGroupSyncable(groupSyncable)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.NotNil(t, gs)
|
||||||
|
|
||||||
|
gs, err = th.App.GetGroupSyncable(group.Id, th.BasicTeam.Id, model.GroupSyncableTypeTeam)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.NotNil(t, gs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetGroupSyncables(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
group := th.CreateGroup()
|
||||||
|
|
||||||
|
// Create a group team
|
||||||
|
groupSyncable := &model.GroupSyncable{
|
||||||
|
GroupId: group.Id,
|
||||||
|
CanLeave: true,
|
||||||
|
AutoAdd: false,
|
||||||
|
SyncableId: th.BasicTeam.Id,
|
||||||
|
Type: model.GroupSyncableTypeTeam,
|
||||||
|
}
|
||||||
|
|
||||||
|
gs, err := th.App.CreateGroupSyncable(groupSyncable)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.NotNil(t, gs)
|
||||||
|
|
||||||
|
groupTeams, err := th.App.GetGroupSyncables(group.Id, model.GroupSyncableTypeTeam)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
require.NotEmpty(t, groupTeams)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeleteGroupSyncable(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
group := th.CreateGroup()
|
||||||
|
groupChannel := &model.GroupSyncable{
|
||||||
|
GroupId: group.Id,
|
||||||
|
CanLeave: true,
|
||||||
|
AutoAdd: false,
|
||||||
|
SyncableId: th.BasicChannel.Id,
|
||||||
|
Type: model.GroupSyncableTypeChannel,
|
||||||
|
}
|
||||||
|
|
||||||
|
gs, err := th.App.CreateGroupSyncable(groupChannel)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.NotNil(t, gs)
|
||||||
|
|
||||||
|
gs, err = th.App.DeleteGroupSyncable(group.Id, th.BasicChannel.Id, model.GroupSyncableTypeChannel)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.NotNil(t, gs)
|
||||||
|
|
||||||
|
gs, err = th.App.DeleteGroupSyncable(group.Id, th.BasicChannel.Id, model.GroupSyncableTypeChannel)
|
||||||
|
require.NotNil(t, err)
|
||||||
|
require.Nil(t, gs)
|
||||||
|
}
|
||||||
@@ -343,6 +343,28 @@ func (me *TestHelper) CreateScheme() (*model.Scheme, []*model.Role) {
|
|||||||
return scheme, roles
|
return scheme, roles
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (me *TestHelper) CreateGroup() *model.Group {
|
||||||
|
id := model.NewId()
|
||||||
|
group := &model.Group{
|
||||||
|
DisplayName: "dn_" + id,
|
||||||
|
Name: "name" + id,
|
||||||
|
Source: model.GroupSourceLdap,
|
||||||
|
Description: "description_" + id,
|
||||||
|
RemoteId: model.NewId(),
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.DisableDebugLogForTest()
|
||||||
|
var err *model.AppError
|
||||||
|
if group, err = me.App.CreateGroup(group); err != nil {
|
||||||
|
mlog.Error(err.Error())
|
||||||
|
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
utils.EnableDebugLogForTest()
|
||||||
|
return group
|
||||||
|
}
|
||||||
|
|
||||||
func (me *TestHelper) CreateEmoji() *model.Emoji {
|
func (me *TestHelper) CreateEmoji() *model.Emoji {
|
||||||
utils.DisableDebugLogForTest()
|
utils.DisableDebugLogForTest()
|
||||||
|
|
||||||
|
|||||||
40
app/ldap.go
40
app/ldap.go
@@ -40,6 +40,46 @@ func (a *App) TestLdap() *model.AppError {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetLdapGroup retrieves a single LDAP group by the given LDAP group id.
|
||||||
|
func (a *App) GetLdapGroup(ldapGroupID string) (*model.Group, *model.AppError) {
|
||||||
|
var group *model.Group
|
||||||
|
|
||||||
|
if a.Ldap != nil {
|
||||||
|
var err *model.AppError
|
||||||
|
group, err = a.Ldap.GetGroup(ldapGroupID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ae := model.NewAppError("GetLdapGroup", "ent.ldap.app_error", nil, "", http.StatusNotImplemented)
|
||||||
|
mlog.Error(fmt.Sprintf("%v", ae.Error()))
|
||||||
|
return nil, ae
|
||||||
|
}
|
||||||
|
|
||||||
|
return group, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllLdapGroupsPage retrieves all LDAP groups under the configured base DN using the default or configured group
|
||||||
|
// filter.
|
||||||
|
func (a *App) GetAllLdapGroupsPage(page int, perPage int) ([]*model.Group, int, *model.AppError) {
|
||||||
|
var groups []*model.Group
|
||||||
|
var total int
|
||||||
|
|
||||||
|
if a.Ldap != nil {
|
||||||
|
var err *model.AppError
|
||||||
|
groups, total, err = a.Ldap.GetAllGroupsPage(page, perPage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ae := model.NewAppError("GetAllLdapGroupsPage", "ent.ldap.app_error", nil, "", http.StatusNotImplemented)
|
||||||
|
mlog.Error(fmt.Sprintf("%v", ae.Error()))
|
||||||
|
return nil, 0, ae
|
||||||
|
}
|
||||||
|
|
||||||
|
return groups, total, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (a *App) SwitchEmailToLdap(email, password, code, ldapLoginId, ldapPassword string) (string, *model.AppError) {
|
func (a *App) SwitchEmailToLdap(email, password, code, ldapLoginId, ldapPassword string) (string, *model.AppError) {
|
||||||
if a.License() != nil && !*a.Config().ServiceSettings.ExperimentalEnableAuthenticationTransfer {
|
if a.License() != nil && !*a.Config().ServiceSettings.ExperimentalEnableAuthenticationTransfer {
|
||||||
return "", model.NewAppError("emailToLdap", "api.user.email_to_ldap.not_available.app_error", nil, "", http.StatusForbidden)
|
return "", model.NewAppError("emailToLdap", "api.user.email_to_ldap.not_available.app_error", nil, "", http.StatusForbidden)
|
||||||
|
|||||||
63
app/syncables.go
Обычный файл
63
app/syncables.go
Обычный файл
@@ -0,0 +1,63 @@
|
|||||||
|
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mattermost/mattermost-server/mlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PopulateSyncablesSince adds users to teams and channels based on their group memberships and how those groups are
|
||||||
|
// configured to sync with teams and channels for group members on or after the given timestamp.
|
||||||
|
func (a *App) PopulateSyncablesSince(groupMembersCreatedAfter int64) error {
|
||||||
|
userTeamIDs, appErr := a.PendingAutoAddTeamMembers(groupMembersCreatedAfter)
|
||||||
|
if appErr != nil {
|
||||||
|
return appErr
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, userTeam := range userTeamIDs {
|
||||||
|
_, err := a.AddTeamMember(userTeam.TeamID, userTeam.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
a.Log.Info("added teammember",
|
||||||
|
mlog.String("user_id", userTeam.UserID),
|
||||||
|
mlog.String("team_id", userTeam.TeamID),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
userChannelIDs, appErr := a.PendingAutoAddChannelMembers(groupMembersCreatedAfter)
|
||||||
|
if appErr != nil {
|
||||||
|
return appErr
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, userChannel := range userChannelIDs {
|
||||||
|
channel, err := a.GetChannel(userChannel.ChannelID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// First add user to team
|
||||||
|
_, err = a.AddTeamMember(channel.TeamId, userChannel.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.Log.Info("added teammember",
|
||||||
|
mlog.String("user_id", userChannel.UserID),
|
||||||
|
mlog.String("team_id", channel.TeamId),
|
||||||
|
)
|
||||||
|
|
||||||
|
_, err = a.AddChannelMember(userChannel.UserID, channel, "", "", false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
a.Log.Info("added channelmember",
|
||||||
|
mlog.String("user_id", userChannel.UserID),
|
||||||
|
mlog.String("channel_id", userChannel.ChannelID),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
346
app/syncables_test.go
Обычный файл
346
app/syncables_test.go
Обычный файл
@@ -0,0 +1,346 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mattermost/mattermost-server/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPopulateSyncablesSince(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
singersTeam, err := th.App.CreateTeam(&model.Team{
|
||||||
|
DisplayName: "Singers",
|
||||||
|
Name: model.NewId(),
|
||||||
|
Email: "singers@test.com",
|
||||||
|
Type: model.TEAM_OPEN,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test team not created: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
nerdsTeam, err := th.App.CreateTeam(&model.Team{
|
||||||
|
DisplayName: "Nerds",
|
||||||
|
Name: model.NewId(),
|
||||||
|
Email: "nerds@test.com",
|
||||||
|
Type: model.TEAM_INVITE,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test team not created: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
practiceChannel, err := th.App.CreateChannel(&model.Channel{
|
||||||
|
TeamId: singersTeam.Id,
|
||||||
|
DisplayName: "Practices",
|
||||||
|
Name: model.NewId(),
|
||||||
|
Type: model.CHANNEL_OPEN,
|
||||||
|
}, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test channel not created: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
experimentsChannel, err := th.App.CreateChannel(&model.Channel{
|
||||||
|
TeamId: singersTeam.Id,
|
||||||
|
DisplayName: "Experiments",
|
||||||
|
Name: model.NewId(),
|
||||||
|
Type: model.CHANNEL_PRIVATE,
|
||||||
|
}, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test channel not created: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
gleeGroup, err := th.App.CreateGroup(&model.Group{
|
||||||
|
Name: model.NewId(),
|
||||||
|
DisplayName: "Glee Club",
|
||||||
|
RemoteId: model.NewId(),
|
||||||
|
Source: model.GroupSourceLdap,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test group not created: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
scienceGroup, err := th.App.CreateGroup(&model.Group{
|
||||||
|
Name: model.NewId(),
|
||||||
|
DisplayName: "Science Club",
|
||||||
|
RemoteId: model.NewId(),
|
||||||
|
Source: model.GroupSourceLdap,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test group not created: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = th.App.CreateGroupSyncable(&model.GroupSyncable{
|
||||||
|
CanLeave: true,
|
||||||
|
AutoAdd: true,
|
||||||
|
GroupId: gleeGroup.Id,
|
||||||
|
SyncableId: practiceChannel.Id,
|
||||||
|
Type: model.GroupSyncableTypeChannel,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test groupchannel not created: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
scienceTeamGroupSyncable, err := th.App.CreateGroupSyncable(&model.GroupSyncable{
|
||||||
|
CanLeave: true,
|
||||||
|
AutoAdd: false,
|
||||||
|
GroupId: scienceGroup.Id,
|
||||||
|
SyncableId: nerdsTeam.Id,
|
||||||
|
Type: model.GroupSyncableTypeTeam,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test groupteam not created: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
scienceChannelGroupSyncable, err := th.App.CreateGroupSyncable(&model.GroupSyncable{
|
||||||
|
CanLeave: true,
|
||||||
|
AutoAdd: false,
|
||||||
|
GroupId: scienceGroup.Id,
|
||||||
|
SyncableId: experimentsChannel.Id,
|
||||||
|
Type: model.GroupSyncableTypeChannel,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test groupchannel not created: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
singer1 := th.BasicUser
|
||||||
|
scientist1 := th.BasicUser2
|
||||||
|
|
||||||
|
_, err = th.App.CreateOrRestoreGroupMember(gleeGroup.Id, singer1.Id)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test groupmember not created: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
scientistGroupMember, err := th.App.CreateOrRestoreGroupMember(scienceGroup.Id, scientist1.Id)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test groupmember not created: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
pErr := th.App.PopulateSyncablesSince(0)
|
||||||
|
if pErr != nil {
|
||||||
|
t.Errorf("faild to populate syncables: %s", pErr.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Singer should be in team and channel
|
||||||
|
_, err = th.App.GetTeamMember(singersTeam.Id, singer1.Id)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error retrieving team member: %s", err.Error())
|
||||||
|
}
|
||||||
|
_, err = th.App.GetChannelMember(practiceChannel.Id, singer1.Id)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error retrieving channel member: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
tMembers, err := th.App.GetTeamMembers(singersTeam.Id, 0, 999)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error retrieving team members: %s", err.Error())
|
||||||
|
}
|
||||||
|
expected := 1
|
||||||
|
actual := len(tMembers)
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf("expected %d team members but got %d", expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
cMembersCount, err := th.App.GetChannelMemberCount(practiceChannel.Id)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error retrieving team members: %s", err.Error())
|
||||||
|
}
|
||||||
|
if cMembersCount != int64(expected) {
|
||||||
|
t.Errorf("expected %d team member but got %d", expected, cMembersCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scientist should not be in team or channel
|
||||||
|
_, err = th.App.GetTeamMember(nerdsTeam.Id, scientist1.Id)
|
||||||
|
if err.Id != "store.sql_team.get_member.missing.app_error" {
|
||||||
|
t.Errorf("wrong error: %s", err.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = th.App.GetChannelMember(experimentsChannel.Id, scientist1.Id)
|
||||||
|
if err.Id != "store.sql_channel.get_member.missing.app_error" {
|
||||||
|
t.Errorf("wrong error: %s", err.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
tMembers, err = th.App.GetTeamMembers(nerdsTeam.Id, 0, 999)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error retrieving team members: %s", err.Error())
|
||||||
|
}
|
||||||
|
expected = 0
|
||||||
|
actual = len(tMembers)
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf("expected %d team members but got %d", expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
cMembersCount, err = th.App.GetChannelMemberCount(experimentsChannel.Id)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error retrieving team members: %s", err.Error())
|
||||||
|
}
|
||||||
|
if cMembersCount != int64(expected) {
|
||||||
|
t.Errorf("expected %d team members but got %d", expected, cMembersCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// update AutoAdd to true
|
||||||
|
scienceTeamGroupSyncable.AutoAdd = true
|
||||||
|
scienceTeamGroupSyncable, err = th.App.UpdateGroupSyncable(scienceTeamGroupSyncable)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error updating group syncable: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync everything after syncable was created (proving that team updates trigger re-sync)
|
||||||
|
pErr = th.App.PopulateSyncablesSince(scientistGroupMember.CreateAt + 1)
|
||||||
|
if pErr != nil {
|
||||||
|
t.Errorf("faild to populate syncables: %s", pErr.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scientist should be in team but not the channel
|
||||||
|
_, err = th.App.GetTeamMember(nerdsTeam.Id, scientist1.Id)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error retrieving team member: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = th.App.GetChannelMember(experimentsChannel.Id, scientist1.Id)
|
||||||
|
if err.Id != "store.sql_channel.get_member.missing.app_error" {
|
||||||
|
t.Errorf("wrong error: %s", err.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
tMembers, err = th.App.GetTeamMembers(nerdsTeam.Id, 0, 999)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error retrieving team members: %s", err.Error())
|
||||||
|
}
|
||||||
|
expected = 1
|
||||||
|
actual = len(tMembers)
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf("expected %d team members but got %d", expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected = 0
|
||||||
|
cMembersCount, err = th.App.GetChannelMemberCount(experimentsChannel.Id)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error retrieving team members: %s", err.Error())
|
||||||
|
}
|
||||||
|
if cMembersCount != int64(expected) {
|
||||||
|
t.Errorf("expected %d team members but got %d", expected, cMembersCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the channel syncable
|
||||||
|
scienceChannelGroupSyncable.AutoAdd = true
|
||||||
|
scienceChannelGroupSyncable, err = th.App.UpdateGroupSyncable(scienceChannelGroupSyncable)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error updating group syncable: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync everything after syncable was created (proving that channel updates trigger re-sync)
|
||||||
|
pErr = th.App.PopulateSyncablesSince(scientistGroupMember.CreateAt + 1)
|
||||||
|
if pErr != nil {
|
||||||
|
t.Errorf("faild to populate syncables: %s", pErr.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
expected = 1
|
||||||
|
cMembersCount, err = th.App.GetChannelMemberCount(experimentsChannel.Id)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error retrieving team members: %s", err.Error())
|
||||||
|
}
|
||||||
|
if cMembersCount != int64(expected) {
|
||||||
|
t.Errorf("expected %d team members but got %d", expected, cMembersCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// singer leaves team and channel
|
||||||
|
err = th.App.LeaveChannel(practiceChannel.Id, singer1.Id)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error leaving channel: %s", err.Error())
|
||||||
|
}
|
||||||
|
err = th.App.LeaveTeam(singersTeam, singer1, "")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error leaving team: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Even re-syncing from the beginning doesn't re-add to channel or team
|
||||||
|
pErr = th.App.PopulateSyncablesSince(0)
|
||||||
|
if pErr != nil {
|
||||||
|
t.Errorf("faild to populate syncables: %s", pErr.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Singer should not be in team or channel
|
||||||
|
tMember, err := th.App.GetTeamMember(singersTeam.Id, singer1.Id)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error retrieving team member: %s", err.Error())
|
||||||
|
}
|
||||||
|
if tMember.DeleteAt == 0 {
|
||||||
|
t.Error("expected team member to remain deleted")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = th.App.GetChannelMember(practiceChannel.Id, singer1.Id)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Expected channel member to remain deleted")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure members are in channel
|
||||||
|
_, err = th.App.AddChannelMember(scientist1.Id, experimentsChannel, "", "", false)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unable to add user to channel: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add other user so that user can leave channel
|
||||||
|
_, err = th.App.AddTeamMember(singersTeam.Id, singer1.Id)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unable to add user to team: %s", err.Error())
|
||||||
|
}
|
||||||
|
_, err = th.App.AddChannelMember(singer1.Id, experimentsChannel, "", "", false)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unable to add user to channel: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// the channel syncable is updated
|
||||||
|
scienceChannelGroupSyncable.CanLeave = false
|
||||||
|
scienceChannelGroupSyncable, err = th.App.UpdateGroupSyncable(scienceChannelGroupSyncable)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error updating group syncable: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
pErr = th.App.PopulateSyncablesSince(0)
|
||||||
|
if pErr != nil {
|
||||||
|
t.Errorf("faild to populate syncables: %s", pErr.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
timeBeforeLeaving := model.GetMillis()
|
||||||
|
|
||||||
|
// User leaves channel
|
||||||
|
err = th.App.LeaveChannel(experimentsChannel.Id, scientist1.Id)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unable to add user to channel: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
timeAfterLeaving := model.GetMillis()
|
||||||
|
|
||||||
|
// Purging channelmemberhistory doesn't re-add user to channel
|
||||||
|
result := <-th.App.Srv.Store.ChannelMemberHistory().PermanentDeleteBatch(timeBeforeLeaving, 1000)
|
||||||
|
if result.Err != nil {
|
||||||
|
t.Errorf("error permanently deleting channelmemberhistory: %s", result.Err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
pErr = th.App.PopulateSyncablesSince(scienceChannelGroupSyncable.UpdateAt)
|
||||||
|
if pErr != nil {
|
||||||
|
t.Errorf("failed to populate syncables: %s", pErr.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = th.App.GetChannelMember(experimentsChannel.Id, scientist1.Id)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Expected channel member to remain deleted")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Purging channelmemberhistory doesn't re-add user to channel
|
||||||
|
result = <-th.App.Srv.Jobs.Store.ChannelMemberHistory().PermanentDeleteBatch(timeAfterLeaving, 1000)
|
||||||
|
if result.Err != nil {
|
||||||
|
t.Errorf("error permanently deleting channelmemberhistory: %s", result.Err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
pErr = th.App.PopulateSyncablesSince(scienceChannelGroupSyncable.UpdateAt)
|
||||||
|
if pErr != nil {
|
||||||
|
t.Errorf("failed to populate syncables: %s", pErr.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Channel member is re-added.
|
||||||
|
_, err = th.App.GetChannelMember(experimentsChannel.Id, scientist1.Id)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected channel member: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -113,13 +113,8 @@ pipeline {
|
|||||||
sh """
|
sh """
|
||||||
docker-compose --no-ansi run --rm start_dependencies
|
docker-compose --no-ansi run --rm start_dependencies
|
||||||
docker-compose --no-ansi ps
|
docker-compose --no-ansi ps
|
||||||
|
docker-compose --no-ansi exec -T openldap bash -c 'ldapadd -x -D "cn=admin,dc=mm,dc=test,dc=com" -w mostest -f /add-users.ldif';
|
||||||
docker-compose --no-ansi exec -T openldap bash -c 'echo -e "dn: ou=testusers,dc=mm,dc=test,dc=com\nobjectclass: organizationalunit" | ldapadd -x -D "cn=admin,dc=mm,dc=test,dc=com" -w mostest';
|
docker-compose --no-ansi exec -T openldap bash -c 'ldapadd -x -D "cn=admin,dc=mm,dc=test,dc=com" -w mostest -f /add-groups.ldif';
|
||||||
docker-compose --no-ansi exec -T openldap bash -c 'echo -e "dn: uid=test.one,ou=testusers,dc=mm,dc=test,dc=com\nobjectclass: iNetOrgPerson\nsn: User\ncn: Test1\nmail: success+testone@simulator.amazonses.com" | ldapadd -x -D "cn=admin,dc=mm,dc=test,dc=com" -w mostest';
|
|
||||||
docker-compose --no-ansi exec -T openldap bash -c 'ldappasswd -s Password1 -D "cn=admin,dc=mm,dc=test,dc=com" -x "uid=test.one,ou=testusers,dc=mm,dc=test,dc=com" -w mostest';
|
|
||||||
docker-compose --no-ansi exec -T openldap bash -c 'echo -e "dn: uid=test.two,ou=testusers,dc=mm,dc=test,dc=com\nobjectclass: iNetOrgPerson\nsn: User\ncn: Test2\nmail: success+testtwo@simulator.amazonses.com" | ldapadd -x -D "cn=admin,dc=mm,dc=test,dc=com" -w mostest';
|
|
||||||
docker-compose --no-ansi exec -T openldap bash -c 'ldappasswd -s Password1 -D "cn=admin,dc=mm,dc=test,dc=com" -x "uid=test.two,ou=testusers,dc=mm,dc=test,dc=com" -w mostest';
|
|
||||||
docker-compose --no-ansi exec -T openldap bash -c 'echo -e "dn: cn=tgroup,ou=testusers,dc=mm,dc=test,dc=com\nobjectclass: groupOfUniqueNames\nuniqueMember: uid=test.one,ou=testusers,dc=mm,dc=test,dc=com" | ldapadd -x -D "cn=admin,dc=mm,dc=test,dc=com" -w mostest';
|
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- mm-test
|
- mm-test
|
||||||
openldap:
|
openldap:
|
||||||
image: "osixia/openldap:1.1.6"
|
image: "osixia/openldap:1.2.2"
|
||||||
restart: always
|
restart: always
|
||||||
networks:
|
networks:
|
||||||
- mm-test
|
- mm-test
|
||||||
@@ -47,6 +47,9 @@ services:
|
|||||||
LDAP_ORGANISATION: "Mattermost Test"
|
LDAP_ORGANISATION: "Mattermost Test"
|
||||||
LDAP_DOMAIN: "mm.test.com"
|
LDAP_DOMAIN: "mm.test.com"
|
||||||
LDAP_ADMIN_PASSWORD: "mostest"
|
LDAP_ADMIN_PASSWORD: "mostest"
|
||||||
|
volumes:
|
||||||
|
- "../tests/add-users.ldif:/add-users.ldif"
|
||||||
|
- "../tests/add-groups.ldif:/add-groups.ldif"
|
||||||
elasticsearch:
|
elasticsearch:
|
||||||
image: "mattermost/mattermost-elasticsearch-docker:6.5.1"
|
image: "mattermost/mattermost-elasticsearch-docker:6.5.1"
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
@@ -76,7 +76,8 @@
|
|||||||
"ImageProxyURL": "",
|
"ImageProxyURL": "",
|
||||||
"EnableAPITeamDeletion": false,
|
"EnableAPITeamDeletion": false,
|
||||||
"ExperimentalEnableHardenedMode": false,
|
"ExperimentalEnableHardenedMode": false,
|
||||||
"EnableEmailInvitations": false
|
"EnableEmailInvitations": false,
|
||||||
|
"ExperimentalLdapGroupSync": false
|
||||||
},
|
},
|
||||||
"TeamSettings": {
|
"TeamSettings": {
|
||||||
"SiteName": "Mattermost",
|
"SiteName": "Mattermost",
|
||||||
@@ -277,6 +278,9 @@
|
|||||||
"BindUsername": "",
|
"BindUsername": "",
|
||||||
"BindPassword": "",
|
"BindPassword": "",
|
||||||
"UserFilter": "",
|
"UserFilter": "",
|
||||||
|
"GroupFilter": "",
|
||||||
|
"GroupDisplayNameAttribute": "",
|
||||||
|
"GroupIdAttribute": "",
|
||||||
"FirstNameAttribute": "",
|
"FirstNameAttribute": "",
|
||||||
"LastNameAttribute": "",
|
"LastNameAttribute": "",
|
||||||
"EmailAttribute": "",
|
"EmailAttribute": "",
|
||||||
|
|||||||
@@ -19,4 +19,7 @@ type LdapInterface interface {
|
|||||||
RunTest() *model.AppError
|
RunTest() *model.AppError
|
||||||
GetAllLdapUsers() ([]*model.User, *model.AppError)
|
GetAllLdapUsers() ([]*model.User, *model.AppError)
|
||||||
MigrateIDAttribute(toAttribute string) error
|
MigrateIDAttribute(toAttribute string) error
|
||||||
|
GetGroup(groupUID string) (*model.Group, *model.AppError)
|
||||||
|
GetAllGroupsPage(page int, perPage int) ([]*model.Group, int, *model.AppError)
|
||||||
|
FirstLoginSync(user *model.User) *model.AppError
|
||||||
}
|
}
|
||||||
|
|||||||
144
i18n/en.json
144
i18n/en.json
@@ -6765,5 +6765,149 @@
|
|||||||
{
|
{
|
||||||
"id": "web.incoming_webhook.user.app_error",
|
"id": "web.incoming_webhook.user.app_error",
|
||||||
"translation": "Couldn't find the user"
|
"translation": "Couldn't find the user"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "api.io_error",
|
||||||
|
"translation": "input/output error"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "api.marshal_error",
|
||||||
|
"translation": "marshal error"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "api.ldap_groups.license_error",
|
||||||
|
"translation": "your license does not support ldap groups"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "api.ldap_group.not_found",
|
||||||
|
"translation": "ldap group not found"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ent.ldap_groups.reachable_groups_error",
|
||||||
|
"translation": "error retrieving groups for user"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ent.ldap_groups.no_rows",
|
||||||
|
"translation": "no groups found with matching uid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ent.ldap_groups.members_of_group_error",
|
||||||
|
"translation": "error retrieving members of group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ent.ldap_groups.group_search_error",
|
||||||
|
"translation": "error retrieving ldap group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ent.ldap_groups.groups_search_error",
|
||||||
|
"translation": "error retrieving ldap groups"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ent.ldap.syncronize.get_all_groups.app_error",
|
||||||
|
"translation": "error retrieving groups"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ent.ldap.syncronize.populate_syncables",
|
||||||
|
"translation": "error populating syncables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "model.group.create_at.app_error",
|
||||||
|
"translation": "invalid create at property for group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "model.group.description.app_error",
|
||||||
|
"translation": "invalid description property for group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "model.group.display_name.app_error",
|
||||||
|
"translation": "invalid display name property for group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "model.group.id.app_error",
|
||||||
|
"translation": "invalid id property for group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "model.group.name.app_error",
|
||||||
|
"translation": "invalid name property for group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "model.group.remote_id.app_error",
|
||||||
|
"translation": "invalid remote id property for group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "model.group.source.app_error",
|
||||||
|
"translation": "invalid source property for group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "model.group_syncable.type.app_error",
|
||||||
|
"translation": "invalid type property for group syncable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "model.group_syncable.unmarshaljson.duplicate_type_identifier",
|
||||||
|
"translation": "type identifier already exists: perhaps json contains both 'team_id' and 'channel_id'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "model.group.update_at.app_error",
|
||||||
|
"translation": "invalid update at property for group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "model.group_member.group_id.app_error",
|
||||||
|
"translation": "invalid group id property for group member"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "model.group_member.user_id.app_error",
|
||||||
|
"translation": "invalid user id property for group member"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "model.group_syncable.group_id.app_error",
|
||||||
|
"translation": "invalid group id property for group syncable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "model.group_syncable.syncable_id.app_error",
|
||||||
|
"translation": "invalid syncable id for group syncable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "store.sql_group.group_syncable_already_deleted",
|
||||||
|
"translation": "group syncable was already deleted"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "store.insert_error",
|
||||||
|
"translation": "insert error"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "model.group.delete_at.app_error",
|
||||||
|
"translation": "invalid delete at property for group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "store.sql_group.no_rows",
|
||||||
|
"translation": "no matching group found"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "store.sql_group.no_rows_changed",
|
||||||
|
"translation": "no rows changed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "store.sql_group.nothing_to_update",
|
||||||
|
"translation": "nothing to update for group syncable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "store.select_error",
|
||||||
|
"translation": "select error"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "store.sql_group.unique_constraint",
|
||||||
|
"translation": "a group with that name already exists"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "store.sql_group.uniqueness_error",
|
||||||
|
"translation": "group member already exists"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "store.update_error",
|
||||||
|
"translation": "update error"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ent.ldap.app_error",
|
||||||
|
"translation": "ldap interface was nil"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -53,6 +53,13 @@ type Channel struct {
|
|||||||
Props map[string]interface{} `json:"props" db:"-"`
|
Props map[string]interface{} `json:"props" db:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ChannelWithTeamData struct {
|
||||||
|
Channel
|
||||||
|
TeamDisplayName string `json:"team_display_name"`
|
||||||
|
TeamName string `json:"team_name"`
|
||||||
|
TeamUpdateAt int64 `json:"team_update_at"`
|
||||||
|
}
|
||||||
|
|
||||||
type ChannelPatch struct {
|
type ChannelPatch struct {
|
||||||
DisplayName *string `json:"display_name"`
|
DisplayName *string `json:"display_name"`
|
||||||
Name *string `json:"name"`
|
Name *string `json:"name"`
|
||||||
|
|||||||
@@ -51,3 +51,45 @@ func ChannelSliceFromJson(data io.Reader) []*Channel {
|
|||||||
json.NewDecoder(data).Decode(&o)
|
json.NewDecoder(data).Decode(&o)
|
||||||
return o
|
return o
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ChannelListWithTeamData []*ChannelWithTeamData
|
||||||
|
|
||||||
|
func (o *ChannelListWithTeamData) ToJson() string {
|
||||||
|
if b, err := json.Marshal(o); err != nil {
|
||||||
|
return "[]"
|
||||||
|
} else {
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *ChannelListWithTeamData) Etag() string {
|
||||||
|
|
||||||
|
id := "0"
|
||||||
|
var t int64 = 0
|
||||||
|
var delta int64 = 0
|
||||||
|
|
||||||
|
for _, v := range *o {
|
||||||
|
if v.LastPostAt > t {
|
||||||
|
t = v.LastPostAt
|
||||||
|
id = v.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.UpdateAt > t {
|
||||||
|
t = v.UpdateAt
|
||||||
|
id = v.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.TeamUpdateAt > t {
|
||||||
|
t = v.TeamUpdateAt
|
||||||
|
id = v.Id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Etag(id, t, delta, len(*o))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChannelListWithTeamDataFromJson(data io.Reader) *ChannelListWithTeamData {
|
||||||
|
var o *ChannelListWithTeamData
|
||||||
|
json.NewDecoder(data).Decode(&o)
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|||||||
144
model/client4.go
144
model/client4.go
@@ -409,6 +409,22 @@ func (c *Client4) GetTermsOfServiceRoute() string {
|
|||||||
return "/terms_of_service"
|
return "/terms_of_service"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client4) GetGroupsRoute() string {
|
||||||
|
return "/groups"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client4) GetGroupRoute(groupID string) string {
|
||||||
|
return fmt.Sprintf("%s/%s", c.GetGroupsRoute(), groupID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client4) GetGroupSyncableRoute(groupID, syncableID string, syncableType GroupSyncableType) string {
|
||||||
|
return fmt.Sprintf("%s/%ss/%s", c.GetGroupRoute(groupID), strings.ToLower(syncableType.String()), syncableID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client4) GetGroupSyncablesRoute(groupID string, syncableType GroupSyncableType) string {
|
||||||
|
return fmt.Sprintf("%s/%ss", c.GetGroupRoute(groupID), strings.ToLower(syncableType.String()))
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client4) DoApiGet(url string, etag string) (*http.Response, *AppError) {
|
func (c *Client4) DoApiGet(url string, etag string) (*http.Response, *AppError) {
|
||||||
return c.DoApiRequest(http.MethodGet, c.ApiUrl+url, "", etag)
|
return c.DoApiRequest(http.MethodGet, c.ApiUrl+url, "", etag)
|
||||||
}
|
}
|
||||||
@@ -1706,6 +1722,17 @@ func (c *Client4) RemoveTeamIcon(teamId string) (bool, *Response) {
|
|||||||
|
|
||||||
// Channel Section
|
// Channel Section
|
||||||
|
|
||||||
|
// GetAllChannels get all the channels. Must be a system administrator.
|
||||||
|
func (c *Client4) GetAllChannels(page int, perPage int, etag string) (*ChannelListWithTeamData, *Response) {
|
||||||
|
query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage)
|
||||||
|
r, err := c.DoApiGet(c.GetChannelsRoute()+query, etag)
|
||||||
|
if err != nil {
|
||||||
|
return nil, BuildErrorResponse(r, err)
|
||||||
|
}
|
||||||
|
defer closeBody(r)
|
||||||
|
return ChannelListWithTeamDataFromJson(r.Body), BuildResponse(r)
|
||||||
|
}
|
||||||
|
|
||||||
// CreateChannel creates a channel based on the provided channel struct.
|
// CreateChannel creates a channel based on the provided channel struct.
|
||||||
func (c *Client4) CreateChannel(channel *Channel) (*Channel, *Response) {
|
func (c *Client4) CreateChannel(channel *Channel) (*Channel, *Response) {
|
||||||
r, err := c.DoApiPost(c.GetChannelsRoute(), channel.ToJson())
|
r, err := c.DoApiPost(c.GetChannelsRoute(), channel.ToJson())
|
||||||
@@ -1870,6 +1897,16 @@ func (c *Client4) SearchChannels(teamId string, search *ChannelSearch) ([]*Chann
|
|||||||
return ChannelSliceFromJson(r.Body), BuildResponse(r)
|
return ChannelSliceFromJson(r.Body), BuildResponse(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SearchAllChannels search in all the channels. Must be a system administrator.
|
||||||
|
func (c *Client4) SearchAllChannels(search *ChannelSearch) (*ChannelListWithTeamData, *Response) {
|
||||||
|
r, err := c.DoApiPost(c.GetChannelsRoute()+"/search", search.ToJson())
|
||||||
|
if err != nil {
|
||||||
|
return nil, BuildErrorResponse(r, err)
|
||||||
|
}
|
||||||
|
defer closeBody(r)
|
||||||
|
return ChannelListWithTeamDataFromJson(r.Body), BuildResponse(r)
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteChannel deletes channel based on the provided channel id string.
|
// DeleteChannel deletes channel based on the provided channel id string.
|
||||||
func (c *Client4) DeleteChannel(channelId string) (bool, *Response) {
|
func (c *Client4) DeleteChannel(channelId string) (bool, *Response) {
|
||||||
r, err := c.DoApiDelete(c.GetChannelRoute(channelId))
|
r, err := c.DoApiDelete(c.GetChannelRoute(channelId))
|
||||||
@@ -3066,6 +3103,45 @@ func (c *Client4) TestLdap() (bool, *Response) {
|
|||||||
return CheckStatusOK(r), BuildResponse(r)
|
return CheckStatusOK(r), BuildResponse(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetLdapGroups retrieves the immediate child groups of the given parent group.
|
||||||
|
func (c *Client4) GetLdapGroups() ([]*Group, *Response) {
|
||||||
|
path := fmt.Sprintf("%s/groups", c.GetLdapRoute())
|
||||||
|
|
||||||
|
r, appErr := c.DoApiGet(path, "")
|
||||||
|
if appErr != nil {
|
||||||
|
return nil, BuildErrorResponse(r, appErr)
|
||||||
|
}
|
||||||
|
defer closeBody(r)
|
||||||
|
|
||||||
|
return GroupsFromJson(r.Body), BuildResponse(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LinkLdapGroup creates or undeletes a Mattermost group and associates it to the given LDAP group DN.
|
||||||
|
func (c *Client4) LinkLdapGroup(dn string) (*Group, *Response) {
|
||||||
|
path := fmt.Sprintf("%s/groups/%s/link", c.GetLdapRoute(), dn)
|
||||||
|
|
||||||
|
r, appErr := c.DoApiPost(path, "")
|
||||||
|
if appErr != nil {
|
||||||
|
return nil, BuildErrorResponse(r, appErr)
|
||||||
|
}
|
||||||
|
defer closeBody(r)
|
||||||
|
|
||||||
|
return GroupFromJson(r.Body), BuildResponse(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnlinkLdapGroup deletes the Mattermost group associated with the given LDAP group DN.
|
||||||
|
func (c *Client4) UnlinkLdapGroup(dn string) (*Group, *Response) {
|
||||||
|
path := fmt.Sprintf("%s/groups/%s/link", c.GetLdapRoute(), dn)
|
||||||
|
|
||||||
|
r, appErr := c.DoApiDelete(path)
|
||||||
|
if appErr != nil {
|
||||||
|
return nil, BuildErrorResponse(r, appErr)
|
||||||
|
}
|
||||||
|
defer closeBody(r)
|
||||||
|
|
||||||
|
return GroupFromJson(r.Body), BuildResponse(r)
|
||||||
|
}
|
||||||
|
|
||||||
// Audits Section
|
// Audits Section
|
||||||
|
|
||||||
// GetAudits returns a list of audits for the whole system.
|
// GetAudits returns a list of audits for the whole system.
|
||||||
@@ -4030,3 +4106,71 @@ func (c *Client4) CreateTermsOfService(text, userId string) (*TermsOfService, *R
|
|||||||
defer closeBody(r)
|
defer closeBody(r)
|
||||||
return TermsOfServiceFromJson(r.Body), BuildResponse(r)
|
return TermsOfServiceFromJson(r.Body), BuildResponse(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client4) GetGroup(groupID, etag string) (*Group, *Response) {
|
||||||
|
r, appErr := c.DoApiGet(c.GetGroupRoute(groupID), etag)
|
||||||
|
if appErr != nil {
|
||||||
|
return nil, BuildErrorResponse(r, appErr)
|
||||||
|
}
|
||||||
|
defer closeBody(r)
|
||||||
|
return GroupFromJson(r.Body), BuildResponse(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client4) PatchGroup(groupID string, patch *GroupPatch) (*Group, *Response) {
|
||||||
|
payload, _ := json.Marshal(patch)
|
||||||
|
r, appErr := c.DoApiPut(c.GetGroupRoute(groupID)+"/patch", string(payload))
|
||||||
|
if appErr != nil {
|
||||||
|
return nil, BuildErrorResponse(r, appErr)
|
||||||
|
}
|
||||||
|
defer closeBody(r)
|
||||||
|
return GroupFromJson(r.Body), BuildResponse(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client4) LinkGroupSyncable(groupID, syncableID string, syncableType GroupSyncableType, patch *GroupSyncablePatch) (*GroupSyncable, *Response) {
|
||||||
|
payload, _ := json.Marshal(patch)
|
||||||
|
url := fmt.Sprintf("%s/link", c.GetGroupSyncableRoute(groupID, syncableID, syncableType))
|
||||||
|
r, appErr := c.DoApiPost(url, string(payload))
|
||||||
|
if appErr != nil {
|
||||||
|
return nil, BuildErrorResponse(r, appErr)
|
||||||
|
}
|
||||||
|
defer closeBody(r)
|
||||||
|
return GroupSyncableFromJson(r.Body), BuildResponse(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client4) UnlinkGroupSyncable(groupID, syncableID string, syncableType GroupSyncableType) *Response {
|
||||||
|
url := fmt.Sprintf("%s/link", c.GetGroupSyncableRoute(groupID, syncableID, syncableType))
|
||||||
|
r, appErr := c.DoApiDelete(url)
|
||||||
|
if appErr != nil {
|
||||||
|
return BuildErrorResponse(r, appErr)
|
||||||
|
}
|
||||||
|
defer closeBody(r)
|
||||||
|
return BuildResponse(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client4) GetGroupSyncable(groupID, syncableID string, syncableType GroupSyncableType, etag string) (*GroupSyncable, *Response) {
|
||||||
|
r, appErr := c.DoApiGet(c.GetGroupSyncableRoute(groupID, syncableID, syncableType), etag)
|
||||||
|
if appErr != nil {
|
||||||
|
return nil, BuildErrorResponse(r, appErr)
|
||||||
|
}
|
||||||
|
defer closeBody(r)
|
||||||
|
return GroupSyncableFromJson(r.Body), BuildResponse(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client4) GetGroupSyncables(groupID string, syncableType GroupSyncableType, etag string) ([]*GroupSyncable, *Response) {
|
||||||
|
r, appErr := c.DoApiGet(c.GetGroupSyncablesRoute(groupID, syncableType), etag)
|
||||||
|
if appErr != nil {
|
||||||
|
return nil, BuildErrorResponse(r, appErr)
|
||||||
|
}
|
||||||
|
defer closeBody(r)
|
||||||
|
return GroupSyncablesFromJson(r.Body), BuildResponse(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client4) PatchGroupSyncable(groupID, syncableID string, syncableType GroupSyncableType, patch *GroupSyncablePatch) (*GroupSyncable, *Response) {
|
||||||
|
payload, _ := json.Marshal(patch)
|
||||||
|
r, appErr := c.DoApiPut(c.GetGroupSyncableRoute(groupID, syncableID, syncableType)+"/patch", string(payload))
|
||||||
|
if appErr != nil {
|
||||||
|
return nil, BuildErrorResponse(r, appErr)
|
||||||
|
}
|
||||||
|
defer closeBody(r)
|
||||||
|
return GroupSyncableFromJson(r.Body), BuildResponse(r)
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ const (
|
|||||||
CLUSTER_EVENT_CLEAR_SESSION_CACHE_FOR_USER = "clear_session_user"
|
CLUSTER_EVENT_CLEAR_SESSION_CACHE_FOR_USER = "clear_session_user"
|
||||||
CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES = "inv_roles"
|
CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES = "inv_roles"
|
||||||
CLUSTER_EVENT_INVALIDATE_CACHE_FOR_SCHEMES = "inv_schemes"
|
CLUSTER_EVENT_INVALIDATE_CACHE_FOR_SCHEMES = "inv_schemes"
|
||||||
|
CLUSTER_EVENT_INVALIDATE_CACHE_FOR_GROUPS = "inv_groups"
|
||||||
|
|
||||||
CLUSTER_SEND_BEST_EFFORT = "best_effort"
|
CLUSTER_SEND_BEST_EFFORT = "best_effort"
|
||||||
CLUSTER_SEND_RELIABLE = "reliable"
|
CLUSTER_SEND_RELIABLE = "reliable"
|
||||||
|
|||||||
@@ -114,14 +114,16 @@ const (
|
|||||||
SUPPORT_SETTINGS_DEFAULT_SUPPORT_EMAIL = "feedback@mattermost.com"
|
SUPPORT_SETTINGS_DEFAULT_SUPPORT_EMAIL = "feedback@mattermost.com"
|
||||||
SUPPORT_SETTINGS_DEFAULT_RE_ACCEPTANCE_PERIOD = 365
|
SUPPORT_SETTINGS_DEFAULT_RE_ACCEPTANCE_PERIOD = 365
|
||||||
|
|
||||||
LDAP_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE = ""
|
LDAP_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE = ""
|
||||||
LDAP_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE = ""
|
LDAP_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE = ""
|
||||||
LDAP_SETTINGS_DEFAULT_EMAIL_ATTRIBUTE = ""
|
LDAP_SETTINGS_DEFAULT_EMAIL_ATTRIBUTE = ""
|
||||||
LDAP_SETTINGS_DEFAULT_USERNAME_ATTRIBUTE = ""
|
LDAP_SETTINGS_DEFAULT_USERNAME_ATTRIBUTE = ""
|
||||||
LDAP_SETTINGS_DEFAULT_NICKNAME_ATTRIBUTE = ""
|
LDAP_SETTINGS_DEFAULT_NICKNAME_ATTRIBUTE = ""
|
||||||
LDAP_SETTINGS_DEFAULT_ID_ATTRIBUTE = ""
|
LDAP_SETTINGS_DEFAULT_ID_ATTRIBUTE = ""
|
||||||
LDAP_SETTINGS_DEFAULT_POSITION_ATTRIBUTE = ""
|
LDAP_SETTINGS_DEFAULT_POSITION_ATTRIBUTE = ""
|
||||||
LDAP_SETTINGS_DEFAULT_LOGIN_FIELD_NAME = ""
|
LDAP_SETTINGS_DEFAULT_LOGIN_FIELD_NAME = ""
|
||||||
|
LDAP_SETTINGS_DEFAULT_GROUP_DISPLAY_NAME_ATTRIBUTE = ""
|
||||||
|
LDAP_SETTINGS_DEFAULT_GROUP_ID_ATTRIBUTE = ""
|
||||||
|
|
||||||
SAML_SETTINGS_DEFAULT_ID_ATTRIBUTE = ""
|
SAML_SETTINGS_DEFAULT_ID_ATTRIBUTE = ""
|
||||||
SAML_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE = ""
|
SAML_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE = ""
|
||||||
@@ -276,6 +278,7 @@ type ServiceSettings struct {
|
|||||||
EnableAPITeamDeletion *bool
|
EnableAPITeamDeletion *bool
|
||||||
ExperimentalEnableHardenedMode *bool
|
ExperimentalEnableHardenedMode *bool
|
||||||
EnableEmailInvitations *bool
|
EnableEmailInvitations *bool
|
||||||
|
ExperimentalLdapGroupSync *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServiceSettings) SetDefaults() {
|
func (s *ServiceSettings) SetDefaults() {
|
||||||
@@ -566,6 +569,10 @@ func (s *ServiceSettings) SetDefaults() {
|
|||||||
if s.ExperimentalEnableHardenedMode == nil {
|
if s.ExperimentalEnableHardenedMode == nil {
|
||||||
s.ExperimentalEnableHardenedMode = NewBool(false)
|
s.ExperimentalEnableHardenedMode = NewBool(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.ExperimentalLdapGroupSync == nil {
|
||||||
|
s.ExperimentalLdapGroupSync = NewBool(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClusterSettings struct {
|
type ClusterSettings struct {
|
||||||
@@ -1322,7 +1329,12 @@ type LdapSettings struct {
|
|||||||
BindPassword *string
|
BindPassword *string
|
||||||
|
|
||||||
// Filtering
|
// Filtering
|
||||||
UserFilter *string
|
UserFilter *string
|
||||||
|
GroupFilter *string
|
||||||
|
|
||||||
|
// Group Mapping
|
||||||
|
GroupDisplayNameAttribute *string
|
||||||
|
GroupIdAttribute *string
|
||||||
|
|
||||||
// User Mapping
|
// User Mapping
|
||||||
FirstNameAttribute *string
|
FirstNameAttribute *string
|
||||||
@@ -1388,6 +1400,18 @@ func (s *LdapSettings) SetDefaults() {
|
|||||||
s.UserFilter = NewString("")
|
s.UserFilter = NewString("")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.GroupFilter == nil {
|
||||||
|
s.GroupFilter = NewString("")
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.GroupDisplayNameAttribute == nil {
|
||||||
|
s.GroupDisplayNameAttribute = NewString(LDAP_SETTINGS_DEFAULT_GROUP_DISPLAY_NAME_ATTRIBUTE)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.GroupIdAttribute == nil {
|
||||||
|
s.GroupIdAttribute = NewString(LDAP_SETTINGS_DEFAULT_GROUP_ID_ATTRIBUTE)
|
||||||
|
}
|
||||||
|
|
||||||
if s.FirstNameAttribute == nil {
|
if s.FirstNameAttribute == nil {
|
||||||
s.FirstNameAttribute = NewString(LDAP_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE)
|
s.FirstNameAttribute = NewString(LDAP_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE)
|
||||||
}
|
}
|
||||||
|
|||||||
135
model/group.go
Обычный файл
135
model/group.go
Обычный файл
@@ -0,0 +1,135 @@
|
|||||||
|
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
GroupSourceLdap GroupSource = "ldap"
|
||||||
|
|
||||||
|
GroupNameMaxLength = 64
|
||||||
|
GroupSourceMaxLength = 64
|
||||||
|
GroupDisplayNameMaxLength = 128
|
||||||
|
GroupDescriptionMaxLength = 1024
|
||||||
|
GroupRemoteIDMaxLength = 48
|
||||||
|
)
|
||||||
|
|
||||||
|
type GroupSource string
|
||||||
|
|
||||||
|
var allGroupSources = []GroupSource{
|
||||||
|
GroupSourceLdap,
|
||||||
|
}
|
||||||
|
|
||||||
|
var groupSourcesRequiringRemoteID = []GroupSource{
|
||||||
|
GroupSourceLdap,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Group struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
DisplayName string `json:"display_name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Source GroupSource `json:"source"`
|
||||||
|
RemoteId string `json:"remote_id"`
|
||||||
|
CreateAt int64 `json:"create_at"`
|
||||||
|
UpdateAt int64 `json:"update_at"`
|
||||||
|
DeleteAt int64 `json:"delete_at"`
|
||||||
|
HasSyncables bool `db:"-" json:"has_syncables"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GroupPatch struct {
|
||||||
|
Name *string `json:"name"`
|
||||||
|
DisplayName *string `json:"display_name"`
|
||||||
|
Description *string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (group *Group) Patch(patch *GroupPatch) {
|
||||||
|
if patch.Name != nil {
|
||||||
|
group.Name = *patch.Name
|
||||||
|
}
|
||||||
|
if patch.DisplayName != nil {
|
||||||
|
group.DisplayName = *patch.DisplayName
|
||||||
|
}
|
||||||
|
if patch.Description != nil {
|
||||||
|
group.Description = *patch.Description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (group *Group) IsValidForCreate() *AppError {
|
||||||
|
if l := len(group.Name); l == 0 || l > GroupNameMaxLength {
|
||||||
|
return NewAppError("Group.IsValidForCreate", "model.group.name.app_error", map[string]interface{}{"GroupNameMaxLength": GroupNameMaxLength}, "", http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
if l := len(group.DisplayName); l == 0 || l > GroupDisplayNameMaxLength {
|
||||||
|
return NewAppError("Group.IsValidForCreate", "model.group.display_name.app_error", map[string]interface{}{"GroupDisplayNameMaxLength": GroupDisplayNameMaxLength}, "", http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(group.Description) > GroupDescriptionMaxLength {
|
||||||
|
return NewAppError("Group.IsValidForCreate", "model.group.description.app_error", map[string]interface{}{"GroupDescriptionMaxLength": GroupDescriptionMaxLength}, "", http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
isValidSource := false
|
||||||
|
for _, groupSource := range allGroupSources {
|
||||||
|
if group.Source == groupSource {
|
||||||
|
isValidSource = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !isValidSource {
|
||||||
|
return NewAppError("Group.IsValidForCreate", "model.group.source.app_error", nil, "", http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(group.RemoteId) > GroupRemoteIDMaxLength || (len(group.RemoteId) == 0 && group.requiresRemoteId()) {
|
||||||
|
return NewAppError("Group.IsValidForCreate", "model.group.remote_id.app_error", nil, "", http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (group *Group) requiresRemoteId() bool {
|
||||||
|
for _, groupSource := range groupSourcesRequiringRemoteID {
|
||||||
|
if groupSource == group.Source {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (group *Group) IsValidForUpdate() *AppError {
|
||||||
|
if len(group.Id) != 26 {
|
||||||
|
return NewAppError("Group.IsValidForUpdate", "model.group.id.app_error", nil, "", http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
if group.CreateAt == 0 {
|
||||||
|
return NewAppError("Group.IsValidForUpdate", "model.group.create_at.app_error", nil, "", http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
if group.UpdateAt == 0 {
|
||||||
|
return NewAppError("Group.IsValidForUpdate", "model.group.update_at.app_error", nil, "", http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
if err := group.IsValidForCreate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GroupFromJson(data io.Reader) *Group {
|
||||||
|
var group *Group
|
||||||
|
json.NewDecoder(data).Decode(&group)
|
||||||
|
return group
|
||||||
|
}
|
||||||
|
|
||||||
|
func GroupsFromJson(data io.Reader) []*Group {
|
||||||
|
var groups []*Group
|
||||||
|
json.NewDecoder(data).Decode(&groups)
|
||||||
|
return groups
|
||||||
|
}
|
||||||
|
|
||||||
|
func GroupPatchFromJson(data io.Reader) *GroupPatch {
|
||||||
|
var groupPatch *GroupPatch
|
||||||
|
json.NewDecoder(data).Decode(&groupPatch)
|
||||||
|
return groupPatch
|
||||||
|
}
|
||||||
23
model/group_member.go
Обычный файл
23
model/group_member.go
Обычный файл
@@ -0,0 +1,23 @@
|
|||||||
|
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
type GroupMember struct {
|
||||||
|
GroupId string `json:"group_id"`
|
||||||
|
UserId string `json:"user_id"`
|
||||||
|
CreateAt int64 `json:"create_at"`
|
||||||
|
DeleteAt int64 `json:"delete_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gm *GroupMember) IsValid() *AppError {
|
||||||
|
if !IsValidId(gm.GroupId) {
|
||||||
|
return NewAppError("GroupMember.IsValid", "model.group_member.group_id.app_error", nil, "", http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
if !IsValidId(gm.UserId) {
|
||||||
|
return NewAppError("GroupMember.IsValid", "model.group_member.user_id.app_error", nil, "", http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
169
model/group_syncable.go
Обычный файл
169
model/group_syncable.go
Обычный файл
@@ -0,0 +1,169 @@
|
|||||||
|
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GroupSyncableType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
GroupSyncableTypeTeam GroupSyncableType = "Team"
|
||||||
|
GroupSyncableTypeChannel GroupSyncableType = "Channel"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (gst GroupSyncableType) String() string {
|
||||||
|
return string(gst)
|
||||||
|
}
|
||||||
|
|
||||||
|
type GroupSyncable struct {
|
||||||
|
GroupId string `json:"group_id"`
|
||||||
|
|
||||||
|
// SyncableId represents the Id of the model that is being synced with the group, for example a ChannelId or
|
||||||
|
// TeamId.
|
||||||
|
SyncableId string `db:"-" json:"-"`
|
||||||
|
|
||||||
|
CanLeave bool `db:"-" json:"can_leave"`
|
||||||
|
AutoAdd bool `json:"auto_add"`
|
||||||
|
CreateAt int64 `json:"create_at"`
|
||||||
|
DeleteAt int64 `json:"delete_at"`
|
||||||
|
UpdateAt int64 `json:"update_at"`
|
||||||
|
Type GroupSyncableType `db:"-" json:"-"`
|
||||||
|
|
||||||
|
// Values joined in from the associated team and/or channel
|
||||||
|
ChannelDisplayName string `db:"-" json:"-"`
|
||||||
|
TeamDisplayName string `db:"-" json:"-"`
|
||||||
|
TeamType string `db:"-" json:"-"`
|
||||||
|
ChannelType string `db:"-" json:"-"`
|
||||||
|
TeamID string `db:"-" json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (syncable *GroupSyncable) IsValid() *AppError {
|
||||||
|
if !IsValidId(syncable.GroupId) {
|
||||||
|
return NewAppError("GroupSyncable.SyncableIsValid", "model.group_syncable.group_id.app_error", nil, "", http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
if !IsValidId(syncable.SyncableId) {
|
||||||
|
return NewAppError("GroupSyncable.SyncableIsValid", "model.group_syncable.syncable_id.app_error", nil, "", http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
// TODO: Add this validation check for phase 2 of LDAP group sync.
|
||||||
|
// if syncable.AutoAdd == false && syncable.CanLeave == false {
|
||||||
|
// return NewAppError("GroupSyncable.SyncableIsValid", "model.group_syncable.invalid_state", nil, "", http.StatusBadRequest)
|
||||||
|
// }
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (syncable *GroupSyncable) UnmarshalJSON(b []byte) error {
|
||||||
|
var kvp map[string]interface{}
|
||||||
|
err := json.Unmarshal(b, &kvp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for key, value := range kvp {
|
||||||
|
switch key {
|
||||||
|
case "team_id":
|
||||||
|
syncable.SyncableId = value.(string)
|
||||||
|
syncable.Type = GroupSyncableTypeTeam
|
||||||
|
case "channel_id":
|
||||||
|
syncable.SyncableId = value.(string)
|
||||||
|
syncable.Type = GroupSyncableTypeChannel
|
||||||
|
case "group_id":
|
||||||
|
syncable.GroupId = value.(string)
|
||||||
|
case "can_leave":
|
||||||
|
syncable.CanLeave = value.(bool)
|
||||||
|
case "auto_add":
|
||||||
|
syncable.AutoAdd = value.(bool)
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (syncable *GroupSyncable) MarshalJSON() ([]byte, error) {
|
||||||
|
type Alias GroupSyncable
|
||||||
|
|
||||||
|
switch syncable.Type {
|
||||||
|
case GroupSyncableTypeTeam:
|
||||||
|
return json.Marshal(&struct {
|
||||||
|
TeamID string `json:"team_id"`
|
||||||
|
TeamDisplayName string `json:"team_display_name,omitempty"`
|
||||||
|
TeamType string `json:"team_type,omitempty"`
|
||||||
|
*Alias
|
||||||
|
}{
|
||||||
|
TeamDisplayName: syncable.TeamDisplayName,
|
||||||
|
TeamType: syncable.TeamType,
|
||||||
|
TeamID: syncable.SyncableId,
|
||||||
|
Alias: (*Alias)(syncable),
|
||||||
|
})
|
||||||
|
case GroupSyncableTypeChannel:
|
||||||
|
return json.Marshal(&struct {
|
||||||
|
ChannelID string `json:"channel_id"`
|
||||||
|
ChannelDisplayName string `json:"channel_display_name,omitempty"`
|
||||||
|
ChannelType string `json:"channel_type,omitempty"`
|
||||||
|
|
||||||
|
TeamID string `json:"team_id,omitempty"`
|
||||||
|
TeamDisplayName string `json:"team_display_name,omitempty"`
|
||||||
|
TeamType string `json:"team_type,omitempty"`
|
||||||
|
|
||||||
|
*Alias
|
||||||
|
}{
|
||||||
|
ChannelID: syncable.SyncableId,
|
||||||
|
ChannelDisplayName: syncable.ChannelDisplayName,
|
||||||
|
ChannelType: syncable.ChannelType,
|
||||||
|
|
||||||
|
TeamID: syncable.TeamID,
|
||||||
|
TeamDisplayName: syncable.TeamDisplayName,
|
||||||
|
TeamType: syncable.TeamType,
|
||||||
|
|
||||||
|
Alias: (*Alias)(syncable),
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
return nil, &json.MarshalerError{
|
||||||
|
Err: fmt.Errorf("unknown syncable type: %s", syncable.Type),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type GroupSyncablePatch struct {
|
||||||
|
CanLeave *bool `json:"can_leave"`
|
||||||
|
AutoAdd *bool `json:"auto_add"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (syncable *GroupSyncable) Patch(patch *GroupSyncablePatch) {
|
||||||
|
// TODO: Add this validation check for phase 2 of LDAP group sync.
|
||||||
|
// if patch.CanLeave != nil {
|
||||||
|
// syncable.CanLeave = *patch.CanLeave
|
||||||
|
// }
|
||||||
|
if patch.AutoAdd != nil {
|
||||||
|
syncable.AutoAdd = *patch.AutoAdd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserTeamIDPair struct {
|
||||||
|
UserID string
|
||||||
|
TeamID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserChannelIDPair struct {
|
||||||
|
UserID string
|
||||||
|
ChannelID string
|
||||||
|
}
|
||||||
|
|
||||||
|
func GroupSyncableFromJson(data io.Reader) *GroupSyncable {
|
||||||
|
groupSyncable := &GroupSyncable{}
|
||||||
|
bodyBytes, _ := ioutil.ReadAll(data)
|
||||||
|
json.Unmarshal(bodyBytes, groupSyncable)
|
||||||
|
return groupSyncable
|
||||||
|
}
|
||||||
|
|
||||||
|
func GroupSyncablesFromJson(data io.Reader) []*GroupSyncable {
|
||||||
|
groupSyncables := []*GroupSyncable{}
|
||||||
|
bodyBytes, _ := ioutil.ReadAll(data)
|
||||||
|
json.Unmarshal(bodyBytes, &groupSyncables)
|
||||||
|
return groupSyncables
|
||||||
|
}
|
||||||
@@ -42,6 +42,7 @@ type Customer struct {
|
|||||||
type Features struct {
|
type Features struct {
|
||||||
Users *int `json:"users"`
|
Users *int `json:"users"`
|
||||||
LDAP *bool `json:"ldap"`
|
LDAP *bool `json:"ldap"`
|
||||||
|
LDAPGroups *bool `json:"ldap_groups"`
|
||||||
MFA *bool `json:"mfa"`
|
MFA *bool `json:"mfa"`
|
||||||
GoogleOAuth *bool `json:"google_oauth"`
|
GoogleOAuth *bool `json:"google_oauth"`
|
||||||
Office365OAuth *bool `json:"office365_oauth"`
|
Office365OAuth *bool `json:"office365_oauth"`
|
||||||
@@ -66,6 +67,7 @@ type Features struct {
|
|||||||
func (f *Features) ToMap() map[string]interface{} {
|
func (f *Features) ToMap() map[string]interface{} {
|
||||||
return map[string]interface{}{
|
return map[string]interface{}{
|
||||||
"ldap": *f.LDAP,
|
"ldap": *f.LDAP,
|
||||||
|
"ldap_groups": *f.LDAPGroups,
|
||||||
"mfa": *f.MFA,
|
"mfa": *f.MFA,
|
||||||
"google": *f.GoogleOAuth,
|
"google": *f.GoogleOAuth,
|
||||||
"office365": *f.Office365OAuth,
|
"office365": *f.Office365OAuth,
|
||||||
@@ -96,6 +98,10 @@ func (f *Features) SetDefaults() {
|
|||||||
f.LDAP = NewBool(*f.FutureFeatures)
|
f.LDAP = NewBool(*f.FutureFeatures)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if f.LDAPGroups == nil {
|
||||||
|
f.LDAPGroups = NewBool(*f.FutureFeatures)
|
||||||
|
}
|
||||||
|
|
||||||
if f.MFA == nil {
|
if f.MFA == nil {
|
||||||
f.MFA = NewBool(*f.FutureFeatures)
|
f.MFA = NewBool(*f.FutureFeatures)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ func TestLicenseFeaturesToMap(t *testing.T) {
|
|||||||
m := f.ToMap()
|
m := f.ToMap()
|
||||||
|
|
||||||
CheckTrue(t, m["ldap"].(bool))
|
CheckTrue(t, m["ldap"].(bool))
|
||||||
|
CheckTrue(t, m["ldap_groups"].(bool))
|
||||||
CheckTrue(t, m["mfa"].(bool))
|
CheckTrue(t, m["mfa"].(bool))
|
||||||
CheckTrue(t, m["google"].(bool))
|
CheckTrue(t, m["google"].(bool))
|
||||||
CheckTrue(t, m["office365"].(bool))
|
CheckTrue(t, m["office365"].(bool))
|
||||||
@@ -37,6 +38,7 @@ func TestLicenseFeaturesSetDefaults(t *testing.T) {
|
|||||||
|
|
||||||
CheckInt(t, *f.Users, 0)
|
CheckInt(t, *f.Users, 0)
|
||||||
CheckTrue(t, *f.LDAP)
|
CheckTrue(t, *f.LDAP)
|
||||||
|
CheckTrue(t, *f.LDAPGroups)
|
||||||
CheckTrue(t, *f.MFA)
|
CheckTrue(t, *f.MFA)
|
||||||
CheckTrue(t, *f.GoogleOAuth)
|
CheckTrue(t, *f.GoogleOAuth)
|
||||||
CheckTrue(t, *f.Office365OAuth)
|
CheckTrue(t, *f.Office365OAuth)
|
||||||
@@ -58,6 +60,7 @@ func TestLicenseFeaturesSetDefaults(t *testing.T) {
|
|||||||
*f.Users = 300
|
*f.Users = 300
|
||||||
*f.FutureFeatures = false
|
*f.FutureFeatures = false
|
||||||
*f.LDAP = true
|
*f.LDAP = true
|
||||||
|
*f.LDAPGroups = true
|
||||||
*f.MFA = true
|
*f.MFA = true
|
||||||
*f.GoogleOAuth = true
|
*f.GoogleOAuth = true
|
||||||
*f.Office365OAuth = true
|
*f.Office365OAuth = true
|
||||||
@@ -76,6 +79,7 @@ func TestLicenseFeaturesSetDefaults(t *testing.T) {
|
|||||||
|
|
||||||
CheckInt(t, *f.Users, 300)
|
CheckInt(t, *f.Users, 300)
|
||||||
CheckTrue(t, *f.LDAP)
|
CheckTrue(t, *f.LDAP)
|
||||||
|
CheckTrue(t, *f.LDAPGroups)
|
||||||
CheckTrue(t, *f.MFA)
|
CheckTrue(t, *f.MFA)
|
||||||
CheckTrue(t, *f.GoogleOAuth)
|
CheckTrue(t, *f.GoogleOAuth)
|
||||||
CheckTrue(t, *f.Office365OAuth)
|
CheckTrue(t, *f.Office365OAuth)
|
||||||
@@ -159,6 +163,7 @@ func TestLicenseToFromJson(t *testing.T) {
|
|||||||
|
|
||||||
CheckInt(t, *f1.Users, *f.Users)
|
CheckInt(t, *f1.Users, *f.Users)
|
||||||
CheckBool(t, *f1.LDAP, *f.LDAP)
|
CheckBool(t, *f1.LDAP, *f.LDAP)
|
||||||
|
CheckBool(t, *f1.LDAPGroups, *f.LDAPGroups)
|
||||||
CheckBool(t, *f1.MFA, *f.MFA)
|
CheckBool(t, *f1.MFA, *f.MFA)
|
||||||
CheckBool(t, *f1.GoogleOAuth, *f.GoogleOAuth)
|
CheckBool(t, *f1.GoogleOAuth, *f.GoogleOAuth)
|
||||||
CheckBool(t, *f1.Office365OAuth, *f.Office365OAuth)
|
CheckBool(t, *f1.Office365OAuth, *f.Office365OAuth)
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ type LayeredStore struct {
|
|||||||
LocalCacheLayer *LocalCacheSupplier
|
LocalCacheLayer *LocalCacheSupplier
|
||||||
RedisLayer *RedisSupplier
|
RedisLayer *RedisSupplier
|
||||||
LayerChainHead LayeredStoreSupplier
|
LayerChainHead LayeredStoreSupplier
|
||||||
|
GroupStore GroupStore
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLayeredStore(db LayeredStoreDatabaseLayer, metrics einterfaces.MetricsInterface, cluster einterfaces.ClusterInterface) Store {
|
func NewLayeredStore(db LayeredStoreDatabaseLayer, metrics einterfaces.MetricsInterface, cluster einterfaces.ClusterInterface) Store {
|
||||||
@@ -41,6 +42,7 @@ func NewLayeredStore(db LayeredStoreDatabaseLayer, metrics einterfaces.MetricsIn
|
|||||||
store.ReactionStore = &LayeredReactionStore{store}
|
store.ReactionStore = &LayeredReactionStore{store}
|
||||||
store.RoleStore = &LayeredRoleStore{store}
|
store.RoleStore = &LayeredRoleStore{store}
|
||||||
store.SchemeStore = &LayeredSchemeStore{store}
|
store.SchemeStore = &LayeredSchemeStore{store}
|
||||||
|
store.GroupStore = &LayeredGroupStore{store}
|
||||||
|
|
||||||
// Setup the chain
|
// Setup the chain
|
||||||
if ENABLE_EXPERIMENTAL_REDIS {
|
if ENABLE_EXPERIMENTAL_REDIS {
|
||||||
@@ -181,6 +183,10 @@ func (s *LayeredStore) Scheme() SchemeStore {
|
|||||||
return s.SchemeStore
|
return s.SchemeStore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *LayeredStore) Group() GroupStore {
|
||||||
|
return s.GroupStore
|
||||||
|
}
|
||||||
|
|
||||||
func (s *LayeredStore) MarkSystemRanUnitTests() {
|
func (s *LayeredStore) MarkSystemRanUnitTests() {
|
||||||
s.DatabaseLayer.MarkSystemRanUnitTests()
|
s.DatabaseLayer.MarkSystemRanUnitTests()
|
||||||
}
|
}
|
||||||
@@ -333,3 +339,115 @@ func (s *LayeredSchemeStore) PermanentDeleteAll() StoreChannel {
|
|||||||
return supplier.SchemePermanentDeleteAll(s.TmpContext)
|
return supplier.SchemePermanentDeleteAll(s.TmpContext)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LayeredGroupStore struct {
|
||||||
|
*LayeredStore
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LayeredGroupStore) Create(group *model.Group) StoreChannel {
|
||||||
|
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
|
||||||
|
return supplier.GroupCreate(s.TmpContext, group)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LayeredGroupStore) Get(groupID string) StoreChannel {
|
||||||
|
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
|
||||||
|
return supplier.GroupGet(s.TmpContext, groupID)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LayeredGroupStore) GetByRemoteID(remoteID string, groupSource model.GroupSource) StoreChannel {
|
||||||
|
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
|
||||||
|
return supplier.GroupGetByRemoteID(s.TmpContext, remoteID, groupSource)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LayeredGroupStore) GetAllBySource(groupSource model.GroupSource) StoreChannel {
|
||||||
|
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
|
||||||
|
return supplier.GroupGetAllBySource(s.TmpContext, groupSource)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LayeredGroupStore) Update(group *model.Group) StoreChannel {
|
||||||
|
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
|
||||||
|
return supplier.GroupUpdate(s.TmpContext, group)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LayeredGroupStore) Delete(groupID string) StoreChannel {
|
||||||
|
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
|
||||||
|
return supplier.GroupDelete(s.TmpContext, groupID)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LayeredGroupStore) GetMemberUsers(groupID string) StoreChannel {
|
||||||
|
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
|
||||||
|
return supplier.GroupGetMemberUsers(s.TmpContext, groupID)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LayeredGroupStore) GetMemberUsersPage(groupID string, offset int, limit int) StoreChannel {
|
||||||
|
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
|
||||||
|
return supplier.GroupGetMemberUsersPage(s.TmpContext, groupID, offset, limit)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LayeredGroupStore) GetMemberCount(groupID string) StoreChannel {
|
||||||
|
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
|
||||||
|
return supplier.GroupGetMemberCount(s.TmpContext, groupID)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LayeredGroupStore) CreateOrRestoreMember(groupID string, userID string) StoreChannel {
|
||||||
|
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
|
||||||
|
return supplier.GroupCreateOrRestoreMember(s.TmpContext, groupID, userID)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LayeredGroupStore) DeleteMember(groupID string, userID string) StoreChannel {
|
||||||
|
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
|
||||||
|
return supplier.GroupDeleteMember(s.TmpContext, groupID, userID)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LayeredGroupStore) CreateGroupSyncable(groupSyncable *model.GroupSyncable) StoreChannel {
|
||||||
|
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
|
||||||
|
return supplier.GroupCreateGroupSyncable(s.TmpContext, groupSyncable)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LayeredGroupStore) GetGroupSyncable(groupID string, syncableID string, syncableType model.GroupSyncableType) StoreChannel {
|
||||||
|
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
|
||||||
|
return supplier.GroupGetGroupSyncable(s.TmpContext, groupID, syncableID, syncableType)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LayeredGroupStore) GetAllGroupSyncablesByGroupId(groupID string, syncableType model.GroupSyncableType) StoreChannel {
|
||||||
|
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
|
||||||
|
return supplier.GroupGetAllGroupSyncablesByGroup(s.TmpContext, groupID, syncableType)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LayeredGroupStore) UpdateGroupSyncable(groupSyncable *model.GroupSyncable) StoreChannel {
|
||||||
|
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
|
||||||
|
return supplier.GroupUpdateGroupSyncable(s.TmpContext, groupSyncable)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LayeredGroupStore) DeleteGroupSyncable(groupID string, syncableID string, syncableType model.GroupSyncableType) StoreChannel {
|
||||||
|
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
|
||||||
|
return supplier.GroupDeleteGroupSyncable(s.TmpContext, groupID, syncableID, syncableType)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LayeredGroupStore) PendingAutoAddTeamMembers(minGroupMembersCreateAt int64) StoreChannel {
|
||||||
|
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
|
||||||
|
return supplier.PendingAutoAddTeamMembers(s.TmpContext, minGroupMembersCreateAt)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LayeredGroupStore) PendingAutoAddChannelMembers(minGroupMembersCreateAt int64) StoreChannel {
|
||||||
|
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
|
||||||
|
return supplier.PendingAutoAddChannelMembers(s.TmpContext, minGroupMembersCreateAt)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -46,4 +46,27 @@ type LayeredStoreSupplier interface {
|
|||||||
SchemeDelete(ctx context.Context, schemeId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
SchemeDelete(ctx context.Context, schemeId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||||
SchemeGetAllPage(ctx context.Context, scope string, offset int, limit int, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
SchemeGetAllPage(ctx context.Context, scope string, offset int, limit int, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||||
SchemePermanentDeleteAll(ctx context.Context, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
SchemePermanentDeleteAll(ctx context.Context, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||||
|
|
||||||
|
// Groups
|
||||||
|
GroupCreate(ctx context.Context, group *model.Group, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||||
|
GroupGet(ctx context.Context, groupID string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||||
|
GroupGetByRemoteID(ctx context.Context, remoteID string, groupSource model.GroupSource, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||||
|
GroupGetAllBySource(ctx context.Context, groupSource model.GroupSource, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||||
|
GroupUpdate(ctx context.Context, group *model.Group, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||||
|
GroupDelete(ctx context.Context, groupID string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||||
|
|
||||||
|
GroupGetMemberUsers(ctx context.Context, groupID string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||||
|
GroupGetMemberUsersPage(ctx context.Context, groupID string, offset int, limit int, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||||
|
GroupGetMemberCount(ctx context.Context, groupID string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||||
|
GroupCreateOrRestoreMember(ctx context.Context, groupID string, userID string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||||
|
GroupDeleteMember(ctx context.Context, groupID string, userID string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||||
|
|
||||||
|
GroupCreateGroupSyncable(ctx context.Context, groupSyncable *model.GroupSyncable, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||||
|
GroupGetGroupSyncable(ctx context.Context, groupID string, syncableID string, syncableType model.GroupSyncableType, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||||
|
GroupGetAllGroupSyncablesByGroup(ctx context.Context, groupID string, syncableType model.GroupSyncableType, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||||
|
GroupUpdateGroupSyncable(ctx context.Context, groupSyncable *model.GroupSyncable, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||||
|
GroupDeleteGroupSyncable(ctx context.Context, groupID string, syncableID string, syncableType model.GroupSyncableType, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||||
|
|
||||||
|
PendingAutoAddTeamMembers(ctx context.Context, minGroupMembersCreateAt int64, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||||
|
PendingAutoAddChannelMembers(ctx context.Context, minGroupMembersCreateAt int64, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ const (
|
|||||||
SCHEME_CACHE_SIZE = 20000
|
SCHEME_CACHE_SIZE = 20000
|
||||||
SCHEME_CACHE_SEC = 30 * 60
|
SCHEME_CACHE_SEC = 30 * 60
|
||||||
|
|
||||||
|
GROUP_CACHE_SIZE = 20000
|
||||||
|
GROUP_CACHE_SEC = 30 * 60
|
||||||
|
|
||||||
CLEAR_CACHE_MESSAGE_DATA = ""
|
CLEAR_CACHE_MESSAGE_DATA = ""
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -31,6 +34,7 @@ type LocalCacheSupplier struct {
|
|||||||
schemeCache *utils.Cache
|
schemeCache *utils.Cache
|
||||||
metrics einterfaces.MetricsInterface
|
metrics einterfaces.MetricsInterface
|
||||||
cluster einterfaces.ClusterInterface
|
cluster einterfaces.ClusterInterface
|
||||||
|
groupCache *utils.Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
// Caching Interface
|
// Caching Interface
|
||||||
@@ -50,6 +54,7 @@ func NewLocalCacheSupplier(metrics einterfaces.MetricsInterface, cluster einterf
|
|||||||
reactionCache: utils.NewLruWithParams(REACTION_CACHE_SIZE, "Reaction", REACTION_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_REACTIONS),
|
reactionCache: utils.NewLruWithParams(REACTION_CACHE_SIZE, "Reaction", REACTION_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_REACTIONS),
|
||||||
roleCache: utils.NewLruWithParams(ROLE_CACHE_SIZE, "Role", ROLE_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES),
|
roleCache: utils.NewLruWithParams(ROLE_CACHE_SIZE, "Role", ROLE_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES),
|
||||||
schemeCache: utils.NewLruWithParams(SCHEME_CACHE_SIZE, "Scheme", SCHEME_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_SCHEMES),
|
schemeCache: utils.NewLruWithParams(SCHEME_CACHE_SIZE, "Scheme", SCHEME_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_SCHEMES),
|
||||||
|
groupCache: utils.NewLruWithParams(GROUP_CACHE_SIZE, "Group", GROUP_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_GROUPS),
|
||||||
metrics: metrics,
|
metrics: metrics,
|
||||||
cluster: cluster,
|
cluster: cluster,
|
||||||
}
|
}
|
||||||
@@ -57,6 +62,7 @@ func NewLocalCacheSupplier(metrics einterfaces.MetricsInterface, cluster einterf
|
|||||||
if cluster != nil {
|
if cluster != nil {
|
||||||
cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_REACTIONS, supplier.handleClusterInvalidateReaction)
|
cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_REACTIONS, supplier.handleClusterInvalidateReaction)
|
||||||
cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES, supplier.handleClusterInvalidateRole)
|
cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES, supplier.handleClusterInvalidateRole)
|
||||||
|
cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_GROUPS, supplier.handleClusterInvalidateGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
return supplier
|
return supplier
|
||||||
|
|||||||
102
store/local_cache_supplier_groups.go
Обычный файл
102
store/local_cache_supplier_groups.go
Обычный файл
@@ -0,0 +1,102 @@
|
|||||||
|
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/mattermost/mattermost-server/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *LocalCacheSupplier) handleClusterInvalidateGroup(msg *model.ClusterMessage) {
|
||||||
|
if msg.Data == CLEAR_CACHE_MESSAGE_DATA {
|
||||||
|
s.groupCache.Purge()
|
||||||
|
} else {
|
||||||
|
s.groupCache.Remove(msg.Data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LocalCacheSupplier) GroupCreate(ctx context.Context, group *model.Group, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
return s.Next().GroupCreate(ctx, group, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LocalCacheSupplier) GroupGet(ctx context.Context, groupID string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
if result := s.doStandardReadCache(ctx, s.groupCache, groupID, hints...); result != nil {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
result := s.Next().GroupGet(ctx, groupID, hints...)
|
||||||
|
|
||||||
|
s.doStandardAddToCache(ctx, s.groupCache, groupID, result, hints...)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LocalCacheSupplier) GroupGetByRemoteID(ctx context.Context, remoteID string, groupSource model.GroupSource, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
return s.Next().GroupGetByRemoteID(ctx, remoteID, groupSource, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LocalCacheSupplier) GroupGetAllBySource(ctx context.Context, groupSource model.GroupSource, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
return s.Next().GroupGetAllBySource(ctx, groupSource, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LocalCacheSupplier) GroupUpdate(ctx context.Context, group *model.Group, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
defer s.doInvalidateCacheCluster(s.groupCache, group.Id)
|
||||||
|
return s.Next().GroupUpdate(ctx, group, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LocalCacheSupplier) GroupDelete(ctx context.Context, groupID string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
defer s.doInvalidateCacheCluster(s.groupCache, groupID)
|
||||||
|
defer s.doClearCacheCluster(s.groupCache)
|
||||||
|
|
||||||
|
return s.Next().GroupDelete(ctx, groupID, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LocalCacheSupplier) GroupGetMemberUsers(ctx context.Context, groupID string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
return s.Next().GroupGetMemberUsers(ctx, groupID, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LocalCacheSupplier) GroupGetMemberUsersPage(ctx context.Context, groupID string, offset int, limit int, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
return s.Next().GroupGetMemberUsersPage(ctx, groupID, offset, limit, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LocalCacheSupplier) GroupGetMemberCount(ctx context.Context, groupID string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
return s.Next().GroupGetMemberCount(ctx, groupID, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LocalCacheSupplier) GroupCreateOrRestoreMember(ctx context.Context, groupID string, userID string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
return s.Next().GroupCreateOrRestoreMember(ctx, groupID, userID, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LocalCacheSupplier) GroupDeleteMember(ctx context.Context, groupID string, userID string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
return s.Next().GroupDeleteMember(ctx, groupID, userID, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LocalCacheSupplier) GroupCreateGroupSyncable(ctx context.Context, groupSyncable *model.GroupSyncable, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
return s.Next().GroupCreateGroupSyncable(ctx, groupSyncable, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LocalCacheSupplier) GroupGetGroupSyncable(ctx context.Context, groupID string, syncableID string, syncableType model.GroupSyncableType, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
return s.Next().GroupGetGroupSyncable(ctx, groupID, syncableID, syncableType, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LocalCacheSupplier) GroupGetAllGroupSyncablesByGroup(ctx context.Context, groupID string, syncableType model.GroupSyncableType, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
return s.Next().GroupGetAllGroupSyncablesByGroup(ctx, groupID, syncableType, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LocalCacheSupplier) GroupUpdateGroupSyncable(ctx context.Context, groupSyncable *model.GroupSyncable, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
return s.Next().GroupUpdateGroupSyncable(ctx, groupSyncable, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LocalCacheSupplier) GroupDeleteGroupSyncable(ctx context.Context, groupID string, syncableID string, syncableType model.GroupSyncableType, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
return s.Next().GroupDeleteGroupSyncable(ctx, groupID, syncableID, syncableType, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LocalCacheSupplier) PendingAutoAddTeamMembers(ctx context.Context, minGroupMembersCreateAt int64, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
return s.Next().PendingAutoAddTeamMembers(ctx, minGroupMembersCreateAt, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LocalCacheSupplier) PendingAutoAddChannelMembers(ctx context.Context, minGroupMembersCreateAt int64, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
return s.Next().PendingAutoAddChannelMembers(ctx, minGroupMembersCreateAt, hints...)
|
||||||
|
}
|
||||||
@@ -18,7 +18,7 @@ func (s *LocalCacheSupplier) handleClusterInvalidateRole(msg *model.ClusterMessa
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *LocalCacheSupplier) RoleSave(ctx context.Context, role *model.Role, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
func (s *LocalCacheSupplier) RoleSave(ctx context.Context, role *model.Role, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
if len(role.Id) != 0 {
|
if len(role.Name) != 0 {
|
||||||
defer s.doInvalidateCacheCluster(s.roleCache, role.Name)
|
defer s.doInvalidateCacheCluster(s.roleCache, role.Name)
|
||||||
}
|
}
|
||||||
return s.Next().RoleSave(ctx, role, hints...)
|
return s.Next().RoleSave(ctx, role, hints...)
|
||||||
|
|||||||
100
store/redis_supplier_groups.go
Обычный файл
100
store/redis_supplier_groups.go
Обычный файл
@@ -0,0 +1,100 @@
|
|||||||
|
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/mattermost/mattermost-server/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *RedisSupplier) GroupCreate(ctx context.Context, group *model.Group, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
// TODO: Redis caching.
|
||||||
|
return s.Next().GroupCreate(ctx, group, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RedisSupplier) GroupGet(ctx context.Context, groupID string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
// TODO: Redis caching.
|
||||||
|
return s.Next().GroupGet(ctx, groupID, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RedisSupplier) GroupGetByRemoteID(ctx context.Context, remoteID string, groupSource model.GroupSource, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
// TODO: Redis caching.
|
||||||
|
return s.Next().GroupGetByRemoteID(ctx, remoteID, groupSource, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RedisSupplier) GroupGetAllBySource(ctx context.Context, groupSource model.GroupSource, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
// TODO: Redis caching.
|
||||||
|
return s.Next().GroupGetAllBySource(ctx, groupSource, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RedisSupplier) GroupUpdate(ctx context.Context, group *model.Group, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
// TODO: Redis caching.
|
||||||
|
return s.Next().GroupUpdate(ctx, group, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RedisSupplier) GroupDelete(ctx context.Context, groupID string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
// TODO: Redis caching.
|
||||||
|
return s.Next().GroupDelete(ctx, groupID, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RedisSupplier) GroupGetMemberUsers(ctx context.Context, groupID string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
// TODO: Redis caching.
|
||||||
|
return s.Next().GroupGetMemberUsers(ctx, groupID, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RedisSupplier) GroupGetMemberUsersPage(ctx context.Context, groupID string, offset int, limit int, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
// TODO: Redis caching.
|
||||||
|
return s.Next().GroupGetMemberUsersPage(ctx, groupID, offset, limit, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RedisSupplier) GroupGetMemberCount(ctx context.Context, groupID string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
// TODO: Redis caching.
|
||||||
|
return s.Next().GroupGetMemberCount(ctx, groupID, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RedisSupplier) GroupCreateOrRestoreMember(ctx context.Context, groupID string, userID string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
// TODO: Redis caching.
|
||||||
|
return s.Next().GroupCreateOrRestoreMember(ctx, groupID, userID, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RedisSupplier) GroupDeleteMember(ctx context.Context, groupID string, userID string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
// TODO: Redis caching.
|
||||||
|
return s.Next().GroupDeleteMember(ctx, groupID, userID, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RedisSupplier) GroupCreateGroupSyncable(ctx context.Context, groupSyncable *model.GroupSyncable, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
// TODO: Redis caching.
|
||||||
|
return s.Next().GroupCreateGroupSyncable(ctx, groupSyncable, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RedisSupplier) GroupGetGroupSyncable(ctx context.Context, groupID string, syncableID string, syncableType model.GroupSyncableType, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
// TODO: Redis caching.
|
||||||
|
return s.Next().GroupGetGroupSyncable(ctx, groupID, syncableID, syncableType, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RedisSupplier) GroupGetAllGroupSyncablesByGroup(ctx context.Context, groupID string, syncableType model.GroupSyncableType, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
// TODO: Redis caching.
|
||||||
|
return s.Next().GroupGetAllGroupSyncablesByGroup(ctx, groupID, syncableType, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RedisSupplier) GroupUpdateGroupSyncable(ctx context.Context, groupSyncable *model.GroupSyncable, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
// TODO: Redis caching.
|
||||||
|
return s.Next().GroupUpdateGroupSyncable(ctx, groupSyncable, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RedisSupplier) GroupDeleteGroupSyncable(ctx context.Context, groupID string, syncableID string, syncableType model.GroupSyncableType, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
// TODO: Redis caching.
|
||||||
|
return s.Next().GroupDeleteGroupSyncable(ctx, groupID, syncableID, syncableType, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RedisSupplier) PendingAutoAddTeamMembers(ctx context.Context, minGroupMembersCreateAt int64, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
// TODO: Redis caching.
|
||||||
|
return s.Next().PendingAutoAddTeamMembers(ctx, minGroupMembersCreateAt, hints...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RedisSupplier) PendingAutoAddChannelMembers(ctx context.Context, minGroupMembersCreateAt int64, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||||
|
// TODO: Redis caching.
|
||||||
|
return s.Next().PendingAutoAddChannelMembers(ctx, minGroupMembersCreateAt, hints...)
|
||||||
|
}
|
||||||
@@ -955,6 +955,27 @@ func (s SqlChannelStore) GetChannels(teamId string, userId string, includeDelete
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s SqlChannelStore) GetAllChannels(offset int, limit int, includeDeleted bool) store.StoreChannel {
|
||||||
|
return store.Do(func(result *store.StoreResult) {
|
||||||
|
deleteFilter := "AND c.DeleteAt = 0"
|
||||||
|
if includeDeleted {
|
||||||
|
deleteFilter = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
query := "SELECT c.*, Teams.DisplayName AS TeamDisplayName, Teams.Name AS TeamName, Teams.UpdateAt as TeamUpdateAt FROM Channels AS c JOIN Teams ON Teams.Id = c.TeamId WHERE (c.Type = 'P' OR c.Type = 'O') " + deleteFilter + " ORDER BY c.DisplayName, Teams.DisplayName LIMIT :Limit OFFSET :Offset"
|
||||||
|
|
||||||
|
data := &model.ChannelListWithTeamData{}
|
||||||
|
_, err := s.GetReplica().Select(data, query, map[string]interface{}{"Limit": limit, "Offset": offset})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlChannelStore.GetAllChannels", "store.sql_channel.get_all_channels.get.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Data = data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (s SqlChannelStore) GetMoreChannels(teamId string, userId string, offset int, limit int) store.StoreChannel {
|
func (s SqlChannelStore) GetMoreChannels(teamId string, userId string, offset int, limit int) store.StoreChannel {
|
||||||
return store.Do(func(result *store.StoreResult) {
|
return store.Do(func(result *store.StoreResult) {
|
||||||
data := &model.ChannelList{}
|
data := &model.ChannelList{}
|
||||||
@@ -2072,6 +2093,36 @@ func (s SqlChannelStore) SearchInTeam(teamId string, term string, includeDeleted
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s SqlChannelStore) SearchAllChannels(term string, includeDeleted bool) store.StoreChannel {
|
||||||
|
return store.Do(func(result *store.StoreResult) {
|
||||||
|
parameters := map[string]interface{}{}
|
||||||
|
deleteFilter := "AND c.DeleteAt = 0"
|
||||||
|
if includeDeleted {
|
||||||
|
deleteFilter = ""
|
||||||
|
}
|
||||||
|
searchQuery := `SELECT c.*, t.DisplayName AS TeamDisplayName, t.Name AS TeamName, t.UpdateAt as TeamUpdateAt FROM Channels AS c JOIN Teams AS t ON t.Id = c.TeamId WHERE (c.Type = 'P' OR c.Type = 'O') ` + deleteFilter + ` SEARCH_CLAUSE ORDER BY c.DisplayName, t.DisplayName LIMIT 100`
|
||||||
|
|
||||||
|
likeClause, likeTerm := s.buildLIKEClause(term, "c.Name, c.DisplayName, c.Purpose")
|
||||||
|
if likeTerm == "" {
|
||||||
|
// If the likeTerm is empty after preparing, then don't bother searching.
|
||||||
|
searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "", 1)
|
||||||
|
} else {
|
||||||
|
parameters["LikeTerm"] = likeTerm
|
||||||
|
fulltextClause, fulltextTerm := s.buildFulltextClause(term, "c.Name, c.DisplayName, c.Purpose")
|
||||||
|
parameters["FulltextTerm"] = fulltextTerm
|
||||||
|
searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "AND ("+likeClause+" OR "+fulltextClause+")", 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
var channels model.ChannelListWithTeamData
|
||||||
|
|
||||||
|
if _, err := s.GetReplica().Select(&channels, searchQuery, parameters); err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlChannelStore.Search", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Data = &channels
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (s SqlChannelStore) SearchMore(userId string, teamId string, term string) store.StoreChannel {
|
func (s SqlChannelStore) SearchMore(userId string, teamId string, term string) store.StoreChannel {
|
||||||
return store.Do(func(result *store.StoreResult) {
|
return store.Do(func(result *store.StoreResult) {
|
||||||
*result = s.performSearch(`
|
*result = s.performSearch(`
|
||||||
|
|||||||
14
store/sqlstore/group_store_test.go
Обычный файл
14
store/sqlstore/group_store_test.go
Обычный файл
@@ -0,0 +1,14 @@
|
|||||||
|
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package sqlstore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mattermost/mattermost-server/store/storetest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGroupStore(t *testing.T) {
|
||||||
|
StoreTest(t, storetest.TestGroupStore)
|
||||||
|
}
|
||||||
771
store/sqlstore/group_supplier.go
Обычный файл
771
store/sqlstore/group_supplier.go
Обычный файл
@@ -0,0 +1,771 @@
|
|||||||
|
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package sqlstore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/mattermost/mattermost-server/model"
|
||||||
|
"github.com/mattermost/mattermost-server/store"
|
||||||
|
)
|
||||||
|
|
||||||
|
type groupTeam struct {
|
||||||
|
model.GroupSyncable
|
||||||
|
TeamId string `db:"TeamId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type groupChannel struct {
|
||||||
|
model.GroupSyncable
|
||||||
|
ChannelId string `db:"ChannelId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type groupTeamJoin struct {
|
||||||
|
groupTeam
|
||||||
|
TeamDisplayName string `db:"TeamDisplayName"`
|
||||||
|
TeamType string `db:"TeamType"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type groupChannelJoin struct {
|
||||||
|
groupChannel
|
||||||
|
ChannelDisplayName string `db:"ChannelDisplayName"`
|
||||||
|
TeamDisplayName string `db:"TeamDisplayName"`
|
||||||
|
TeamType string `db:"TeamType"`
|
||||||
|
ChannelType string `db:"ChannelType"`
|
||||||
|
TeamID string `db:"TeamId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func initSqlSupplierGroups(sqlStore SqlStore) {
|
||||||
|
for _, db := range sqlStore.GetAllConns() {
|
||||||
|
groups := db.AddTableWithName(model.Group{}, "UserGroups").SetKeys(false, "Id")
|
||||||
|
groups.ColMap("Id").SetMaxSize(26)
|
||||||
|
groups.ColMap("Name").SetMaxSize(model.GroupNameMaxLength).SetUnique(true)
|
||||||
|
groups.ColMap("DisplayName").SetMaxSize(model.GroupDisplayNameMaxLength)
|
||||||
|
groups.ColMap("Description").SetMaxSize(model.GroupDescriptionMaxLength)
|
||||||
|
groups.ColMap("Source").SetMaxSize(model.GroupSourceMaxLength)
|
||||||
|
groups.ColMap("RemoteId").SetMaxSize(model.GroupRemoteIDMaxLength)
|
||||||
|
groups.SetUniqueTogether("Source", "RemoteId")
|
||||||
|
|
||||||
|
groupMembers := db.AddTableWithName(model.GroupMember{}, "GroupMembers").SetKeys(false, "GroupId", "UserId")
|
||||||
|
groupMembers.ColMap("GroupId").SetMaxSize(26)
|
||||||
|
groupMembers.ColMap("UserId").SetMaxSize(26)
|
||||||
|
|
||||||
|
groupTeams := db.AddTableWithName(groupTeam{}, "GroupTeams").SetKeys(false, "GroupId", "TeamId")
|
||||||
|
groupTeams.ColMap("GroupId").SetMaxSize(26)
|
||||||
|
groupTeams.ColMap("TeamId").SetMaxSize(26)
|
||||||
|
|
||||||
|
groupChannels := db.AddTableWithName(groupChannel{}, "GroupChannels").SetKeys(false, "GroupId", "ChannelId")
|
||||||
|
groupChannels.ColMap("GroupId").SetMaxSize(26)
|
||||||
|
groupChannels.ColMap("ChannelId").SetMaxSize(26)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SqlSupplier) CreateIndexesIfNotExistsGroups() {
|
||||||
|
s.CreateIndexIfNotExists("idx_groupmembers_create_at", "GroupMembers", "CreateAt")
|
||||||
|
s.CreateIndexIfNotExists("idx_usergroups_remote_id", "UserGroups", "RemoteId")
|
||||||
|
s.CreateIndexIfNotExists("idx_usergroups_delete_at", "UserGroups", "DeleteAt")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SqlSupplier) GroupCreate(ctx context.Context, group *model.Group, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
result := store.NewSupplierResult()
|
||||||
|
|
||||||
|
if len(group.Id) != 0 {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupCreate", "model.group.id.app_error", nil, "", http.StatusBadRequest)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := group.IsValidForCreate(); err != nil {
|
||||||
|
result.Err = err
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
group.Id = model.NewId()
|
||||||
|
group.CreateAt = model.GetMillis()
|
||||||
|
group.UpdateAt = group.CreateAt
|
||||||
|
|
||||||
|
if err := s.GetMaster().Insert(group); err != nil {
|
||||||
|
if IsUniqueConstraintError(err, []string{"Name", "groups_name_key"}) {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupCreate", "store.sql_group.unique_constraint", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
} else {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupCreate", "store.insert_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Data = group
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SqlSupplier) GroupGet(ctx context.Context, groupId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
result := store.NewSupplierResult()
|
||||||
|
|
||||||
|
var group *model.Group
|
||||||
|
if err := s.GetReplica().SelectOne(&group, "SELECT * from UserGroups WHERE Id = :Id", map[string]interface{}{"Id": groupId}); err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupGet", "store.sql_group.no_rows", nil, err.Error(), http.StatusNotFound)
|
||||||
|
} else {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupGet", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Data = group
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SqlSupplier) GroupGetByRemoteID(ctx context.Context, remoteID string, groupSource model.GroupSource, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
result := store.NewSupplierResult()
|
||||||
|
|
||||||
|
var group *model.Group
|
||||||
|
if err := s.GetReplica().SelectOne(&group, "SELECT * from UserGroups WHERE RemoteId = :RemoteId AND Source = :Source", map[string]interface{}{"RemoteId": remoteID, "Source": groupSource}); err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupGetByRemoteID", "store.sql_group.no_rows", nil, err.Error(), http.StatusNotFound)
|
||||||
|
} else {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupGetByRemoteID", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Data = group
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SqlSupplier) GroupGetAllBySource(ctx context.Context, groupSource model.GroupSource, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
result := store.NewSupplierResult()
|
||||||
|
|
||||||
|
var groups []*model.Group
|
||||||
|
|
||||||
|
if _, err := s.GetReplica().Select(&groups, "SELECT * from UserGroups WHERE DeleteAt = 0 AND Source = :Source", map[string]interface{}{"Source": groupSource}); err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupGetAllBySource", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Data = groups
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SqlSupplier) GroupUpdate(ctx context.Context, group *model.Group, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
result := store.NewSupplierResult()
|
||||||
|
|
||||||
|
var retrievedGroup *model.Group
|
||||||
|
if err := s.GetMaster().SelectOne(&retrievedGroup, "SELECT * FROM UserGroups WHERE Id = :Id", map[string]interface{}{"Id": group.Id}); err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupUpdate", "store.sql_group.no_rows", nil, "id="+group.Id+","+err.Error(), http.StatusNotFound)
|
||||||
|
} else {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupUpdate", "store.select_error", nil, "id="+group.Id+","+err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// If updating DeleteAt it can only be to 0
|
||||||
|
if group.DeleteAt != retrievedGroup.DeleteAt && group.DeleteAt != 0 {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupUpdate", "model.group.delete_at.app_error", nil, "", http.StatusInternalServerError)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset these properties, don't update them based on input
|
||||||
|
group.CreateAt = retrievedGroup.CreateAt
|
||||||
|
group.UpdateAt = model.GetMillis()
|
||||||
|
|
||||||
|
if err := group.IsValidForUpdate(); err != nil {
|
||||||
|
result.Err = err
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
rowsChanged, err := s.GetMaster().Update(group)
|
||||||
|
if err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupUpdate", "store.update_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
if rowsChanged != 1 {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupUpdate", "store.sql_group.no_rows_changed", nil, "", http.StatusInternalServerError)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Data = group
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SqlSupplier) GroupDelete(ctx context.Context, groupID string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
result := store.NewSupplierResult()
|
||||||
|
|
||||||
|
var group *model.Group
|
||||||
|
if err := s.GetReplica().SelectOne(&group, "SELECT * from UserGroups WHERE Id = :Id AND DeleteAt = 0", map[string]interface{}{"Id": groupID}); err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupDelete", "store.sql_group.no_rows", nil, "Id="+groupID+", "+err.Error(), http.StatusNotFound)
|
||||||
|
} else {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupDelete", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
time := model.GetMillis()
|
||||||
|
group.DeleteAt = time
|
||||||
|
group.UpdateAt = time
|
||||||
|
|
||||||
|
if _, err := s.GetMaster().Update(group); err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupDelete", "store.update_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Data = group
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SqlSupplier) GroupGetMemberUsers(stc context.Context, groupID string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
result := store.NewSupplierResult()
|
||||||
|
|
||||||
|
var groupMembers []*model.User
|
||||||
|
|
||||||
|
query := `
|
||||||
|
SELECT
|
||||||
|
Users.*
|
||||||
|
FROM
|
||||||
|
GroupMembers
|
||||||
|
JOIN Users ON Users.Id = GroupMembers.UserId
|
||||||
|
WHERE
|
||||||
|
GroupMembers.DeleteAt = 0
|
||||||
|
AND Users.DeleteAt = 0
|
||||||
|
AND GroupId = :GroupId`
|
||||||
|
|
||||||
|
if _, err := s.GetReplica().Select(&groupMembers, query, map[string]interface{}{"GroupId": groupID}); err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupGetAllBySource", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Data = groupMembers
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SqlSupplier) GroupGetMemberUsersPage(stc context.Context, groupID string, offset int, limit int, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
result := store.NewSupplierResult()
|
||||||
|
|
||||||
|
var groupMembers []*model.User
|
||||||
|
|
||||||
|
query := `
|
||||||
|
SELECT
|
||||||
|
Users.*
|
||||||
|
FROM
|
||||||
|
GroupMembers
|
||||||
|
JOIN Users ON Users.Id = GroupMembers.UserId
|
||||||
|
WHERE
|
||||||
|
GroupMembers.DeleteAt = 0
|
||||||
|
AND Users.DeleteAt = 0
|
||||||
|
AND GroupId = :GroupId
|
||||||
|
ORDER BY
|
||||||
|
GroupMembers.CreateAt DESC
|
||||||
|
LIMIT
|
||||||
|
:Limit
|
||||||
|
OFFSET
|
||||||
|
:Offset`
|
||||||
|
|
||||||
|
if _, err := s.GetReplica().Select(&groupMembers, query, map[string]interface{}{"GroupId": groupID, "Limit": limit, "Offset": offset}); err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupGetMemberUsersPage", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Data = groupMembers
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SqlSupplier) GroupGetMemberCount(stc context.Context, groupID string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
result := store.NewSupplierResult()
|
||||||
|
|
||||||
|
var count int64
|
||||||
|
var err error
|
||||||
|
|
||||||
|
query := `
|
||||||
|
SELECT
|
||||||
|
count(*)
|
||||||
|
FROM
|
||||||
|
GroupMembers
|
||||||
|
WHERE
|
||||||
|
GroupMembers.GroupId = :GroupId`
|
||||||
|
|
||||||
|
if count, err = s.GetReplica().SelectInt(query, map[string]interface{}{"GroupId": groupID}); err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupGetMemberUsersPage", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Data = count
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SqlSupplier) GroupCreateOrRestoreMember(ctx context.Context, groupID string, userID string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
result := store.NewSupplierResult()
|
||||||
|
|
||||||
|
member := &model.GroupMember{
|
||||||
|
GroupId: groupID,
|
||||||
|
UserId: userID,
|
||||||
|
CreateAt: model.GetMillis(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.Err = member.IsValid(); result.Err != nil {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
var retrievedGroup *model.Group
|
||||||
|
if err := s.GetMaster().SelectOne(&retrievedGroup, "SELECT * FROM UserGroups WHERE Id = :Id", map[string]interface{}{"Id": groupID}); err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupCreateOrRestoreMember", "store.insert_error", nil, "group_id="+member.GroupId+"user_id="+member.UserId+","+err.Error(), http.StatusInternalServerError)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
var retrievedMember *model.GroupMember
|
||||||
|
if err := s.GetMaster().SelectOne(&retrievedMember, "SELECT * FROM GroupMembers WHERE GroupId = :GroupId AND UserId = :UserId", map[string]interface{}{"GroupId": member.GroupId, "UserId": member.UserId}); err != nil {
|
||||||
|
if err != sql.ErrNoRows {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupCreateOrRestoreMember", "store.select_error", nil, "group_id="+member.GroupId+"user_id="+member.UserId+","+err.Error(), http.StatusInternalServerError)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if retrievedMember != nil && retrievedMember.DeleteAt == 0 {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupCreateOrRestoreMember", "store.sql_group.uniqueness_error", nil, "group_id="+member.GroupId+", user_id="+member.UserId, http.StatusBadRequest)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
if retrievedMember == nil {
|
||||||
|
if err := s.GetMaster().Insert(member); err != nil {
|
||||||
|
if IsUniqueConstraintError(err, []string{"GroupId", "UserId", "groupmembers_pkey", "PRIMARY"}) {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupCreateOrRestoreMember", "store.sql_group.uniqueness_error", nil, "group_id="+member.GroupId+", user_id="+member.UserId+", "+err.Error(), http.StatusBadRequest)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupCreateOrRestoreMember", "store.insert_error", nil, "group_id="+member.GroupId+", user_id="+member.UserId+", "+err.Error(), http.StatusInternalServerError)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
member.DeleteAt = 0
|
||||||
|
var rowsChanged int64
|
||||||
|
var err error
|
||||||
|
if rowsChanged, err = s.GetMaster().Update(member); err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupCreateOrRestoreMember", "store.update_error", nil, "group_id="+member.GroupId+", user_id="+member.UserId+", "+err.Error(), http.StatusInternalServerError)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
if rowsChanged != 1 {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupCreateOrRestoreMember", "store.sql_group.no_rows_changed", nil, "", http.StatusInternalServerError)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Data = member
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SqlSupplier) GroupDeleteMember(ctx context.Context, groupID string, userID string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
result := store.NewSupplierResult()
|
||||||
|
|
||||||
|
var retrievedMember *model.GroupMember
|
||||||
|
if err := s.GetMaster().SelectOne(&retrievedMember, "SELECT * FROM GroupMembers WHERE GroupId = :GroupId AND UserId = :UserId AND DeleteAt = 0", map[string]interface{}{"GroupId": groupID, "UserId": userID}); err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupDeleteMember", "store.sql_group.no_rows", nil, "group_id="+groupID+"user_id="+userID+","+err.Error(), http.StatusNotFound)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupDeleteMember", "store.select_error", nil, "group_id="+groupID+"user_id="+userID+","+err.Error(), http.StatusInternalServerError)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
retrievedMember.DeleteAt = model.GetMillis()
|
||||||
|
|
||||||
|
if _, err := s.GetMaster().Update(retrievedMember); err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupDeleteMember", "store.update_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Data = retrievedMember
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SqlSupplier) GroupCreateGroupSyncable(ctx context.Context, groupSyncable *model.GroupSyncable, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
result := store.NewSupplierResult()
|
||||||
|
|
||||||
|
if err := groupSyncable.IsValid(); err != nil {
|
||||||
|
result.Err = err
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset values that shouldn't be updatable by parameter
|
||||||
|
groupSyncable.DeleteAt = 0
|
||||||
|
groupSyncable.CreateAt = model.GetMillis()
|
||||||
|
groupSyncable.UpdateAt = groupSyncable.CreateAt
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch groupSyncable.Type {
|
||||||
|
case model.GroupSyncableTypeTeam:
|
||||||
|
teamResult := <-s.Team().Get(groupSyncable.SyncableId)
|
||||||
|
if teamResult.Err != nil {
|
||||||
|
result.Err = teamResult.Err
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.GetMaster().Insert(groupSyncableToGroupTeam(groupSyncable))
|
||||||
|
case model.GroupSyncableTypeChannel:
|
||||||
|
channelResult := <-s.Channel().Get(groupSyncable.SyncableId, false)
|
||||||
|
if channelResult.Err != nil {
|
||||||
|
result.Err = channelResult.Err
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.GetMaster().Insert(groupSyncableToGroupChannel(groupSyncable))
|
||||||
|
default:
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupCreateGroupSyncable", "model.group_syncable.type.app_error", nil, "group_id="+groupSyncable.GroupId+", syncable_id="+groupSyncable.SyncableId+", "+err.Error(), http.StatusInternalServerError)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupCreateGroupSyncable", "store.insert_error", nil, "group_id="+groupSyncable.GroupId+", syncable_id="+groupSyncable.SyncableId+", "+err.Error(), http.StatusInternalServerError)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Data = groupSyncable
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SqlSupplier) GroupGetGroupSyncable(ctx context.Context, groupID string, syncableID string, syncableType model.GroupSyncableType, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
result := store.NewSupplierResult()
|
||||||
|
|
||||||
|
groupSyncable, err := s.getGroupSyncable(groupID, syncableID, syncableType)
|
||||||
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupGetGroupSyncable", "store.sql_group.no_rows", nil, err.Error(), http.StatusNotFound)
|
||||||
|
} else {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupGetGroupSyncable", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Data = groupSyncable
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SqlSupplier) getGroupSyncable(groupID string, syncableID string, syncableType model.GroupSyncableType) (*model.GroupSyncable, error) {
|
||||||
|
var err error
|
||||||
|
var result interface{}
|
||||||
|
|
||||||
|
switch syncableType {
|
||||||
|
case model.GroupSyncableTypeTeam:
|
||||||
|
result, err = s.GetMaster().Get(groupTeam{}, groupID, syncableID)
|
||||||
|
case model.GroupSyncableTypeChannel:
|
||||||
|
result, err = s.GetMaster().Get(groupChannel{}, groupID, syncableID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if result == nil {
|
||||||
|
return nil, sql.ErrNoRows
|
||||||
|
}
|
||||||
|
|
||||||
|
groupSyncable := model.GroupSyncable{}
|
||||||
|
switch syncableType {
|
||||||
|
case model.GroupSyncableTypeTeam:
|
||||||
|
groupTeam := result.(*groupTeam)
|
||||||
|
groupSyncable.SyncableId = groupTeam.TeamId
|
||||||
|
groupSyncable.GroupId = groupTeam.GroupId
|
||||||
|
groupSyncable.CanLeave = groupTeam.CanLeave
|
||||||
|
groupSyncable.AutoAdd = groupTeam.AutoAdd
|
||||||
|
groupSyncable.CreateAt = groupTeam.CreateAt
|
||||||
|
groupSyncable.DeleteAt = groupTeam.DeleteAt
|
||||||
|
groupSyncable.UpdateAt = groupTeam.UpdateAt
|
||||||
|
groupSyncable.Type = syncableType
|
||||||
|
case model.GroupSyncableTypeChannel:
|
||||||
|
groupChannel := result.(*groupChannel)
|
||||||
|
groupSyncable.SyncableId = groupChannel.ChannelId
|
||||||
|
groupSyncable.GroupId = groupChannel.GroupId
|
||||||
|
groupSyncable.CanLeave = groupChannel.CanLeave
|
||||||
|
groupSyncable.AutoAdd = groupChannel.AutoAdd
|
||||||
|
groupSyncable.CreateAt = groupChannel.CreateAt
|
||||||
|
groupSyncable.DeleteAt = groupChannel.DeleteAt
|
||||||
|
groupSyncable.UpdateAt = groupChannel.UpdateAt
|
||||||
|
groupSyncable.Type = syncableType
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unable to convert syncableType: %s", syncableType.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return &groupSyncable, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SqlSupplier) GroupGetAllGroupSyncablesByGroup(ctx context.Context, groupID string, syncableType model.GroupSyncableType, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
result := store.NewSupplierResult()
|
||||||
|
|
||||||
|
args := map[string]interface{}{"GroupId": groupID}
|
||||||
|
|
||||||
|
appErrF := func(msg string) *model.AppError {
|
||||||
|
return model.NewAppError("SqlGroupStore.GroupGetAllGroupSyncablesByGroup", "store.select_error", nil, msg, http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
groupSyncables := []*model.GroupSyncable{}
|
||||||
|
|
||||||
|
switch syncableType {
|
||||||
|
case model.GroupSyncableTypeTeam:
|
||||||
|
sqlQuery := `
|
||||||
|
SELECT
|
||||||
|
GroupTeams.*,
|
||||||
|
Teams.DisplayName AS TeamDisplayName,
|
||||||
|
Teams.Type AS TeamType
|
||||||
|
FROM
|
||||||
|
GroupTeams
|
||||||
|
JOIN Teams ON Teams.Id = GroupTeams.TeamId
|
||||||
|
WHERE
|
||||||
|
GroupId = :GroupId AND GroupTeams.DeleteAt = 0`
|
||||||
|
|
||||||
|
results := []*groupTeamJoin{}
|
||||||
|
_, err := s.GetMaster().Select(&results, sqlQuery, args)
|
||||||
|
if err != nil {
|
||||||
|
result.Err = appErrF(err.Error())
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
for _, result := range results {
|
||||||
|
groupSyncable := &model.GroupSyncable{
|
||||||
|
SyncableId: result.TeamId,
|
||||||
|
GroupId: result.GroupId,
|
||||||
|
CanLeave: result.CanLeave,
|
||||||
|
AutoAdd: result.AutoAdd,
|
||||||
|
CreateAt: result.CreateAt,
|
||||||
|
DeleteAt: result.DeleteAt,
|
||||||
|
UpdateAt: result.UpdateAt,
|
||||||
|
Type: syncableType,
|
||||||
|
TeamDisplayName: result.TeamDisplayName,
|
||||||
|
TeamType: result.TeamType,
|
||||||
|
}
|
||||||
|
groupSyncables = append(groupSyncables, groupSyncable)
|
||||||
|
}
|
||||||
|
case model.GroupSyncableTypeChannel:
|
||||||
|
sqlQuery := `
|
||||||
|
SELECT
|
||||||
|
GroupChannels.*,
|
||||||
|
Channels.DisplayName AS ChannelDisplayName,
|
||||||
|
Teams.DisplayName AS TeamDisplayName,
|
||||||
|
Channels.Type As ChannelType,
|
||||||
|
Teams.Type As TeamType,
|
||||||
|
Teams.Id AS TeamId
|
||||||
|
FROM
|
||||||
|
GroupChannels
|
||||||
|
JOIN Channels ON Channels.Id = GroupChannels.ChannelId
|
||||||
|
JOIN Teams ON Teams.Id = Channels.TeamId
|
||||||
|
WHERE
|
||||||
|
GroupId = :GroupId AND GroupChannels.DeleteAt = 0`
|
||||||
|
|
||||||
|
results := []*groupChannelJoin{}
|
||||||
|
_, err := s.GetMaster().Select(&results, sqlQuery, args)
|
||||||
|
if err != nil {
|
||||||
|
result.Err = appErrF(err.Error())
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
for _, result := range results {
|
||||||
|
groupSyncable := &model.GroupSyncable{
|
||||||
|
SyncableId: result.ChannelId,
|
||||||
|
GroupId: result.GroupId,
|
||||||
|
CanLeave: result.CanLeave,
|
||||||
|
AutoAdd: result.AutoAdd,
|
||||||
|
CreateAt: result.CreateAt,
|
||||||
|
DeleteAt: result.DeleteAt,
|
||||||
|
UpdateAt: result.UpdateAt,
|
||||||
|
Type: syncableType,
|
||||||
|
ChannelDisplayName: result.ChannelDisplayName,
|
||||||
|
ChannelType: result.ChannelType,
|
||||||
|
TeamDisplayName: result.TeamDisplayName,
|
||||||
|
TeamType: result.TeamType,
|
||||||
|
TeamID: result.TeamID,
|
||||||
|
}
|
||||||
|
groupSyncables = append(groupSyncables, groupSyncable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Data = groupSyncables
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SqlSupplier) GroupUpdateGroupSyncable(ctx context.Context, groupSyncable *model.GroupSyncable, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
result := store.NewSupplierResult()
|
||||||
|
|
||||||
|
retrievedGroupSyncable, err := s.getGroupSyncable(groupSyncable.GroupId, groupSyncable.SyncableId, groupSyncable.Type)
|
||||||
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupUpdateGroupSyncable", "store.sql_group.no_rows", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupUpdateGroupSyncable", "store.select_error", nil, "GroupId="+groupSyncable.GroupId+", SyncableId="+groupSyncable.SyncableId+", SyncableType="+groupSyncable.Type.String()+", "+err.Error(), http.StatusInternalServerError)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := groupSyncable.IsValid(); err != nil {
|
||||||
|
result.Err = err
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// If updating DeleteAt it can only be to 0
|
||||||
|
if groupSyncable.DeleteAt != retrievedGroupSyncable.DeleteAt && groupSyncable.DeleteAt != 0 {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupUpdateGroupSyncable", "model.group.delete_at.app_error", nil, "", http.StatusInternalServerError)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset these properties, don't update them based on input
|
||||||
|
groupSyncable.CreateAt = retrievedGroupSyncable.CreateAt
|
||||||
|
groupSyncable.UpdateAt = model.GetMillis()
|
||||||
|
|
||||||
|
switch groupSyncable.Type {
|
||||||
|
case model.GroupSyncableTypeTeam:
|
||||||
|
_, err = s.GetMaster().Update(groupSyncableToGroupTeam(groupSyncable))
|
||||||
|
case model.GroupSyncableTypeChannel:
|
||||||
|
_, err = s.GetMaster().Update(groupSyncableToGroupChannel(groupSyncable))
|
||||||
|
default:
|
||||||
|
model.NewAppError("SqlGroupStore.GroupUpdateGroupSyncable", "model.group_syncable.type.app_error", nil, "group_id="+groupSyncable.GroupId+", syncable_id="+groupSyncable.SyncableId+", "+err.Error(), http.StatusInternalServerError)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupUpdateGroupSyncable", "store.update_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Data = groupSyncable
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SqlSupplier) GroupDeleteGroupSyncable(ctx context.Context, groupID string, syncableID string, syncableType model.GroupSyncableType, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
result := store.NewSupplierResult()
|
||||||
|
|
||||||
|
groupSyncable, err := s.getGroupSyncable(groupID, syncableID, syncableType)
|
||||||
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupDeleteGroupSyncable", "store.sql_group.no_rows", nil, "Id="+groupID+", "+err.Error(), http.StatusNotFound)
|
||||||
|
} else {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupDeleteGroupSyncable", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
if groupSyncable.DeleteAt != 0 {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupDeleteGroupSyncable", "store.sql_group.group_syncable_already_deleted", nil, "group_id="+groupID+"syncable_id="+syncableID, http.StatusBadRequest)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
time := model.GetMillis()
|
||||||
|
groupSyncable.DeleteAt = time
|
||||||
|
groupSyncable.UpdateAt = time
|
||||||
|
|
||||||
|
switch groupSyncable.Type {
|
||||||
|
case model.GroupSyncableTypeTeam:
|
||||||
|
_, err = s.GetMaster().Update(groupSyncableToGroupTeam(groupSyncable))
|
||||||
|
case model.GroupSyncableTypeChannel:
|
||||||
|
_, err = s.GetMaster().Update(groupSyncableToGroupChannel(groupSyncable))
|
||||||
|
default:
|
||||||
|
model.NewAppError("SqlGroupStore.GroupDeleteGroupSyncable", "model.group_syncable.type.app_error", nil, "group_id="+groupSyncable.GroupId+", syncable_id="+groupSyncable.SyncableId+", "+err.Error(), http.StatusInternalServerError)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.GroupDeleteGroupSyncable", "store.update_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Data = groupSyncable
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// PendingAutoAddTeamMembers returns a slice of UserTeamIDPair that need newly created memberships
|
||||||
|
// based on the groups configurations.
|
||||||
|
//
|
||||||
|
// Typically since will be the last successful group sync time.
|
||||||
|
func (s *SqlSupplier) PendingAutoAddTeamMembers(ctx context.Context, since int64, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
result := store.NewSupplierResult()
|
||||||
|
|
||||||
|
sql := `
|
||||||
|
SELECT
|
||||||
|
GroupMembers.UserId, GroupTeams.TeamId
|
||||||
|
FROM
|
||||||
|
GroupMembers
|
||||||
|
JOIN GroupTeams
|
||||||
|
ON GroupTeams.GroupId = GroupMembers.GroupId
|
||||||
|
JOIN UserGroups ON UserGroups.Id = GroupMembers.GroupId
|
||||||
|
JOIN Teams ON Teams.Id = GroupTeams.TeamId
|
||||||
|
LEFT OUTER JOIN TeamMembers
|
||||||
|
ON
|
||||||
|
TeamMembers.TeamId = GroupTeams.TeamId
|
||||||
|
AND TeamMembers.UserId = GroupMembers.UserId
|
||||||
|
WHERE
|
||||||
|
TeamMembers.UserId IS NULL
|
||||||
|
AND UserGroups.DeleteAt = 0
|
||||||
|
AND GroupTeams.DeleteAt = 0
|
||||||
|
AND GroupTeams.AutoAdd = true
|
||||||
|
AND GroupMembers.DeleteAt = 0
|
||||||
|
AND Teams.DeleteAt = 0
|
||||||
|
AND (GroupMembers.CreateAt >= :Since
|
||||||
|
OR GroupTeams.UpdateAt >= :Since)`
|
||||||
|
|
||||||
|
var userTeamIDs []*model.UserTeamIDPair
|
||||||
|
|
||||||
|
_, err := s.GetMaster().Select(&userTeamIDs, sql, map[string]interface{}{"Since": since})
|
||||||
|
if err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.PendingAutoAddTeamMembers", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Data = userTeamIDs
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// PendingAutoAddChannelMembers returns a slice of UserChannelIDPair that need newly created memberships
|
||||||
|
// based on the groups configurations.
|
||||||
|
//
|
||||||
|
// Typically since will be the last successful group sync time.
|
||||||
|
func (s *SqlSupplier) PendingAutoAddChannelMembers(ctx context.Context, since int64, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
result := store.NewSupplierResult()
|
||||||
|
|
||||||
|
sql := `
|
||||||
|
SELECT
|
||||||
|
GroupMembers.UserId, GroupChannels.ChannelId
|
||||||
|
FROM
|
||||||
|
GroupMembers
|
||||||
|
JOIN GroupChannels ON GroupChannels.GroupId = GroupMembers.GroupId
|
||||||
|
JOIN UserGroups ON UserGroups.Id = GroupMembers.GroupId
|
||||||
|
JOIN Channels ON Channels.Id = GroupChannels.ChannelId
|
||||||
|
LEFT OUTER JOIN ChannelMemberHistory
|
||||||
|
ON
|
||||||
|
ChannelMemberHistory.ChannelId = GroupChannels.ChannelId
|
||||||
|
AND ChannelMemberHistory.UserId = GroupMembers.UserId
|
||||||
|
WHERE
|
||||||
|
ChannelMemberHistory.UserId IS NULL
|
||||||
|
AND ChannelMemberHistory.LeaveTime IS NULL
|
||||||
|
AND UserGroups.DeleteAt = 0
|
||||||
|
AND GroupChannels.DeleteAt = 0
|
||||||
|
AND GroupChannels.AutoAdd = true
|
||||||
|
AND GroupMembers.DeleteAt = 0
|
||||||
|
AND Channels.DeleteAt = 0
|
||||||
|
AND (GroupMembers.CreateAt >= :Since
|
||||||
|
OR GroupChannels.UpdateAt >= :Since)`
|
||||||
|
|
||||||
|
var userChannelIDs []*model.UserChannelIDPair
|
||||||
|
|
||||||
|
_, err := s.GetMaster().Select(&userChannelIDs, sql, map[string]interface{}{"Since": since})
|
||||||
|
if err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlGroupStore.PendingAutoAddChannelMembers", "store.select_error", nil, "", http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Data = userChannelIDs
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func groupSyncableToGroupTeam(groupSyncable *model.GroupSyncable) *groupTeam {
|
||||||
|
return &groupTeam{
|
||||||
|
GroupSyncable: *groupSyncable,
|
||||||
|
TeamId: groupSyncable.SyncableId,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func groupSyncableToGroupChannel(groupSyncable *model.GroupSyncable) *groupChannel {
|
||||||
|
return &groupChannel{
|
||||||
|
GroupSyncable: *groupSyncable,
|
||||||
|
ChannelId: groupSyncable.SyncableId,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -93,6 +93,7 @@ type SqlSupplierOldStores struct {
|
|||||||
role store.RoleStore
|
role store.RoleStore
|
||||||
scheme store.SchemeStore
|
scheme store.SchemeStore
|
||||||
TermsOfService store.TermsOfServiceStore
|
TermsOfService store.TermsOfServiceStore
|
||||||
|
group store.GroupStore
|
||||||
UserTermsOfService store.UserTermsOfServiceStore
|
UserTermsOfService store.UserTermsOfServiceStore
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,6 +149,7 @@ func NewSqlSupplier(settings model.SqlSettings, metrics einterfaces.MetricsInter
|
|||||||
initSqlSupplierReactions(supplier)
|
initSqlSupplierReactions(supplier)
|
||||||
initSqlSupplierRoles(supplier)
|
initSqlSupplierRoles(supplier)
|
||||||
initSqlSupplierSchemes(supplier)
|
initSqlSupplierSchemes(supplier)
|
||||||
|
initSqlSupplierGroups(supplier)
|
||||||
|
|
||||||
err := supplier.GetMaster().CreateTablesIfNotExists()
|
err := supplier.GetMaster().CreateTablesIfNotExists()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -182,6 +184,8 @@ func NewSqlSupplier(settings model.SqlSettings, metrics einterfaces.MetricsInter
|
|||||||
supplier.oldStores.TermsOfService.(SqlTermsOfServiceStore).CreateIndexesIfNotExists()
|
supplier.oldStores.TermsOfService.(SqlTermsOfServiceStore).CreateIndexesIfNotExists()
|
||||||
supplier.oldStores.UserTermsOfService.(SqlUserTermsOfServiceStore).CreateIndexesIfNotExists()
|
supplier.oldStores.UserTermsOfService.(SqlUserTermsOfServiceStore).CreateIndexesIfNotExists()
|
||||||
|
|
||||||
|
supplier.CreateIndexesIfNotExistsGroups()
|
||||||
|
|
||||||
supplier.oldStores.preference.(*SqlPreferenceStore).DeleteUnusedFeatures()
|
supplier.oldStores.preference.(*SqlPreferenceStore).DeleteUnusedFeatures()
|
||||||
|
|
||||||
return supplier
|
return supplier
|
||||||
@@ -1024,6 +1028,10 @@ func (ss *SqlSupplier) Scheme() store.SchemeStore {
|
|||||||
return ss.oldStores.scheme
|
return ss.oldStores.scheme
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ss *SqlSupplier) Group() store.GroupStore {
|
||||||
|
return ss.oldStores.group
|
||||||
|
}
|
||||||
|
|
||||||
func (ss *SqlSupplier) DropAllTables() {
|
func (ss *SqlSupplier) DropAllTables() {
|
||||||
ss.master.TruncateTables()
|
ss.master.TruncateTables()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -532,6 +532,7 @@ func UpgradeDatabaseToVersion56(sqlStore SqlStore) {
|
|||||||
sqlStore.RemoveIndexIfExists("idx_users_firstname_lower", "lower(FirstName)")
|
sqlStore.RemoveIndexIfExists("idx_users_firstname_lower", "lower(FirstName)")
|
||||||
sqlStore.RemoveIndexIfExists("idx_users_lastname_lower", "lower(LastName)")
|
sqlStore.RemoveIndexIfExists("idx_users_lastname_lower", "lower(LastName)")
|
||||||
}
|
}
|
||||||
|
|
||||||
saveSchemaVersion(sqlStore, VERSION_5_6_0)
|
saveSchemaVersion(sqlStore, VERSION_5_6_0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ type Store interface {
|
|||||||
ChannelMemberHistory() ChannelMemberHistoryStore
|
ChannelMemberHistory() ChannelMemberHistoryStore
|
||||||
Plugin() PluginStore
|
Plugin() PluginStore
|
||||||
TermsOfService() TermsOfServiceStore
|
TermsOfService() TermsOfServiceStore
|
||||||
|
Group() GroupStore
|
||||||
UserTermsOfService() UserTermsOfServiceStore
|
UserTermsOfService() UserTermsOfServiceStore
|
||||||
MarkSystemRanUnitTests()
|
MarkSystemRanUnitTests()
|
||||||
Close()
|
Close()
|
||||||
@@ -137,6 +138,7 @@ type ChannelStore interface {
|
|||||||
GetDeletedByName(team_id string, name string) StoreChannel
|
GetDeletedByName(team_id string, name string) StoreChannel
|
||||||
GetDeleted(team_id string, offset int, limit int) StoreChannel
|
GetDeleted(team_id string, offset int, limit int) StoreChannel
|
||||||
GetChannels(teamId string, userId string, includeDeleted bool) StoreChannel
|
GetChannels(teamId string, userId string, includeDeleted bool) StoreChannel
|
||||||
|
GetAllChannels(page, perPage int, includeDeleted bool) StoreChannel
|
||||||
GetMoreChannels(teamId string, userId string, offset int, limit int) StoreChannel
|
GetMoreChannels(teamId string, userId string, offset int, limit int) StoreChannel
|
||||||
GetPublicChannelsForTeam(teamId string, offset int, limit int) StoreChannel
|
GetPublicChannelsForTeam(teamId string, offset int, limit int) StoreChannel
|
||||||
GetPublicChannelsByIdsForTeam(teamId string, channelIds []string) StoreChannel
|
GetPublicChannelsByIdsForTeam(teamId string, channelIds []string) StoreChannel
|
||||||
@@ -168,6 +170,7 @@ type ChannelStore interface {
|
|||||||
GetMembersForUser(teamId string, userId string) StoreChannel
|
GetMembersForUser(teamId string, userId string) StoreChannel
|
||||||
AutocompleteInTeam(teamId string, term string, includeDeleted bool) StoreChannel
|
AutocompleteInTeam(teamId string, term string, includeDeleted bool) StoreChannel
|
||||||
AutocompleteInTeamForSearch(teamId string, userId string, term string, includeDeleted bool) StoreChannel
|
AutocompleteInTeamForSearch(teamId string, userId string, term string, includeDeleted bool) StoreChannel
|
||||||
|
SearchAllChannels(term string, includeDeleted bool) StoreChannel
|
||||||
SearchInTeam(teamId string, term string, includeDeleted bool) StoreChannel
|
SearchInTeam(teamId string, term string, includeDeleted bool) StoreChannel
|
||||||
SearchMore(userId string, teamId string, term string) StoreChannel
|
SearchMore(userId string, teamId string, term string) StoreChannel
|
||||||
GetMembersByIds(channelId string, userIds []string) StoreChannel
|
GetMembersByIds(channelId string, userIds []string) StoreChannel
|
||||||
@@ -536,3 +539,27 @@ type UserTermsOfServiceStore interface {
|
|||||||
Save(userTermsOfService *model.UserTermsOfService) StoreChannel
|
Save(userTermsOfService *model.UserTermsOfService) StoreChannel
|
||||||
Delete(userId, termsOfServiceId string) StoreChannel
|
Delete(userId, termsOfServiceId string) StoreChannel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GroupStore interface {
|
||||||
|
Create(group *model.Group) StoreChannel
|
||||||
|
Get(groupID string) StoreChannel
|
||||||
|
GetByRemoteID(remoteID string, groupSource model.GroupSource) StoreChannel
|
||||||
|
GetAllBySource(groupSource model.GroupSource) StoreChannel
|
||||||
|
Update(group *model.Group) StoreChannel
|
||||||
|
Delete(groupID string) StoreChannel
|
||||||
|
|
||||||
|
GetMemberUsers(groupID string) StoreChannel
|
||||||
|
GetMemberUsersPage(groupID string, offset int, limit int) StoreChannel
|
||||||
|
GetMemberCount(groupID string) StoreChannel
|
||||||
|
CreateOrRestoreMember(groupID string, userID string) StoreChannel
|
||||||
|
DeleteMember(groupID string, userID string) StoreChannel
|
||||||
|
|
||||||
|
CreateGroupSyncable(groupSyncable *model.GroupSyncable) StoreChannel
|
||||||
|
GetGroupSyncable(groupID string, syncableID string, syncableType model.GroupSyncableType) StoreChannel
|
||||||
|
GetAllGroupSyncablesByGroupId(groupID string, syncableType model.GroupSyncableType) StoreChannel
|
||||||
|
UpdateGroupSyncable(groupSyncable *model.GroupSyncable) StoreChannel
|
||||||
|
DeleteGroupSyncable(groupID string, syncableID string, syncableType model.GroupSyncableType) StoreChannel
|
||||||
|
|
||||||
|
PendingAutoAddTeamMembers(minGroupMembersCreateAt int64) StoreChannel
|
||||||
|
PendingAutoAddChannelMembers(minGroupMembersCreateAt int64) StoreChannel
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,6 +21,17 @@ type SqlSupplier interface {
|
|||||||
GetMaster() *gorp.DbMap
|
GetMaster() *gorp.DbMap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cleanupChannels(t *testing.T, ss store.Store) {
|
||||||
|
result := <-ss.Channel().GetAllChannels(0, 100000, true)
|
||||||
|
if result.Err != nil {
|
||||||
|
t.Fatal("error cleaning all channels")
|
||||||
|
}
|
||||||
|
list := result.Data.(*model.ChannelListWithTeamData)
|
||||||
|
for _, channel := range *list {
|
||||||
|
ss.Channel().PermanentDelete(channel.Id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestChannelStore(t *testing.T, ss store.Store, s SqlSupplier) {
|
func TestChannelStore(t *testing.T, ss store.Store, s SqlSupplier) {
|
||||||
createDefaultRoles(t, ss)
|
createDefaultRoles(t, ss)
|
||||||
|
|
||||||
@@ -40,6 +51,7 @@ func TestChannelStore(t *testing.T, ss store.Store, s SqlSupplier) {
|
|||||||
t.Run("ChannelMemberStore", func(t *testing.T) { testChannelMemberStore(t, ss) })
|
t.Run("ChannelMemberStore", func(t *testing.T) { testChannelMemberStore(t, ss) })
|
||||||
t.Run("ChannelDeleteMemberStore", func(t *testing.T) { testChannelDeleteMemberStore(t, ss) })
|
t.Run("ChannelDeleteMemberStore", func(t *testing.T) { testChannelDeleteMemberStore(t, ss) })
|
||||||
t.Run("GetChannels", func(t *testing.T) { testChannelStoreGetChannels(t, ss) })
|
t.Run("GetChannels", func(t *testing.T) { testChannelStoreGetChannels(t, ss) })
|
||||||
|
t.Run("GetAllChannels", func(t *testing.T) { testChannelStoreGetAllChannels(t, ss) })
|
||||||
t.Run("GetMoreChannels", func(t *testing.T) { testChannelStoreGetMoreChannels(t, ss) })
|
t.Run("GetMoreChannels", func(t *testing.T) { testChannelStoreGetMoreChannels(t, ss) })
|
||||||
t.Run("GetPublicChannelsForTeam", func(t *testing.T) { testChannelStoreGetPublicChannelsForTeam(t, ss) })
|
t.Run("GetPublicChannelsForTeam", func(t *testing.T) { testChannelStoreGetPublicChannelsForTeam(t, ss) })
|
||||||
t.Run("GetPublicChannelsByIdsForTeam", func(t *testing.T) { testChannelStoreGetPublicChannelsByIdsForTeam(t, ss) })
|
t.Run("GetPublicChannelsByIdsForTeam", func(t *testing.T) { testChannelStoreGetPublicChannelsByIdsForTeam(t, ss) })
|
||||||
@@ -53,6 +65,7 @@ func TestChannelStore(t *testing.T, ss store.Store, s SqlSupplier) {
|
|||||||
t.Run("GetMemberCount", func(t *testing.T) { testGetMemberCount(t, ss) })
|
t.Run("GetMemberCount", func(t *testing.T) { testGetMemberCount(t, ss) })
|
||||||
t.Run("SearchMore", func(t *testing.T) { testChannelStoreSearchMore(t, ss) })
|
t.Run("SearchMore", func(t *testing.T) { testChannelStoreSearchMore(t, ss) })
|
||||||
t.Run("SearchInTeam", func(t *testing.T) { testChannelStoreSearchInTeam(t, ss) })
|
t.Run("SearchInTeam", func(t *testing.T) { testChannelStoreSearchInTeam(t, ss) })
|
||||||
|
t.Run("SearchAllChannels", func(t *testing.T) { testChannelStoreSearchAllChannels(t, ss) })
|
||||||
t.Run("AutocompleteInTeamForSearch", func(t *testing.T) { testChannelStoreAutocompleteInTeamForSearch(t, ss) })
|
t.Run("AutocompleteInTeamForSearch", func(t *testing.T) { testChannelStoreAutocompleteInTeamForSearch(t, ss) })
|
||||||
t.Run("GetMembersByIds", func(t *testing.T) { testChannelStoreGetMembersByIds(t, ss) })
|
t.Run("GetMembersByIds", func(t *testing.T) { testChannelStoreGetMembersByIds(t, ss) })
|
||||||
t.Run("AnalyticsDeletedTypeCount", func(t *testing.T) { testChannelStoreAnalyticsDeletedTypeCount(t, ss) })
|
t.Run("AnalyticsDeletedTypeCount", func(t *testing.T) { testChannelStoreAnalyticsDeletedTypeCount(t, ss) })
|
||||||
@@ -959,6 +972,81 @@ func testChannelStoreGetChannels(t *testing.T, ss store.Store) {
|
|||||||
ss.Channel().InvalidateAllChannelMembersForUser(m1.UserId)
|
ss.Channel().InvalidateAllChannelMembersForUser(m1.UserId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testChannelStoreGetAllChannels(t *testing.T, ss store.Store) {
|
||||||
|
cleanupChannels(t, ss)
|
||||||
|
|
||||||
|
t1 := model.Team{}
|
||||||
|
t1.DisplayName = "Name"
|
||||||
|
t1.Name = model.NewId()
|
||||||
|
t1.Email = MakeEmail()
|
||||||
|
t1.Type = model.TEAM_OPEN
|
||||||
|
store.Must(ss.Team().Save(&t1))
|
||||||
|
|
||||||
|
t2 := model.Team{}
|
||||||
|
t2.DisplayName = "Name2"
|
||||||
|
t2.Name = model.NewId()
|
||||||
|
t2.Email = MakeEmail()
|
||||||
|
t2.Type = model.TEAM_OPEN
|
||||||
|
store.Must(ss.Team().Save(&t2))
|
||||||
|
|
||||||
|
c1 := model.Channel{}
|
||||||
|
c1.TeamId = t1.Id
|
||||||
|
c1.DisplayName = "Channel1" + model.NewId()
|
||||||
|
c1.Name = "zz" + model.NewId() + "b"
|
||||||
|
c1.Type = model.CHANNEL_OPEN
|
||||||
|
store.Must(ss.Channel().Save(&c1, -1))
|
||||||
|
|
||||||
|
c2 := model.Channel{}
|
||||||
|
c2.TeamId = t1.Id
|
||||||
|
c2.DisplayName = "Channel2" + model.NewId()
|
||||||
|
c2.Name = "zz" + model.NewId() + "b"
|
||||||
|
c2.Type = model.CHANNEL_OPEN
|
||||||
|
store.Must(ss.Channel().Save(&c2, -1))
|
||||||
|
c2.DeleteAt = model.GetMillis()
|
||||||
|
c2.UpdateAt = c2.DeleteAt
|
||||||
|
store.Must(ss.Channel().Delete(c2.Id, c2.DeleteAt))
|
||||||
|
|
||||||
|
c3 := model.Channel{}
|
||||||
|
c3.TeamId = t2.Id
|
||||||
|
c3.DisplayName = "Channel3" + model.NewId()
|
||||||
|
c3.Name = "zz" + model.NewId() + "b"
|
||||||
|
c3.Type = model.CHANNEL_PRIVATE
|
||||||
|
store.Must(ss.Channel().Save(&c3, -1))
|
||||||
|
|
||||||
|
store.Must(ss.Channel().CreateDirectChannel(model.NewId(), model.NewId()))
|
||||||
|
|
||||||
|
userIds := []string{model.NewId(), model.NewId(), model.NewId()}
|
||||||
|
|
||||||
|
c5 := model.Channel{}
|
||||||
|
c5.Name = model.GetGroupNameFromUserIds(userIds)
|
||||||
|
c5.DisplayName = "GroupChannel" + model.NewId()
|
||||||
|
c5.Name = "zz" + model.NewId() + "b"
|
||||||
|
c5.Type = model.CHANNEL_GROUP
|
||||||
|
store.Must(ss.Channel().Save(&c5, -1))
|
||||||
|
|
||||||
|
cresult := <-ss.Channel().GetAllChannels(0, 10, false)
|
||||||
|
list := cresult.Data.(*model.ChannelListWithTeamData)
|
||||||
|
assert.Len(t, *list, 2)
|
||||||
|
assert.Equal(t, (*list)[0].Id, c1.Id)
|
||||||
|
assert.Equal(t, (*list)[0].TeamDisplayName, "Name")
|
||||||
|
assert.Equal(t, (*list)[1].Id, c3.Id)
|
||||||
|
assert.Equal(t, (*list)[1].TeamDisplayName, "Name2")
|
||||||
|
|
||||||
|
cresult = <-ss.Channel().GetAllChannels(0, 10, true)
|
||||||
|
list = cresult.Data.(*model.ChannelListWithTeamData)
|
||||||
|
assert.Len(t, *list, 3)
|
||||||
|
assert.Equal(t, (*list)[0].Id, c1.Id)
|
||||||
|
assert.Equal(t, (*list)[0].TeamDisplayName, "Name")
|
||||||
|
assert.Equal(t, (*list)[1].Id, c2.Id)
|
||||||
|
assert.Equal(t, (*list)[2].Id, c3.Id)
|
||||||
|
|
||||||
|
cresult = <-ss.Channel().GetAllChannels(0, 1, true)
|
||||||
|
list = cresult.Data.(*model.ChannelListWithTeamData)
|
||||||
|
assert.Len(t, *list, 1)
|
||||||
|
assert.Equal(t, (*list)[0].Id, c1.Id)
|
||||||
|
assert.Equal(t, (*list)[0].TeamDisplayName, "Name")
|
||||||
|
}
|
||||||
|
|
||||||
func testChannelStoreGetMoreChannels(t *testing.T, ss store.Store) {
|
func testChannelStoreGetMoreChannels(t *testing.T, ss store.Store) {
|
||||||
teamId := model.NewId()
|
teamId := model.NewId()
|
||||||
otherTeamId := model.NewId()
|
otherTeamId := model.NewId()
|
||||||
@@ -2006,6 +2094,184 @@ func testChannelStoreSearchInTeam(t *testing.T, ss store.Store) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testChannelStoreSearchAllChannels(t *testing.T, ss store.Store) {
|
||||||
|
cleanupChannels(t, ss)
|
||||||
|
|
||||||
|
t1 := model.Team{}
|
||||||
|
t1.DisplayName = "Name"
|
||||||
|
t1.Name = model.NewId()
|
||||||
|
t1.Email = MakeEmail()
|
||||||
|
t1.Type = model.TEAM_OPEN
|
||||||
|
store.Must(ss.Team().Save(&t1))
|
||||||
|
|
||||||
|
t2 := model.Team{}
|
||||||
|
t2.DisplayName = "Name2"
|
||||||
|
t2.Name = model.NewId()
|
||||||
|
t2.Email = MakeEmail()
|
||||||
|
t2.Type = model.TEAM_OPEN
|
||||||
|
store.Must(ss.Team().Save(&t2))
|
||||||
|
|
||||||
|
o1 := model.Channel{
|
||||||
|
TeamId: t1.Id,
|
||||||
|
DisplayName: "ChannelA",
|
||||||
|
Name: "zz" + model.NewId() + "b",
|
||||||
|
Type: model.CHANNEL_OPEN,
|
||||||
|
}
|
||||||
|
store.Must(ss.Channel().Save(&o1, -1))
|
||||||
|
|
||||||
|
o2 := model.Channel{
|
||||||
|
TeamId: t2.Id,
|
||||||
|
DisplayName: "ChannelA",
|
||||||
|
Name: "zz" + model.NewId() + "b",
|
||||||
|
Type: model.CHANNEL_OPEN,
|
||||||
|
}
|
||||||
|
store.Must(ss.Channel().Save(&o2, -1))
|
||||||
|
|
||||||
|
m1 := model.ChannelMember{
|
||||||
|
ChannelId: o1.Id,
|
||||||
|
UserId: model.NewId(),
|
||||||
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
||||||
|
}
|
||||||
|
store.Must(ss.Channel().SaveMember(&m1))
|
||||||
|
|
||||||
|
m2 := model.ChannelMember{
|
||||||
|
ChannelId: o1.Id,
|
||||||
|
UserId: model.NewId(),
|
||||||
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
||||||
|
}
|
||||||
|
store.Must(ss.Channel().SaveMember(&m2))
|
||||||
|
|
||||||
|
m3 := model.ChannelMember{
|
||||||
|
ChannelId: o2.Id,
|
||||||
|
UserId: model.NewId(),
|
||||||
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
||||||
|
}
|
||||||
|
store.Must(ss.Channel().SaveMember(&m3))
|
||||||
|
|
||||||
|
o3 := model.Channel{
|
||||||
|
TeamId: t1.Id,
|
||||||
|
DisplayName: "ChannelA (alternate)",
|
||||||
|
Name: "zz" + model.NewId() + "b",
|
||||||
|
Type: model.CHANNEL_OPEN,
|
||||||
|
}
|
||||||
|
store.Must(ss.Channel().Save(&o3, -1))
|
||||||
|
|
||||||
|
o4 := model.Channel{
|
||||||
|
TeamId: t1.Id,
|
||||||
|
DisplayName: "ChannelB",
|
||||||
|
Name: "zz" + model.NewId() + "b",
|
||||||
|
Type: model.CHANNEL_PRIVATE,
|
||||||
|
}
|
||||||
|
store.Must(ss.Channel().Save(&o4, -1))
|
||||||
|
|
||||||
|
o5 := model.Channel{
|
||||||
|
TeamId: t1.Id,
|
||||||
|
DisplayName: "ChannelC",
|
||||||
|
Name: "zz" + model.NewId() + "b",
|
||||||
|
Type: model.CHANNEL_PRIVATE,
|
||||||
|
}
|
||||||
|
store.Must(ss.Channel().Save(&o5, -1))
|
||||||
|
|
||||||
|
o6 := model.Channel{
|
||||||
|
TeamId: t1.Id,
|
||||||
|
DisplayName: "Off-Topic",
|
||||||
|
Name: "off-topic",
|
||||||
|
Type: model.CHANNEL_OPEN,
|
||||||
|
}
|
||||||
|
store.Must(ss.Channel().Save(&o6, -1))
|
||||||
|
|
||||||
|
o7 := model.Channel{
|
||||||
|
TeamId: t1.Id,
|
||||||
|
DisplayName: "Off-Set",
|
||||||
|
Name: "off-set",
|
||||||
|
Type: model.CHANNEL_OPEN,
|
||||||
|
}
|
||||||
|
store.Must(ss.Channel().Save(&o7, -1))
|
||||||
|
|
||||||
|
o8 := model.Channel{
|
||||||
|
TeamId: t1.Id,
|
||||||
|
DisplayName: "Off-Limit",
|
||||||
|
Name: "off-limit",
|
||||||
|
Type: model.CHANNEL_PRIVATE,
|
||||||
|
}
|
||||||
|
store.Must(ss.Channel().Save(&o8, -1))
|
||||||
|
|
||||||
|
o9 := model.Channel{
|
||||||
|
TeamId: t1.Id,
|
||||||
|
DisplayName: "Town Square",
|
||||||
|
Name: "town-square",
|
||||||
|
Type: model.CHANNEL_OPEN,
|
||||||
|
}
|
||||||
|
store.Must(ss.Channel().Save(&o9, -1))
|
||||||
|
|
||||||
|
o10 := model.Channel{
|
||||||
|
TeamId: t1.Id,
|
||||||
|
DisplayName: "The",
|
||||||
|
Name: "the",
|
||||||
|
Type: model.CHANNEL_OPEN,
|
||||||
|
}
|
||||||
|
store.Must(ss.Channel().Save(&o10, -1))
|
||||||
|
|
||||||
|
o11 := model.Channel{
|
||||||
|
TeamId: t1.Id,
|
||||||
|
DisplayName: "Native Mobile Apps",
|
||||||
|
Name: "native-mobile-apps",
|
||||||
|
Type: model.CHANNEL_OPEN,
|
||||||
|
}
|
||||||
|
store.Must(ss.Channel().Save(&o11, -1))
|
||||||
|
|
||||||
|
o12 := model.Channel{
|
||||||
|
TeamId: t1.Id,
|
||||||
|
DisplayName: "ChannelZ",
|
||||||
|
Purpose: "This can now be searchable!",
|
||||||
|
Name: "with-purpose",
|
||||||
|
Type: model.CHANNEL_OPEN,
|
||||||
|
}
|
||||||
|
store.Must(ss.Channel().Save(&o12, -1))
|
||||||
|
|
||||||
|
o13 := model.Channel{
|
||||||
|
TeamId: t1.Id,
|
||||||
|
DisplayName: "ChannelA (deleted)",
|
||||||
|
Name: model.NewId(),
|
||||||
|
Type: model.CHANNEL_OPEN,
|
||||||
|
}
|
||||||
|
store.Must(ss.Channel().Save(&o13, -1))
|
||||||
|
o13.DeleteAt = model.GetMillis()
|
||||||
|
o13.UpdateAt = o13.DeleteAt
|
||||||
|
store.Must(ss.Channel().Delete(o13.Id, o13.DeleteAt))
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
Description string
|
||||||
|
Term string
|
||||||
|
IncludeDeleted bool
|
||||||
|
ExpectedResults *model.ChannelList
|
||||||
|
}{
|
||||||
|
{"ChannelA", "ChannelA", false, &model.ChannelList{&o1, &o2, &o3}},
|
||||||
|
{"ChannelA, include deleted", "ChannelA", true, &model.ChannelList{&o1, &o2, &o3, &o13}},
|
||||||
|
{"empty string", "", false, &model.ChannelList{&o1, &o2, &o3, &o4, &o5, &o12, &o11, &o8, &o7, &o6, &o10, &o9}},
|
||||||
|
{"no matches", "blargh", false, &model.ChannelList{}},
|
||||||
|
{"prefix", "off-", false, &model.ChannelList{&o8, &o7, &o6}},
|
||||||
|
{"full match with dash", "off-topic", false, &model.ChannelList{&o6}},
|
||||||
|
{"town square", "town square", false, &model.ChannelList{&o9}},
|
||||||
|
{"the in name", "the", false, &model.ChannelList{&o10}},
|
||||||
|
{"Mobile", "Mobile", false, &model.ChannelList{&o11}},
|
||||||
|
{"search purpose", "now searchable", false, &model.ChannelList{&o12}},
|
||||||
|
{"pipe ignored", "town square |", false, &model.ChannelList{&o9}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
t.Run(testCase.Description, func(t *testing.T) {
|
||||||
|
result := <-ss.Channel().SearchAllChannels(testCase.Term, testCase.IncludeDeleted)
|
||||||
|
require.Nil(t, result.Err)
|
||||||
|
channels := result.Data.(*model.ChannelListWithTeamData)
|
||||||
|
require.Equal(t, len(*channels), len(*testCase.ExpectedResults))
|
||||||
|
for i, expected := range *testCase.ExpectedResults {
|
||||||
|
require.Equal(t, (*channels)[i].Id, expected.Id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testChannelStoreAutocompleteInTeamForSearch(t *testing.T, ss store.Store) {
|
func testChannelStoreAutocompleteInTeamForSearch(t *testing.T, ss store.Store) {
|
||||||
u1 := &model.User{}
|
u1 := &model.User{}
|
||||||
u1.Email = MakeEmail()
|
u1.Email = MakeEmail()
|
||||||
|
|||||||
1318
store/storetest/group_supplier.go
Обычный файл
1318
store/storetest/group_supplier.go
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
@@ -194,6 +194,22 @@ func (_m *ChannelStore) GetAllChannelMembersNotifyPropsForChannel(channelId stri
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAllChannels provides a mock function with given fields: page, perPage, includeDeleted
|
||||||
|
func (_m *ChannelStore) GetAllChannels(page int, perPage int, includeDeleted bool) store.StoreChannel {
|
||||||
|
ret := _m.Called(page, perPage, includeDeleted)
|
||||||
|
|
||||||
|
var r0 store.StoreChannel
|
||||||
|
if rf, ok := ret.Get(0).(func(int, int, bool) store.StoreChannel); ok {
|
||||||
|
r0 = rf(page, perPage, includeDeleted)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(store.StoreChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
// GetAllChannelsForExportAfter provides a mock function with given fields: limit, afterId
|
// GetAllChannelsForExportAfter provides a mock function with given fields: limit, afterId
|
||||||
func (_m *ChannelStore) GetAllChannelsForExportAfter(limit int, afterId string) store.StoreChannel {
|
func (_m *ChannelStore) GetAllChannelsForExportAfter(limit int, afterId string) store.StoreChannel {
|
||||||
ret := _m.Called(limit, afterId)
|
ret := _m.Called(limit, afterId)
|
||||||
@@ -885,6 +901,22 @@ func (_m *ChannelStore) SaveMember(member *model.ChannelMember) store.StoreChann
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SearchAllChannels provides a mock function with given fields: term, includeDeleted
|
||||||
|
func (_m *ChannelStore) SearchAllChannels(term string, includeDeleted bool) store.StoreChannel {
|
||||||
|
ret := _m.Called(term, includeDeleted)
|
||||||
|
|
||||||
|
var r0 store.StoreChannel
|
||||||
|
if rf, ok := ret.Get(0).(func(string, bool) store.StoreChannel); ok {
|
||||||
|
r0 = rf(term, includeDeleted)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(store.StoreChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
// SearchInTeam provides a mock function with given fields: teamId, term, includeDeleted
|
// SearchInTeam provides a mock function with given fields: teamId, term, includeDeleted
|
||||||
func (_m *ChannelStore) SearchInTeam(teamId string, term string, includeDeleted bool) store.StoreChannel {
|
func (_m *ChannelStore) SearchInTeam(teamId string, term string, includeDeleted bool) store.StoreChannel {
|
||||||
ret := _m.Called(teamId, term, includeDeleted)
|
ret := _m.Called(teamId, term, includeDeleted)
|
||||||
|
|||||||
302
store/storetest/mocks/GroupStore.go
Обычный файл
302
store/storetest/mocks/GroupStore.go
Обычный файл
@@ -0,0 +1,302 @@
|
|||||||
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
package mocks
|
||||||
|
|
||||||
|
import mock "github.com/stretchr/testify/mock"
|
||||||
|
import model "github.com/mattermost/mattermost-server/model"
|
||||||
|
import store "github.com/mattermost/mattermost-server/store"
|
||||||
|
|
||||||
|
// GroupStore is an autogenerated mock type for the GroupStore type
|
||||||
|
type GroupStore struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create provides a mock function with given fields: group
|
||||||
|
func (_m *GroupStore) Create(group *model.Group) store.StoreChannel {
|
||||||
|
ret := _m.Called(group)
|
||||||
|
|
||||||
|
var r0 store.StoreChannel
|
||||||
|
if rf, ok := ret.Get(0).(func(*model.Group) store.StoreChannel); ok {
|
||||||
|
r0 = rf(group)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(store.StoreChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateGroupSyncable provides a mock function with given fields: groupSyncable
|
||||||
|
func (_m *GroupStore) CreateGroupSyncable(groupSyncable *model.GroupSyncable) store.StoreChannel {
|
||||||
|
ret := _m.Called(groupSyncable)
|
||||||
|
|
||||||
|
var r0 store.StoreChannel
|
||||||
|
if rf, ok := ret.Get(0).(func(*model.GroupSyncable) store.StoreChannel); ok {
|
||||||
|
r0 = rf(groupSyncable)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(store.StoreChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOrRestoreMember provides a mock function with given fields: groupID, userID
|
||||||
|
func (_m *GroupStore) CreateOrRestoreMember(groupID string, userID string) store.StoreChannel {
|
||||||
|
ret := _m.Called(groupID, userID)
|
||||||
|
|
||||||
|
var r0 store.StoreChannel
|
||||||
|
if rf, ok := ret.Get(0).(func(string, string) store.StoreChannel); ok {
|
||||||
|
r0 = rf(groupID, userID)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(store.StoreChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete provides a mock function with given fields: groupID
|
||||||
|
func (_m *GroupStore) Delete(groupID string) store.StoreChannel {
|
||||||
|
ret := _m.Called(groupID)
|
||||||
|
|
||||||
|
var r0 store.StoreChannel
|
||||||
|
if rf, ok := ret.Get(0).(func(string) store.StoreChannel); ok {
|
||||||
|
r0 = rf(groupID)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(store.StoreChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteGroupSyncable provides a mock function with given fields: groupID, syncableID, syncableType
|
||||||
|
func (_m *GroupStore) DeleteGroupSyncable(groupID string, syncableID string, syncableType model.GroupSyncableType) store.StoreChannel {
|
||||||
|
ret := _m.Called(groupID, syncableID, syncableType)
|
||||||
|
|
||||||
|
var r0 store.StoreChannel
|
||||||
|
if rf, ok := ret.Get(0).(func(string, string, model.GroupSyncableType) store.StoreChannel); ok {
|
||||||
|
r0 = rf(groupID, syncableID, syncableType)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(store.StoreChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteMember provides a mock function with given fields: groupID, userID
|
||||||
|
func (_m *GroupStore) DeleteMember(groupID string, userID string) store.StoreChannel {
|
||||||
|
ret := _m.Called(groupID, userID)
|
||||||
|
|
||||||
|
var r0 store.StoreChannel
|
||||||
|
if rf, ok := ret.Get(0).(func(string, string) store.StoreChannel); ok {
|
||||||
|
r0 = rf(groupID, userID)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(store.StoreChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get provides a mock function with given fields: groupID
|
||||||
|
func (_m *GroupStore) Get(groupID string) store.StoreChannel {
|
||||||
|
ret := _m.Called(groupID)
|
||||||
|
|
||||||
|
var r0 store.StoreChannel
|
||||||
|
if rf, ok := ret.Get(0).(func(string) store.StoreChannel); ok {
|
||||||
|
r0 = rf(groupID)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(store.StoreChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllBySource provides a mock function with given fields: groupSource
|
||||||
|
func (_m *GroupStore) GetAllBySource(groupSource model.GroupSource) store.StoreChannel {
|
||||||
|
ret := _m.Called(groupSource)
|
||||||
|
|
||||||
|
var r0 store.StoreChannel
|
||||||
|
if rf, ok := ret.Get(0).(func(model.GroupSource) store.StoreChannel); ok {
|
||||||
|
r0 = rf(groupSource)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(store.StoreChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllGroupSyncablesByGroupId provides a mock function with given fields: groupID, syncableType
|
||||||
|
func (_m *GroupStore) GetAllGroupSyncablesByGroupId(groupID string, syncableType model.GroupSyncableType) store.StoreChannel {
|
||||||
|
ret := _m.Called(groupID, syncableType)
|
||||||
|
|
||||||
|
var r0 store.StoreChannel
|
||||||
|
if rf, ok := ret.Get(0).(func(string, model.GroupSyncableType) store.StoreChannel); ok {
|
||||||
|
r0 = rf(groupID, syncableType)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(store.StoreChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetByRemoteID provides a mock function with given fields: remoteID, groupSource
|
||||||
|
func (_m *GroupStore) GetByRemoteID(remoteID string, groupSource model.GroupSource) store.StoreChannel {
|
||||||
|
ret := _m.Called(remoteID, groupSource)
|
||||||
|
|
||||||
|
var r0 store.StoreChannel
|
||||||
|
if rf, ok := ret.Get(0).(func(string, model.GroupSource) store.StoreChannel); ok {
|
||||||
|
r0 = rf(remoteID, groupSource)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(store.StoreChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGroupSyncable provides a mock function with given fields: groupID, syncableID, syncableType
|
||||||
|
func (_m *GroupStore) GetGroupSyncable(groupID string, syncableID string, syncableType model.GroupSyncableType) store.StoreChannel {
|
||||||
|
ret := _m.Called(groupID, syncableID, syncableType)
|
||||||
|
|
||||||
|
var r0 store.StoreChannel
|
||||||
|
if rf, ok := ret.Get(0).(func(string, string, model.GroupSyncableType) store.StoreChannel); ok {
|
||||||
|
r0 = rf(groupID, syncableID, syncableType)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(store.StoreChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMemberCount provides a mock function with given fields: groupID
|
||||||
|
func (_m *GroupStore) GetMemberCount(groupID string) store.StoreChannel {
|
||||||
|
ret := _m.Called(groupID)
|
||||||
|
|
||||||
|
var r0 store.StoreChannel
|
||||||
|
if rf, ok := ret.Get(0).(func(string) store.StoreChannel); ok {
|
||||||
|
r0 = rf(groupID)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(store.StoreChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMemberUsers provides a mock function with given fields: groupID
|
||||||
|
func (_m *GroupStore) GetMemberUsers(groupID string) store.StoreChannel {
|
||||||
|
ret := _m.Called(groupID)
|
||||||
|
|
||||||
|
var r0 store.StoreChannel
|
||||||
|
if rf, ok := ret.Get(0).(func(string) store.StoreChannel); ok {
|
||||||
|
r0 = rf(groupID)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(store.StoreChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMemberUsersPage provides a mock function with given fields: groupID, offset, limit
|
||||||
|
func (_m *GroupStore) GetMemberUsersPage(groupID string, offset int, limit int) store.StoreChannel {
|
||||||
|
ret := _m.Called(groupID, offset, limit)
|
||||||
|
|
||||||
|
var r0 store.StoreChannel
|
||||||
|
if rf, ok := ret.Get(0).(func(string, int, int) store.StoreChannel); ok {
|
||||||
|
r0 = rf(groupID, offset, limit)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(store.StoreChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// PendingAutoAddChannelMembers provides a mock function with given fields: minGroupMembersCreateAt
|
||||||
|
func (_m *GroupStore) PendingAutoAddChannelMembers(minGroupMembersCreateAt int64) store.StoreChannel {
|
||||||
|
ret := _m.Called(minGroupMembersCreateAt)
|
||||||
|
|
||||||
|
var r0 store.StoreChannel
|
||||||
|
if rf, ok := ret.Get(0).(func(int64) store.StoreChannel); ok {
|
||||||
|
r0 = rf(minGroupMembersCreateAt)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(store.StoreChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// PendingAutoAddTeamMembers provides a mock function with given fields: minGroupMembersCreateAt
|
||||||
|
func (_m *GroupStore) PendingAutoAddTeamMembers(minGroupMembersCreateAt int64) store.StoreChannel {
|
||||||
|
ret := _m.Called(minGroupMembersCreateAt)
|
||||||
|
|
||||||
|
var r0 store.StoreChannel
|
||||||
|
if rf, ok := ret.Get(0).(func(int64) store.StoreChannel); ok {
|
||||||
|
r0 = rf(minGroupMembersCreateAt)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(store.StoreChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update provides a mock function with given fields: group
|
||||||
|
func (_m *GroupStore) Update(group *model.Group) store.StoreChannel {
|
||||||
|
ret := _m.Called(group)
|
||||||
|
|
||||||
|
var r0 store.StoreChannel
|
||||||
|
if rf, ok := ret.Get(0).(func(*model.Group) store.StoreChannel); ok {
|
||||||
|
r0 = rf(group)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(store.StoreChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateGroupSyncable provides a mock function with given fields: groupSyncable
|
||||||
|
func (_m *GroupStore) UpdateGroupSyncable(groupSyncable *model.GroupSyncable) store.StoreChannel {
|
||||||
|
ret := _m.Called(groupSyncable)
|
||||||
|
|
||||||
|
var r0 store.StoreChannel
|
||||||
|
if rf, ok := ret.Get(0).(func(*model.GroupSyncable) store.StoreChannel); ok {
|
||||||
|
r0 = rf(groupSyncable)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(store.StoreChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
@@ -168,6 +168,390 @@ func (_m *LayeredStoreDatabaseLayer) FileInfo() store.FileInfoStore {
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Group provides a mock function with given fields:
|
||||||
|
func (_m *LayeredStoreDatabaseLayer) Group() store.GroupStore {
|
||||||
|
ret := _m.Called()
|
||||||
|
|
||||||
|
var r0 store.GroupStore
|
||||||
|
if rf, ok := ret.Get(0).(func() store.GroupStore); ok {
|
||||||
|
r0 = rf()
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(store.GroupStore)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupCreate provides a mock function with given fields: ctx, group, hints
|
||||||
|
func (_m *LayeredStoreDatabaseLayer) GroupCreate(ctx context.Context, group *model.Group, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, group)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, *model.Group, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, group, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupCreateGroupSyncable provides a mock function with given fields: ctx, groupSyncable, hints
|
||||||
|
func (_m *LayeredStoreDatabaseLayer) GroupCreateGroupSyncable(ctx context.Context, groupSyncable *model.GroupSyncable, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, groupSyncable)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, *model.GroupSyncable, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, groupSyncable, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupCreateOrRestoreMember provides a mock function with given fields: ctx, groupID, userID, hints
|
||||||
|
func (_m *LayeredStoreDatabaseLayer) GroupCreateOrRestoreMember(ctx context.Context, groupID string, userID string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, groupID, userID)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, groupID, userID, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupDelete provides a mock function with given fields: ctx, groupID, hints
|
||||||
|
func (_m *LayeredStoreDatabaseLayer) GroupDelete(ctx context.Context, groupID string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, groupID)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, groupID, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupDeleteGroupSyncable provides a mock function with given fields: ctx, groupID, syncableID, syncableType, hints
|
||||||
|
func (_m *LayeredStoreDatabaseLayer) GroupDeleteGroupSyncable(ctx context.Context, groupID string, syncableID string, syncableType model.GroupSyncableType, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, groupID, syncableID, syncableType)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, string, model.GroupSyncableType, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, groupID, syncableID, syncableType, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupDeleteMember provides a mock function with given fields: ctx, groupID, userID, hints
|
||||||
|
func (_m *LayeredStoreDatabaseLayer) GroupDeleteMember(ctx context.Context, groupID string, userID string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, groupID, userID)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, groupID, userID, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupGet provides a mock function with given fields: ctx, groupID, hints
|
||||||
|
func (_m *LayeredStoreDatabaseLayer) GroupGet(ctx context.Context, groupID string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, groupID)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, groupID, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupGetAllBySource provides a mock function with given fields: ctx, groupSource, hints
|
||||||
|
func (_m *LayeredStoreDatabaseLayer) GroupGetAllBySource(ctx context.Context, groupSource model.GroupSource, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, groupSource)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, model.GroupSource, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, groupSource, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupGetAllGroupSyncablesByGroup provides a mock function with given fields: ctx, groupID, syncableType, hints
|
||||||
|
func (_m *LayeredStoreDatabaseLayer) GroupGetAllGroupSyncablesByGroup(ctx context.Context, groupID string, syncableType model.GroupSyncableType, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, groupID, syncableType)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, model.GroupSyncableType, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, groupID, syncableType, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupGetByRemoteID provides a mock function with given fields: ctx, remoteID, groupSource, hints
|
||||||
|
func (_m *LayeredStoreDatabaseLayer) GroupGetByRemoteID(ctx context.Context, remoteID string, groupSource model.GroupSource, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, remoteID, groupSource)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, model.GroupSource, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, remoteID, groupSource, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupGetGroupSyncable provides a mock function with given fields: ctx, groupID, syncableID, syncableType, hints
|
||||||
|
func (_m *LayeredStoreDatabaseLayer) GroupGetGroupSyncable(ctx context.Context, groupID string, syncableID string, syncableType model.GroupSyncableType, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, groupID, syncableID, syncableType)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, string, model.GroupSyncableType, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, groupID, syncableID, syncableType, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupGetMemberCount provides a mock function with given fields: ctx, groupID, hints
|
||||||
|
func (_m *LayeredStoreDatabaseLayer) GroupGetMemberCount(ctx context.Context, groupID string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, groupID)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, groupID, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupGetMemberUsers provides a mock function with given fields: ctx, groupID, hints
|
||||||
|
func (_m *LayeredStoreDatabaseLayer) GroupGetMemberUsers(ctx context.Context, groupID string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, groupID)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, groupID, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupGetMemberUsersPage provides a mock function with given fields: ctx, groupID, offset, limit, hints
|
||||||
|
func (_m *LayeredStoreDatabaseLayer) GroupGetMemberUsersPage(ctx context.Context, groupID string, offset int, limit int, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, groupID, offset, limit)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, int, int, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, groupID, offset, limit, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupUpdate provides a mock function with given fields: ctx, group, hints
|
||||||
|
func (_m *LayeredStoreDatabaseLayer) GroupUpdate(ctx context.Context, group *model.Group, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, group)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, *model.Group, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, group, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupUpdateGroupSyncable provides a mock function with given fields: ctx, groupSyncable, hints
|
||||||
|
func (_m *LayeredStoreDatabaseLayer) GroupUpdateGroupSyncable(ctx context.Context, groupSyncable *model.GroupSyncable, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, groupSyncable)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, *model.GroupSyncable, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, groupSyncable, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
// Job provides a mock function with given fields:
|
// Job provides a mock function with given fields:
|
||||||
func (_m *LayeredStoreDatabaseLayer) Job() store.JobStore {
|
func (_m *LayeredStoreDatabaseLayer) Job() store.JobStore {
|
||||||
ret := _m.Called()
|
ret := _m.Called()
|
||||||
@@ -242,6 +626,52 @@ func (_m *LayeredStoreDatabaseLayer) OAuth() store.OAuthStore {
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PendingAutoAddChannelMembers provides a mock function with given fields: ctx, minGroupMembersCreateAt, hints
|
||||||
|
func (_m *LayeredStoreDatabaseLayer) PendingAutoAddChannelMembers(ctx context.Context, minGroupMembersCreateAt int64, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, minGroupMembersCreateAt)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, int64, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, minGroupMembersCreateAt, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// PendingAutoAddTeamMembers provides a mock function with given fields: ctx, minGroupMembersCreateAt, hints
|
||||||
|
func (_m *LayeredStoreDatabaseLayer) PendingAutoAddTeamMembers(ctx context.Context, minGroupMembersCreateAt int64, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, minGroupMembersCreateAt)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, int64, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, minGroupMembersCreateAt, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
// Plugin provides a mock function with given fields:
|
// Plugin provides a mock function with given fields:
|
||||||
func (_m *LayeredStoreDatabaseLayer) Plugin() store.PluginStore {
|
func (_m *LayeredStoreDatabaseLayer) Plugin() store.PluginStore {
|
||||||
ret := _m.Called()
|
ret := _m.Called()
|
||||||
|
|||||||
@@ -14,6 +14,374 @@ type LayeredStoreSupplier struct {
|
|||||||
mock.Mock
|
mock.Mock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GroupCreate provides a mock function with given fields: ctx, group, hints
|
||||||
|
func (_m *LayeredStoreSupplier) GroupCreate(ctx context.Context, group *model.Group, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, group)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, *model.Group, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, group, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupCreateGroupSyncable provides a mock function with given fields: ctx, groupSyncable, hints
|
||||||
|
func (_m *LayeredStoreSupplier) GroupCreateGroupSyncable(ctx context.Context, groupSyncable *model.GroupSyncable, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, groupSyncable)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, *model.GroupSyncable, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, groupSyncable, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupCreateOrRestoreMember provides a mock function with given fields: ctx, groupID, userID, hints
|
||||||
|
func (_m *LayeredStoreSupplier) GroupCreateOrRestoreMember(ctx context.Context, groupID string, userID string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, groupID, userID)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, groupID, userID, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupDelete provides a mock function with given fields: ctx, groupID, hints
|
||||||
|
func (_m *LayeredStoreSupplier) GroupDelete(ctx context.Context, groupID string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, groupID)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, groupID, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupDeleteGroupSyncable provides a mock function with given fields: ctx, groupID, syncableID, syncableType, hints
|
||||||
|
func (_m *LayeredStoreSupplier) GroupDeleteGroupSyncable(ctx context.Context, groupID string, syncableID string, syncableType model.GroupSyncableType, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, groupID, syncableID, syncableType)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, string, model.GroupSyncableType, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, groupID, syncableID, syncableType, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupDeleteMember provides a mock function with given fields: ctx, groupID, userID, hints
|
||||||
|
func (_m *LayeredStoreSupplier) GroupDeleteMember(ctx context.Context, groupID string, userID string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, groupID, userID)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, groupID, userID, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupGet provides a mock function with given fields: ctx, groupID, hints
|
||||||
|
func (_m *LayeredStoreSupplier) GroupGet(ctx context.Context, groupID string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, groupID)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, groupID, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupGetAllBySource provides a mock function with given fields: ctx, groupSource, hints
|
||||||
|
func (_m *LayeredStoreSupplier) GroupGetAllBySource(ctx context.Context, groupSource model.GroupSource, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, groupSource)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, model.GroupSource, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, groupSource, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupGetAllGroupSyncablesByGroup provides a mock function with given fields: ctx, groupID, syncableType, hints
|
||||||
|
func (_m *LayeredStoreSupplier) GroupGetAllGroupSyncablesByGroup(ctx context.Context, groupID string, syncableType model.GroupSyncableType, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, groupID, syncableType)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, model.GroupSyncableType, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, groupID, syncableType, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupGetByRemoteID provides a mock function with given fields: ctx, remoteID, groupSource, hints
|
||||||
|
func (_m *LayeredStoreSupplier) GroupGetByRemoteID(ctx context.Context, remoteID string, groupSource model.GroupSource, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, remoteID, groupSource)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, model.GroupSource, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, remoteID, groupSource, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupGetGroupSyncable provides a mock function with given fields: ctx, groupID, syncableID, syncableType, hints
|
||||||
|
func (_m *LayeredStoreSupplier) GroupGetGroupSyncable(ctx context.Context, groupID string, syncableID string, syncableType model.GroupSyncableType, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, groupID, syncableID, syncableType)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, string, model.GroupSyncableType, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, groupID, syncableID, syncableType, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupGetMemberCount provides a mock function with given fields: ctx, groupID, hints
|
||||||
|
func (_m *LayeredStoreSupplier) GroupGetMemberCount(ctx context.Context, groupID string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, groupID)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, groupID, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupGetMemberUsers provides a mock function with given fields: ctx, groupID, hints
|
||||||
|
func (_m *LayeredStoreSupplier) GroupGetMemberUsers(ctx context.Context, groupID string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, groupID)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, groupID, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupGetMemberUsersPage provides a mock function with given fields: ctx, groupID, offset, limit, hints
|
||||||
|
func (_m *LayeredStoreSupplier) GroupGetMemberUsersPage(ctx context.Context, groupID string, offset int, limit int, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, groupID, offset, limit)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, int, int, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, groupID, offset, limit, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupUpdate provides a mock function with given fields: ctx, group, hints
|
||||||
|
func (_m *LayeredStoreSupplier) GroupUpdate(ctx context.Context, group *model.Group, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, group)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, *model.Group, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, group, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupUpdateGroupSyncable provides a mock function with given fields: ctx, groupSyncable, hints
|
||||||
|
func (_m *LayeredStoreSupplier) GroupUpdateGroupSyncable(ctx context.Context, groupSyncable *model.GroupSyncable, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, groupSyncable)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, *model.GroupSyncable, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, groupSyncable, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
// Next provides a mock function with given fields:
|
// Next provides a mock function with given fields:
|
||||||
func (_m *LayeredStoreSupplier) Next() store.LayeredStoreSupplier {
|
func (_m *LayeredStoreSupplier) Next() store.LayeredStoreSupplier {
|
||||||
ret := _m.Called()
|
ret := _m.Called()
|
||||||
@@ -30,6 +398,52 @@ func (_m *LayeredStoreSupplier) Next() store.LayeredStoreSupplier {
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PendingAutoAddChannelMembers provides a mock function with given fields: ctx, minGroupMembersCreateAt, hints
|
||||||
|
func (_m *LayeredStoreSupplier) PendingAutoAddChannelMembers(ctx context.Context, minGroupMembersCreateAt int64, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, minGroupMembersCreateAt)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, int64, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, minGroupMembersCreateAt, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// PendingAutoAddTeamMembers provides a mock function with given fields: ctx, minGroupMembersCreateAt, hints
|
||||||
|
func (_m *LayeredStoreSupplier) PendingAutoAddTeamMembers(ctx context.Context, minGroupMembersCreateAt int64, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
|
_va := make([]interface{}, len(hints))
|
||||||
|
for _i := range hints {
|
||||||
|
_va[_i] = hints[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, minGroupMembersCreateAt)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
var r0 *store.LayeredStoreSupplierResult
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, int64, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||||
|
r0 = rf(ctx, minGroupMembersCreateAt, hints...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
// ReactionDelete provides a mock function with given fields: ctx, reaction, hints
|
// ReactionDelete provides a mock function with given fields: ctx, reaction, hints
|
||||||
func (_m *LayeredStoreSupplier) ReactionDelete(ctx context.Context, reaction *model.Reaction, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
func (_m *LayeredStoreSupplier) ReactionDelete(ctx context.Context, reaction *model.Reaction, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||||
_va := make([]interface{}, len(hints))
|
_va := make([]interface{}, len(hints))
|
||||||
|
|||||||
@@ -166,6 +166,22 @@ func (_m *Store) FileInfo() store.FileInfoStore {
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Group provides a mock function with given fields:
|
||||||
|
func (_m *Store) Group() store.GroupStore {
|
||||||
|
ret := _m.Called()
|
||||||
|
|
||||||
|
var r0 store.GroupStore
|
||||||
|
if rf, ok := ret.Get(0).(func() store.GroupStore); ok {
|
||||||
|
r0 = rf()
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(store.GroupStore)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
// Job provides a mock function with given fields:
|
// Job provides a mock function with given fields:
|
||||||
func (_m *Store) Job() store.JobStore {
|
func (_m *Store) Job() store.JobStore {
|
||||||
ret := _m.Called()
|
ret := _m.Called()
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ type Store struct {
|
|||||||
RoleStore mocks.RoleStore
|
RoleStore mocks.RoleStore
|
||||||
SchemeStore mocks.SchemeStore
|
SchemeStore mocks.SchemeStore
|
||||||
TermsOfServiceStore mocks.TermsOfServiceStore
|
TermsOfServiceStore mocks.TermsOfServiceStore
|
||||||
|
GroupStore mocks.GroupStore
|
||||||
UserTermsOfServiceStore mocks.UserTermsOfServiceStore
|
UserTermsOfServiceStore mocks.UserTermsOfServiceStore
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,6 +80,7 @@ func (s *Store) UserTermsOfService() store.UserTermsOfServiceStore { return &s.U
|
|||||||
func (s *Store) ChannelMemberHistory() store.ChannelMemberHistoryStore {
|
func (s *Store) ChannelMemberHistory() store.ChannelMemberHistoryStore {
|
||||||
return &s.ChannelMemberHistoryStore
|
return &s.ChannelMemberHistoryStore
|
||||||
}
|
}
|
||||||
|
func (s *Store) Group() store.GroupStore { return &s.GroupStore }
|
||||||
func (s *Store) MarkSystemRanUnitTests() { /* do nothing */ }
|
func (s *Store) MarkSystemRanUnitTests() { /* do nothing */ }
|
||||||
func (s *Store) Close() { /* do nothing */ }
|
func (s *Store) Close() { /* do nothing */ }
|
||||||
func (s *Store) LockToMaster() { /* do nothing */ }
|
func (s *Store) LockToMaster() { /* do nothing */ }
|
||||||
|
|||||||
81
tests/add-groups.ldif
Обычный файл
81
tests/add-groups.ldif
Обычный файл
@@ -0,0 +1,81 @@
|
|||||||
|
dn: ou=testgroups,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: organizationalunit
|
||||||
|
|
||||||
|
# groupOfNames
|
||||||
|
dn: cn=outsiders,ou=testgroups,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: groupOfNames
|
||||||
|
member: uid=board.three,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
|
||||||
|
dn: cn=board,ou=testgroups,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: groupOfNames
|
||||||
|
member: uid=board.one,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
member: uid=board.two,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
member: cn=outsiders,ou=testgroups,dc=mm,dc=test,dc=com
|
||||||
|
|
||||||
|
dn: cn=executive,ou=testgroups,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: groupOfNames
|
||||||
|
member: uid=exec.one,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
member: uid=exec.two,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
member: cn=board,ou=testgroups,dc=mm,dc=test,dc=com
|
||||||
|
|
||||||
|
dn: cn=tgroup-84,ou=testgroups,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: groupOfNames
|
||||||
|
member: cn=tgroup-9,ou=testgroups,dc=mm,dc=test,dc=com
|
||||||
|
member: uid=test.five,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
|
||||||
|
dn: cn=tgroup-9,ou=testgroups,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: groupOfNames
|
||||||
|
member: cn=tgroup-97,ou=testgroups,dc=mm,dc=test,dc=com
|
||||||
|
|
||||||
|
dn: cn=tgroup-97,ou=testgroups,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: groupOfNames
|
||||||
|
member: uid=test.four,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
|
||||||
|
# groupOfUniqueNames
|
||||||
|
dn: cn=tgroup,ou=testgroups,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: groupOfUniqueNames
|
||||||
|
uniqueMember: uid=test.one,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
|
||||||
|
dn: cn=ugroup,cn=tgroup,ou=testgroups,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: groupOfUniqueNames
|
||||||
|
uniqueMember: uid=test.two,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
|
||||||
|
dn: cn=vgroup,cn=tgroup,ou=testgroups,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: groupOfUniqueNames
|
||||||
|
uniqueMember: uid=test.three,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
|
||||||
|
# Adds a group with a cycle
|
||||||
|
dn: cn=team-one-a,ou=testgroups,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: groupOfUniqueNames
|
||||||
|
uniqueMember: uid=dev.four,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
uniqueMember: cn=developers,ou=testgroups,dc=mm,dc=test,dc=com
|
||||||
|
|
||||||
|
dn: cn=team-one,ou=testgroups,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: groupOfUniqueNames
|
||||||
|
uniqueMember: uid=dev.one,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
uniqueMember: uid=dev.three,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
uniqueMember: cn=team-one-a,ou=testgroups,dc=mm,dc=test,dc=com
|
||||||
|
|
||||||
|
dn: cn=team-two,ou=testgroups,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: groupOfUniqueNames
|
||||||
|
uniqueMember: uid=dev.two,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
|
||||||
|
dn: cn=developers,ou=testgroups,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: groupOfUniqueNames
|
||||||
|
uniqueMember: uid=dev-ops.one,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
uniqueMember: cn=team-one,ou=testgroups,dc=mm,dc=test,dc=com
|
||||||
|
uniqueMember: cn=team-two,ou=testgroups,dc=mm,dc=test,dc=com
|
||||||
130
tests/add-users.ldif
Обычный файл
130
tests/add-users.ldif
Обычный файл
@@ -0,0 +1,130 @@
|
|||||||
|
dn: ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: organizationalunit
|
||||||
|
|
||||||
|
# generic test users
|
||||||
|
dn: uid=test.one,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: iNetOrgPerson
|
||||||
|
sn: User
|
||||||
|
cn: Test1
|
||||||
|
mail: success+testone@simulator.amazonses.com
|
||||||
|
userPassword: Password1
|
||||||
|
|
||||||
|
dn: uid=test.two,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: iNetOrgPerson
|
||||||
|
sn: User
|
||||||
|
cn: Test2
|
||||||
|
mail: success+testtwo@simulator.amazonses.com
|
||||||
|
userPassword: Password1
|
||||||
|
|
||||||
|
dn: uid=test.three,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: iNetOrgPerson
|
||||||
|
sn: User
|
||||||
|
cn: Test3
|
||||||
|
mail: success+testthree@simulator.amazonses.com
|
||||||
|
userPassword: Password1
|
||||||
|
|
||||||
|
dn: uid=test.four,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: iNetOrgPerson
|
||||||
|
sn: User
|
||||||
|
cn: Test4
|
||||||
|
mail: success+testfour@simulator.amazonses.com
|
||||||
|
userPassword: Password1
|
||||||
|
|
||||||
|
dn: uid=test.five,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: iNetOrgPerson
|
||||||
|
sn: User
|
||||||
|
cn: Test5
|
||||||
|
mail: success+testfive@simulator.amazonses.com
|
||||||
|
userPassword: Password1
|
||||||
|
|
||||||
|
|
||||||
|
# developers
|
||||||
|
dn: uid=dev-ops.one,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: iNetOrgPerson
|
||||||
|
sn: User
|
||||||
|
cn: Dev3
|
||||||
|
mail: success+devopsone@simulator.amazonses.com
|
||||||
|
userPassword: Password1
|
||||||
|
|
||||||
|
dn: uid=dev.one,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: iNetOrgPerson
|
||||||
|
sn: User
|
||||||
|
cn: Dev1
|
||||||
|
mail: success+devone@simulator.amazonses.com
|
||||||
|
userPassword: Password1
|
||||||
|
|
||||||
|
dn: uid=dev.two,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: iNetOrgPerson
|
||||||
|
sn: User
|
||||||
|
cn: Dev2
|
||||||
|
mail: success+devtwo@simulator.amazonses.com
|
||||||
|
userPassword: Password1
|
||||||
|
|
||||||
|
dn: uid=dev.three,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: iNetOrgPerson
|
||||||
|
sn: User
|
||||||
|
cn: Dev3
|
||||||
|
mail: success+devthree@simulator.amazonses.com
|
||||||
|
userPassword: Password1
|
||||||
|
|
||||||
|
dn: uid=dev.four,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: iNetOrgPerson
|
||||||
|
sn: User
|
||||||
|
cn: Dev4
|
||||||
|
mail: success+devfour@simulator.amazonses.com
|
||||||
|
userPassword: Password1
|
||||||
|
|
||||||
|
|
||||||
|
# executive
|
||||||
|
dn: uid=exec.one,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: iNetOrgPerson
|
||||||
|
sn: User
|
||||||
|
cn: Exec1
|
||||||
|
mail: success+execone@simulator.amazonses.com
|
||||||
|
userPassword: Password1
|
||||||
|
|
||||||
|
dn: uid=exec.two,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: iNetOrgPerson
|
||||||
|
sn: User
|
||||||
|
cn: Exec2
|
||||||
|
mail: success+exectwo@simulator.amazonses.com
|
||||||
|
userPassword: Password1
|
||||||
|
|
||||||
|
|
||||||
|
# board of directors
|
||||||
|
dn: uid=board.one,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: iNetOrgPerson
|
||||||
|
sn: User
|
||||||
|
cn: Board1
|
||||||
|
mail: success+boardone@simulator.amazonses.com
|
||||||
|
userPassword: Password1
|
||||||
|
|
||||||
|
dn: uid=board.two,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: iNetOrgPerson
|
||||||
|
sn: User
|
||||||
|
cn: Board2
|
||||||
|
mail: success+boardtwo@simulator.amazonses.com
|
||||||
|
userPassword: Password1
|
||||||
|
|
||||||
|
dn: uid=board.three,ou=testusers,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: iNetOrgPerson
|
||||||
|
sn: User
|
||||||
|
cn: Board3
|
||||||
|
mail: success+boardthree@simulator.amazonses.com
|
||||||
|
userPassword: Password1
|
||||||
Двоичные данные
tests/qa-data-graph.png
Обычный файл
Двоичные данные
tests/qa-data-graph.png
Обычный файл
Двоичный файл не отображается.
|
После Ширина: | Высота: | Размер: 40 KiB |
103
tests/qa-data.ldif
Обычный файл
103
tests/qa-data.ldif
Обычный файл
@@ -0,0 +1,103 @@
|
|||||||
|
dn: ou=groups,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: organizationalunit
|
||||||
|
|
||||||
|
dn: ou=users,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: organizationalunit
|
||||||
|
|
||||||
|
dn: uid=corey.test,ou=users,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: iNetOrgPerson
|
||||||
|
cn: Corey
|
||||||
|
sn: Test
|
||||||
|
mail: corey.test@simulator.amazonses.com
|
||||||
|
userPassword: Password1
|
||||||
|
|
||||||
|
dn: uid=william.test,ou=users,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: iNetOrgPerson
|
||||||
|
cn: William
|
||||||
|
sn: Test
|
||||||
|
mail: william.test@simulator.amazonses.com
|
||||||
|
userPassword: Password1
|
||||||
|
|
||||||
|
dn: uid=linda.test,ou=users,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: iNetOrgPerson
|
||||||
|
cn: Linda
|
||||||
|
sn: Test
|
||||||
|
mail: linda.test@simulator.amazonses.com
|
||||||
|
userPassword: Password1
|
||||||
|
|
||||||
|
dn: uid=lindy.test,ou=users,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: iNetOrgPerson
|
||||||
|
cn: Lindy
|
||||||
|
sn: Test
|
||||||
|
mail: lindy.test@simulator.amazonses.com
|
||||||
|
userPassword: Password1
|
||||||
|
|
||||||
|
dn: uid=george.test,ou=users,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: iNetOrgPerson
|
||||||
|
cn: George
|
||||||
|
sn: Test
|
||||||
|
mail: george.test@simulator.amazonses.com
|
||||||
|
userPassword: Password1
|
||||||
|
|
||||||
|
dn: uid=jesus.test,ou=users,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: iNetOrgPerson
|
||||||
|
cn: Jesus
|
||||||
|
sn: Test
|
||||||
|
mail: jesus.test@simulator.amazonses.com
|
||||||
|
userPassword: Password1
|
||||||
|
|
||||||
|
dn: uid=carlos.test,ou=users,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: iNetOrgPerson
|
||||||
|
cn: Carlos
|
||||||
|
sn: Test
|
||||||
|
mail: carlos.test@simulator.amazonses.com
|
||||||
|
userPassword: Password1
|
||||||
|
|
||||||
|
dn: uid=jesse.test,ou=users,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: iNetOrgPerson
|
||||||
|
cn: Jesse
|
||||||
|
sn: Test
|
||||||
|
mail: jesse.test@simulator.amazonses.com
|
||||||
|
userPassword: Password1
|
||||||
|
|
||||||
|
dn: cn=engineering,ou=groups,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: groupOfNames
|
||||||
|
member: uid=corey.test,ou=users,dc=mm,dc=test,dc=com
|
||||||
|
member: cn=qa,ou=groups,dc=mm,dc=test,dc=com
|
||||||
|
member: cn=developers,ou=groups,dc=mm,dc=test,dc=com
|
||||||
|
|
||||||
|
dn: cn=qa,ou=groups,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: groupOfNames
|
||||||
|
member: uid=lindy.test,ou=users,dc=mm,dc=test,dc=com
|
||||||
|
member: uid=linda.test,ou=users,dc=mm,dc=test,dc=com
|
||||||
|
|
||||||
|
dn: cn=developers,ou=groups,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: groupOfNames
|
||||||
|
member: uid=william.test,ou=users,dc=mm,dc=test,dc=com
|
||||||
|
member: cn=xyz,ou=groups,dc=mm,dc=test,dc=com
|
||||||
|
member: cn=abc,ou=groups,dc=mm,dc=test,dc=com
|
||||||
|
|
||||||
|
dn: cn=xyz,ou=groups,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: groupOfNames
|
||||||
|
member: uid=jesus.test,ou=users,dc=mm,dc=test,dc=com
|
||||||
|
member: uid=george.test,ou=users,dc=mm,dc=test,dc=com
|
||||||
|
|
||||||
|
dn: cn=abc,ou=groups,dc=mm,dc=test,dc=com
|
||||||
|
changetype: add
|
||||||
|
objectclass: groupOfNames
|
||||||
|
member: uid=carlos.test,ou=users,dc=mm,dc=test,dc=com
|
||||||
|
member: uid=jesse.test,ou=users,dc=mm,dc=test,dc=com
|
||||||
402
tests/test-config.json
Обычный файл
402
tests/test-config.json
Обычный файл
@@ -0,0 +1,402 @@
|
|||||||
|
{
|
||||||
|
"ServiceSettings": {
|
||||||
|
"SiteURL": "",
|
||||||
|
"WebsocketURL": "",
|
||||||
|
"LicenseFileLocation": "",
|
||||||
|
"ListenAddress": ":8065",
|
||||||
|
"ConnectionSecurity": "",
|
||||||
|
"TLSCertFile": "",
|
||||||
|
"TLSKeyFile": "",
|
||||||
|
"UseLetsEncrypt": false,
|
||||||
|
"LetsEncryptCertificateCacheFile": "./config/letsencrypt.cache",
|
||||||
|
"Forward80To443": false,
|
||||||
|
"ReadTimeout": 300,
|
||||||
|
"WriteTimeout": 300,
|
||||||
|
"MaximumLoginAttempts": 10,
|
||||||
|
"GoroutineHealthThreshold": -1,
|
||||||
|
"GoogleDeveloperKey": "",
|
||||||
|
"EnableOAuthServiceProvider": false,
|
||||||
|
"EnableIncomingWebhooks": true,
|
||||||
|
"EnableOutgoingWebhooks": true,
|
||||||
|
"EnableCommands": true,
|
||||||
|
"EnableOnlyAdminIntegrations": true,
|
||||||
|
"EnablePostUsernameOverride": false,
|
||||||
|
"EnablePostIconOverride": false,
|
||||||
|
"EnableLinkPreviews": false,
|
||||||
|
"EnableTesting": false,
|
||||||
|
"EnableDeveloper": false,
|
||||||
|
"EnableSecurityFixAlert": true,
|
||||||
|
"EnableInsecureOutgoingConnections": false,
|
||||||
|
"AllowedUntrustedInternalConnections": "",
|
||||||
|
"EnableMultifactorAuthentication": false,
|
||||||
|
"EnforceMultifactorAuthentication": false,
|
||||||
|
"EnableUserAccessTokens": false,
|
||||||
|
"AllowCorsFrom": "",
|
||||||
|
"AllowCookiesForSubdomains": false,
|
||||||
|
"SessionLengthWebInDays": 30,
|
||||||
|
"SessionLengthMobileInDays": 30,
|
||||||
|
"SessionLengthSSOInDays": 30,
|
||||||
|
"SessionCacheInMinutes": 10,
|
||||||
|
"SessionIdleTimeoutInMinutes": 0,
|
||||||
|
"WebsocketSecurePort": 443,
|
||||||
|
"WebsocketPort": 80,
|
||||||
|
"WebserverMode": "gzip",
|
||||||
|
"EnableCustomEmoji": false,
|
||||||
|
"EnableEmojiPicker": true,
|
||||||
|
"RestrictCustomEmojiCreation": "all",
|
||||||
|
"RestrictPostDelete": "all",
|
||||||
|
"AllowEditPost": "always",
|
||||||
|
"PostEditTimeLimit": -1,
|
||||||
|
"TimeBetweenUserTypingUpdatesMilliseconds": 5000,
|
||||||
|
"EnablePostSearch": true,
|
||||||
|
"EnableUserTypingMessages": true,
|
||||||
|
"EnableChannelViewedMessages": true,
|
||||||
|
"EnableUserStatuses": true,
|
||||||
|
"ExperimentalEnableAuthenticationTransfer": true,
|
||||||
|
"ClusterLogTimeoutMilliseconds": 2000,
|
||||||
|
"CloseUnusedDirectMessages": false,
|
||||||
|
"EnablePreviewFeatures": true,
|
||||||
|
"EnableTutorial": true,
|
||||||
|
"ExperimentalEnableDefaultChannelLeaveJoinMessages": true,
|
||||||
|
"ExperimentalGroupUnreadChannels": "disabled",
|
||||||
|
"ImageProxyType": "",
|
||||||
|
"ImageProxyURL": "",
|
||||||
|
"ImageProxyOptions": "",
|
||||||
|
"EnableAPITeamDeletion": false,
|
||||||
|
"ExperimentalEnableHardenedMode": false,
|
||||||
|
"ExperimentalLdapGroupSync": true
|
||||||
|
},
|
||||||
|
"TeamSettings": {
|
||||||
|
"SiteName": "Mattermost",
|
||||||
|
"MaxUsersPerTeam": 50,
|
||||||
|
"EnableTeamCreation": true,
|
||||||
|
"EnableUserCreation": true,
|
||||||
|
"EnableOpenServer": false,
|
||||||
|
"EnableUserDeactivation": false,
|
||||||
|
"RestrictCreationToDomains": "",
|
||||||
|
"EnableCustomBrand": false,
|
||||||
|
"CustomBrandText": "",
|
||||||
|
"CustomDescriptionText": "",
|
||||||
|
"RestrictDirectMessage": "any",
|
||||||
|
"RestrictTeamInvite": "all",
|
||||||
|
"RestrictPublicChannelManagement": "all",
|
||||||
|
"RestrictPrivateChannelManagement": "all",
|
||||||
|
"RestrictPublicChannelCreation": "all",
|
||||||
|
"RestrictPrivateChannelCreation": "all",
|
||||||
|
"RestrictPublicChannelDeletion": "all",
|
||||||
|
"RestrictPrivateChannelDeletion": "all",
|
||||||
|
"RestrictPrivateChannelManageMembers": "all",
|
||||||
|
"EnableXToLeaveChannelsFromLHS": false,
|
||||||
|
"UserStatusAwayTimeout": 300,
|
||||||
|
"MaxChannelsPerTeam": 2000,
|
||||||
|
"MaxNotificationsPerChannel": 1000,
|
||||||
|
"EnableConfirmNotificationsToChannel": true,
|
||||||
|
"TeammateNameDisplay": "username",
|
||||||
|
"ExperimentalEnableAutomaticReplies": false,
|
||||||
|
"ExperimentalHideTownSquareinLHS": false,
|
||||||
|
"ExperimentalTownSquareIsReadOnly": false,
|
||||||
|
"ExperimentalPrimaryTeam": ""
|
||||||
|
},
|
||||||
|
"ClientRequirements": {
|
||||||
|
"AndroidLatestVersion": "",
|
||||||
|
"AndroidMinVersion": "",
|
||||||
|
"DesktopLatestVersion": "",
|
||||||
|
"DesktopMinVersion": "",
|
||||||
|
"IosLatestVersion": "",
|
||||||
|
"IosMinVersion": ""
|
||||||
|
},
|
||||||
|
"SqlSettings": {
|
||||||
|
"DriverName": "mysql",
|
||||||
|
"DataSource": "mmuser:mostest@tcp(dockerhost:3306)/mattermost_test?charset=utf8mb4,utf8\u0026readTimeout=30s\u0026writeTimeout=30s",
|
||||||
|
"DataSourceReplicas": [],
|
||||||
|
"DataSourceSearchReplicas": [],
|
||||||
|
"MaxIdleConns": 20,
|
||||||
|
"MaxOpenConns": 300,
|
||||||
|
"Trace": false,
|
||||||
|
"AtRestEncryptKey": "jdh9iergmse3w9mt53snasugmmi9r6it",
|
||||||
|
"QueryTimeout": 30
|
||||||
|
},
|
||||||
|
"LogSettings": {
|
||||||
|
"EnableConsole": true,
|
||||||
|
"ConsoleLevel": "DEBUG",
|
||||||
|
"ConsoleJson": true,
|
||||||
|
"EnableFile": true,
|
||||||
|
"FileLevel": "INFO",
|
||||||
|
"FileJson": true,
|
||||||
|
"FileLocation": "",
|
||||||
|
"EnableWebhookDebugging": true,
|
||||||
|
"EnableDiagnostics": true
|
||||||
|
},
|
||||||
|
"PasswordSettings": {
|
||||||
|
"MinimumLength": 5,
|
||||||
|
"Lowercase": false,
|
||||||
|
"Number": false,
|
||||||
|
"Uppercase": false,
|
||||||
|
"Symbol": false
|
||||||
|
},
|
||||||
|
"FileSettings": {
|
||||||
|
"EnableFileAttachments": true,
|
||||||
|
"EnableMobileUpload": true,
|
||||||
|
"EnableMobileDownload": true,
|
||||||
|
"MaxFileSize": 52428800,
|
||||||
|
"DriverName": "local",
|
||||||
|
"Directory": "./data/",
|
||||||
|
"EnablePublicLink": false,
|
||||||
|
"PublicLinkSalt": "3xh7ztscuezjp1jkdjybtejrtw59xjt1",
|
||||||
|
"InitialFont": "luximbi.ttf",
|
||||||
|
"AmazonS3AccessKeyId": "",
|
||||||
|
"AmazonS3SecretAccessKey": "",
|
||||||
|
"AmazonS3Bucket": "",
|
||||||
|
"AmazonS3Region": "",
|
||||||
|
"AmazonS3Endpoint": "s3.amazonaws.com",
|
||||||
|
"AmazonS3SSL": true,
|
||||||
|
"AmazonS3SignV2": false,
|
||||||
|
"AmazonS3SSE": false,
|
||||||
|
"AmazonS3Trace": false
|
||||||
|
},
|
||||||
|
"EmailSettings": {
|
||||||
|
"EnableSignUpWithEmail": true,
|
||||||
|
"EnableSignInWithEmail": true,
|
||||||
|
"EnableSignInWithUsername": true,
|
||||||
|
"SendEmailNotifications": true,
|
||||||
|
"UseChannelInEmailNotifications": false,
|
||||||
|
"RequireEmailVerification": false,
|
||||||
|
"FeedbackName": "",
|
||||||
|
"FeedbackEmail": "test@example.com",
|
||||||
|
"FeedbackOrganization": "",
|
||||||
|
"EnableSMTPAuth": false,
|
||||||
|
"SMTPUsername": "",
|
||||||
|
"SMTPPassword": "",
|
||||||
|
"SMTPServer": "dockerhost",
|
||||||
|
"SMTPPort": "2500",
|
||||||
|
"ConnectionSecurity": "",
|
||||||
|
"InviteSalt": "n3mceqsek4j5ichs5hw9sudwx3cfbtqa",
|
||||||
|
"SendPushNotifications": false,
|
||||||
|
"PushNotificationServer": "",
|
||||||
|
"PushNotificationContents": "generic",
|
||||||
|
"EnableEmailBatching": false,
|
||||||
|
"EmailBatchingBufferSize": 256,
|
||||||
|
"EmailBatchingInterval": 30,
|
||||||
|
"EnablePreviewModeBanner": true,
|
||||||
|
"SkipServerCertificateVerification": false,
|
||||||
|
"EmailNotificationContentsType": "full",
|
||||||
|
"LoginButtonColor": "",
|
||||||
|
"LoginButtonBorderColor": "",
|
||||||
|
"LoginButtonTextColor": ""
|
||||||
|
},
|
||||||
|
"RateLimitSettings": {
|
||||||
|
"Enable": false,
|
||||||
|
"PerSec": 10,
|
||||||
|
"MaxBurst": 100,
|
||||||
|
"MemoryStoreSize": 10000,
|
||||||
|
"VaryByRemoteAddr": true,
|
||||||
|
"VaryByUser": false,
|
||||||
|
"VaryByHeader": ""
|
||||||
|
},
|
||||||
|
"PrivacySettings": {
|
||||||
|
"ShowEmailAddress": true,
|
||||||
|
"ShowFullName": true
|
||||||
|
},
|
||||||
|
"SupportSettings": {
|
||||||
|
"TermsOfServiceLink": "https://about.mattermost.com/default-terms/",
|
||||||
|
"PrivacyPolicyLink": "https://about.mattermost.com/default-privacy-policy/",
|
||||||
|
"AboutLink": "https://about.mattermost.com/default-about/",
|
||||||
|
"HelpLink": "https://about.mattermost.com/default-help/",
|
||||||
|
"ReportAProblemLink": "https://about.mattermost.com/default-report-a-problem/",
|
||||||
|
"SupportEmail": "feedback@mattermost.com"
|
||||||
|
},
|
||||||
|
"AnnouncementSettings": {
|
||||||
|
"EnableBanner": false,
|
||||||
|
"BannerText": "",
|
||||||
|
"BannerColor": "#f2a93b",
|
||||||
|
"BannerTextColor": "#333333",
|
||||||
|
"AllowBannerDismissal": true
|
||||||
|
},
|
||||||
|
"ThemeSettings": {
|
||||||
|
"EnableThemeSelection": true,
|
||||||
|
"DefaultTheme": "default",
|
||||||
|
"AllowCustomThemes": true,
|
||||||
|
"AllowedThemes": []
|
||||||
|
},
|
||||||
|
"GitLabSettings": {
|
||||||
|
"Enable": false,
|
||||||
|
"Secret": "",
|
||||||
|
"Id": "",
|
||||||
|
"Scope": "",
|
||||||
|
"AuthEndpoint": "",
|
||||||
|
"TokenEndpoint": "",
|
||||||
|
"UserApiEndpoint": ""
|
||||||
|
},
|
||||||
|
"GoogleSettings": {
|
||||||
|
"Enable": false,
|
||||||
|
"Secret": "",
|
||||||
|
"Id": "",
|
||||||
|
"Scope": "profile email",
|
||||||
|
"AuthEndpoint": "https://accounts.google.com/o/oauth2/v2/auth",
|
||||||
|
"TokenEndpoint": "https://www.googleapis.com/oauth2/v4/token",
|
||||||
|
"UserApiEndpoint": "https://www.googleapis.com/plus/v1/people/me"
|
||||||
|
},
|
||||||
|
"Office365Settings": {
|
||||||
|
"Enable": false,
|
||||||
|
"Secret": "",
|
||||||
|
"Id": "",
|
||||||
|
"Scope": "User.Read",
|
||||||
|
"AuthEndpoint": "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
|
||||||
|
"TokenEndpoint": "https://login.microsoftonline.com/common/oauth2/v2.0/token",
|
||||||
|
"UserApiEndpoint": "https://graph.microsoft.com/v1.0/me"
|
||||||
|
},
|
||||||
|
"LdapSettings": {
|
||||||
|
"Enable": false,
|
||||||
|
"EnableSync": false,
|
||||||
|
"LdapServer": "",
|
||||||
|
"LdapPort": 389,
|
||||||
|
"ConnectionSecurity": "",
|
||||||
|
"BaseDN": "",
|
||||||
|
"BindUsername": "",
|
||||||
|
"BindPassword": "",
|
||||||
|
"UserFilter": "",
|
||||||
|
"FirstNameAttribute": "",
|
||||||
|
"LastNameAttribute": "",
|
||||||
|
"EmailAttribute": "",
|
||||||
|
"UsernameAttribute": "",
|
||||||
|
"NicknameAttribute": "",
|
||||||
|
"IdAttribute": "",
|
||||||
|
"PositionAttribute": "",
|
||||||
|
"LoginIdAttribute": "",
|
||||||
|
"SyncIntervalMinutes": 60,
|
||||||
|
"SkipCertificateVerification": false,
|
||||||
|
"QueryTimeout": 60,
|
||||||
|
"MaxPageSize": 0,
|
||||||
|
"LoginFieldName": "",
|
||||||
|
"LoginButtonColor": "",
|
||||||
|
"LoginButtonBorderColor": "",
|
||||||
|
"LoginButtonTextColor": ""
|
||||||
|
},
|
||||||
|
"ComplianceSettings": {
|
||||||
|
"Enable": false,
|
||||||
|
"Directory": "./data/",
|
||||||
|
"EnableDaily": false
|
||||||
|
},
|
||||||
|
"LocalizationSettings": {
|
||||||
|
"DefaultServerLocale": "en",
|
||||||
|
"DefaultClientLocale": "en",
|
||||||
|
"AvailableLocales": ""
|
||||||
|
},
|
||||||
|
"SamlSettings": {
|
||||||
|
"Enable": false,
|
||||||
|
"EnableSyncWithLdap": false,
|
||||||
|
"Verify": true,
|
||||||
|
"Encrypt": true,
|
||||||
|
"IdpUrl": "",
|
||||||
|
"IdpDescriptorUrl": "",
|
||||||
|
"AssertionConsumerServiceURL": "",
|
||||||
|
"ScopingIDPProviderId": "",
|
||||||
|
"ScopingIDPName": "",
|
||||||
|
"IdpCertificateFile": "",
|
||||||
|
"PublicCertificateFile": "",
|
||||||
|
"PrivateKeyFile": "",
|
||||||
|
"FirstNameAttribute": "",
|
||||||
|
"LastNameAttribute": "",
|
||||||
|
"EmailAttribute": "",
|
||||||
|
"UsernameAttribute": "",
|
||||||
|
"NicknameAttribute": "",
|
||||||
|
"LocaleAttribute": "",
|
||||||
|
"PositionAttribute": "",
|
||||||
|
"LoginButtonText": "With SAML",
|
||||||
|
"LoginButtonColor": "",
|
||||||
|
"LoginButtonBorderColor": "",
|
||||||
|
"LoginButtonTextColor": ""
|
||||||
|
},
|
||||||
|
"NativeAppSettings": {
|
||||||
|
"AppDownloadLink": "https://about.mattermost.com/downloads/",
|
||||||
|
"AndroidAppDownloadLink": "https://about.mattermost.com/mattermost-android-app/",
|
||||||
|
"IosAppDownloadLink": "https://about.mattermost.com/mattermost-ios-app/"
|
||||||
|
},
|
||||||
|
"ClusterSettings": {
|
||||||
|
"Enable": false,
|
||||||
|
"ClusterName": "",
|
||||||
|
"OverrideHostname": "",
|
||||||
|
"UseIpAddress": true,
|
||||||
|
"UseExperimentalGossip": false,
|
||||||
|
"ReadOnlyConfig": true,
|
||||||
|
"GossipPort": 8074,
|
||||||
|
"StreamingPort": 8075,
|
||||||
|
"MaxIdleConns": 100,
|
||||||
|
"MaxIdleConnsPerHost": 128,
|
||||||
|
"IdleConnTimeoutMilliseconds": 90000
|
||||||
|
},
|
||||||
|
"MetricsSettings": {
|
||||||
|
"Enable": false,
|
||||||
|
"BlockProfileRate": 0,
|
||||||
|
"ListenAddress": ":8067"
|
||||||
|
},
|
||||||
|
"ExperimentalSettings": {
|
||||||
|
"ClientSideCertEnable": false,
|
||||||
|
"ClientSideCertCheck": "secondary"
|
||||||
|
},
|
||||||
|
"AnalyticsSettings": {
|
||||||
|
"MaxUsersForStatistics": 2500
|
||||||
|
},
|
||||||
|
"ElasticsearchSettings": {
|
||||||
|
"ConnectionUrl": "http://dockerhost:9200",
|
||||||
|
"Username": "elastic",
|
||||||
|
"Password": "changeme",
|
||||||
|
"EnableIndexing": false,
|
||||||
|
"EnableSearching": false,
|
||||||
|
"Sniff": true,
|
||||||
|
"PostIndexReplicas": 1,
|
||||||
|
"PostIndexShards": 1,
|
||||||
|
"AggregatePostsAfterDays": 365,
|
||||||
|
"PostsAggregatorJobStartTime": "03:00",
|
||||||
|
"IndexPrefix": "",
|
||||||
|
"LiveIndexingBatchSize": 1,
|
||||||
|
"BulkIndexingTimeWindowSeconds": 3600,
|
||||||
|
"RequestTimeoutSeconds": 30
|
||||||
|
},
|
||||||
|
"DataRetentionSettings": {
|
||||||
|
"EnableMessageDeletion": false,
|
||||||
|
"EnableFileDeletion": false,
|
||||||
|
"MessageRetentionDays": 365,
|
||||||
|
"FileRetentionDays": 365,
|
||||||
|
"DeletionJobStartTime": "02:00"
|
||||||
|
},
|
||||||
|
"MessageExportSettings": {
|
||||||
|
"EnableExport": false,
|
||||||
|
"ExportFormat": "actiance",
|
||||||
|
"DailyRunTime": "01:00",
|
||||||
|
"ExportFromTimestamp": 0,
|
||||||
|
"BatchSize": 10000,
|
||||||
|
"GlobalRelaySettings": {
|
||||||
|
"CustomerType": "A9",
|
||||||
|
"SmtpUsername": "",
|
||||||
|
"SmtpPassword": "",
|
||||||
|
"EmailAddress": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"JobSettings": {
|
||||||
|
"RunJobs": true,
|
||||||
|
"RunScheduler": true
|
||||||
|
},
|
||||||
|
"PluginSettings": {
|
||||||
|
"Enable": true,
|
||||||
|
"EnableUploads": true,
|
||||||
|
"Directory": "./test-plugins",
|
||||||
|
"ClientDirectory": "./test-client-plugins",
|
||||||
|
"Plugins": {},
|
||||||
|
"PluginStates": {
|
||||||
|
"jira": {
|
||||||
|
"Enable": true
|
||||||
|
},
|
||||||
|
"testplugin": {
|
||||||
|
"Enable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"DisplaySettings": {
|
||||||
|
"CustomUrlSchemes": [],
|
||||||
|
"ExperimentalTimezone": false
|
||||||
|
},
|
||||||
|
"TimezoneSettings": {
|
||||||
|
"SupportedTimezonesPath": "timezones.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
Двоичные данные
tests/test-data-graph.png
Обычный файл
Двоичные данные
tests/test-data-graph.png
Обычный файл
Двоичный файл не отображается.
|
После Ширина: | Высота: | Размер: 38 KiB |
@@ -133,6 +133,7 @@ func GetClientLicense(l *model.License) map[string]string {
|
|||||||
props["SkuShortName"] = l.SkuShortName
|
props["SkuShortName"] = l.SkuShortName
|
||||||
props["Users"] = strconv.Itoa(*l.Features.Users)
|
props["Users"] = strconv.Itoa(*l.Features.Users)
|
||||||
props["LDAP"] = strconv.FormatBool(*l.Features.LDAP)
|
props["LDAP"] = strconv.FormatBool(*l.Features.LDAP)
|
||||||
|
props["LDAPGroups"] = strconv.FormatBool(*l.Features.LDAPGroups)
|
||||||
props["MFA"] = strconv.FormatBool(*l.Features.MFA)
|
props["MFA"] = strconv.FormatBool(*l.Features.MFA)
|
||||||
props["SAML"] = strconv.FormatBool(*l.Features.SAML)
|
props["SAML"] = strconv.FormatBool(*l.Features.SAML)
|
||||||
props["Cluster"] = strconv.FormatBool(*l.Features.Cluster)
|
props["Cluster"] = strconv.FormatBool(*l.Features.Cluster)
|
||||||
|
|||||||
@@ -519,3 +519,47 @@ func (c *Context) RequireRoleName() *Context {
|
|||||||
|
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Context) RequireGroupId() *Context {
|
||||||
|
if c.Err != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.Params.GroupId) != 26 {
|
||||||
|
c.SetInvalidUrlParam("group_id")
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) RequireRemoteId() *Context {
|
||||||
|
if c.Err != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.Params.RemoteId) == 0 {
|
||||||
|
c.SetInvalidUrlParam("remote_id")
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) RequireSyncableId() *Context {
|
||||||
|
if c.Err != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.Params.SyncableId) != 26 {
|
||||||
|
c.SetInvalidUrlParam("syncable_id")
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) RequireSyncableType() *Context {
|
||||||
|
if c.Err != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Params.SyncableType != model.GroupSyncableTypeTeam && c.Params.SyncableType != model.GroupSyncableTypeChannel {
|
||||||
|
c.SetInvalidUrlParam("syncable_type")
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/mattermost/mattermost-server/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -49,10 +50,14 @@ type Params struct {
|
|||||||
RoleName string
|
RoleName string
|
||||||
SchemeId string
|
SchemeId string
|
||||||
Scope string
|
Scope string
|
||||||
|
GroupId string
|
||||||
Page int
|
Page int
|
||||||
PerPage int
|
PerPage int
|
||||||
LogsPerPage int
|
LogsPerPage int
|
||||||
Permanent bool
|
Permanent bool
|
||||||
|
RemoteId string
|
||||||
|
SyncableId string
|
||||||
|
SyncableType model.GroupSyncableType
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParamsFromRequest(r *http.Request) *Params {
|
func ParamsFromRequest(r *http.Request) *Params {
|
||||||
@@ -173,6 +178,14 @@ func ParamsFromRequest(r *http.Request) *Params {
|
|||||||
params.SchemeId = val
|
params.SchemeId = val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if val, ok := props["group_id"]; ok {
|
||||||
|
params.GroupId = val
|
||||||
|
}
|
||||||
|
|
||||||
|
if val, ok := props["remote_id"]; ok {
|
||||||
|
params.RemoteId = val
|
||||||
|
}
|
||||||
|
|
||||||
params.Scope = query.Get("scope")
|
params.Scope = query.Get("scope")
|
||||||
|
|
||||||
if val, err := strconv.Atoi(query.Get("page")); err != nil || val < 0 {
|
if val, err := strconv.Atoi(query.Get("page")); err != nil || val < 0 {
|
||||||
@@ -201,5 +214,17 @@ func ParamsFromRequest(r *http.Request) *Params {
|
|||||||
params.LogsPerPage = val
|
params.LogsPerPage = val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if val, ok := props["syncable_id"]; ok {
|
||||||
|
params.SyncableId = val
|
||||||
|
}
|
||||||
|
|
||||||
|
if val, ok := props["syncable_type"]; ok {
|
||||||
|
switch val {
|
||||||
|
case "teams":
|
||||||
|
params.SyncableType = model.GroupSyncableTypeTeam
|
||||||
|
case "channels":
|
||||||
|
params.SyncableType = model.GroupSyncableTypeChannel
|
||||||
|
}
|
||||||
|
}
|
||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user