mirror of
https://github.com/matrix-org/dendrite.git
synced 2025-12-16 03:13:11 -06:00
Implement user directory for local users #649
Signed-off-by: Anton Stuetz <opensource@ti-zero.com>
This commit is contained in:
parent
145921f207
commit
9441f0863e
|
|
@ -18,6 +18,12 @@ import (
|
|||
"context"
|
||||
"database/sql"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/userutil"
|
||||
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
|
||||
searchtypes "github.com/matrix-org/dendrite/userdirectoryapi/types"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||
)
|
||||
|
||||
|
|
@ -39,6 +45,9 @@ const insertProfileSQL = "" +
|
|||
const selectProfileByLocalpartSQL = "" +
|
||||
"SELECT localpart, display_name, avatar_url FROM account_profiles WHERE localpart = $1"
|
||||
|
||||
const selectSearchTermLikeLocalpartOrDisplayName = "" +
|
||||
"SELECT localpart, display_name, avatar_url FROM account_profiles WHERE localpart LIKE $1 OR display_name LIKE $1 LIMIT $2"
|
||||
|
||||
const setAvatarURLSQL = "" +
|
||||
"UPDATE account_profiles SET avatar_url = $1 WHERE localpart = $2"
|
||||
|
||||
|
|
@ -46,10 +55,11 @@ const setDisplayNameSQL = "" +
|
|||
"UPDATE account_profiles SET display_name = $1 WHERE localpart = $2"
|
||||
|
||||
type profilesStatements struct {
|
||||
insertProfileStmt *sql.Stmt
|
||||
selectProfileByLocalpartStmt *sql.Stmt
|
||||
setAvatarURLStmt *sql.Stmt
|
||||
setDisplayNameStmt *sql.Stmt
|
||||
insertProfileStmt *sql.Stmt
|
||||
selectProfileByLocalpartStmt *sql.Stmt
|
||||
selectSearchTermLikeLocalpartOrDisplayNameStmt *sql.Stmt
|
||||
setAvatarURLStmt *sql.Stmt
|
||||
setDisplayNameStmt *sql.Stmt
|
||||
}
|
||||
|
||||
func (s *profilesStatements) prepare(db *sql.DB) (err error) {
|
||||
|
|
@ -69,6 +79,9 @@ func (s *profilesStatements) prepare(db *sql.DB) (err error) {
|
|||
if s.setDisplayNameStmt, err = db.Prepare(setDisplayNameSQL); err != nil {
|
||||
return
|
||||
}
|
||||
if s.selectSearchTermLikeLocalpartOrDisplayNameStmt, err = db.Prepare(selectSearchTermLikeLocalpartOrDisplayName); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -92,6 +105,37 @@ func (s *profilesStatements) selectProfileByLocalpart(
|
|||
return &profile, nil
|
||||
}
|
||||
|
||||
func (s *profilesStatements) selectSearchTermLikeLocalpartOrDisplayName(
|
||||
ctx context.Context, searchTerm string, limit int8, serverName gomatrixserverlib.ServerName,
|
||||
) (*[]searchtypes.SearchResult, bool, error) {
|
||||
//increase limit by one to find out if the query was limited
|
||||
rows, err := s.selectSearchTermLikeLocalpartOrDisplayNameStmt.QueryContext(ctx, searchTerm, limit+1)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
searchResults := []searchtypes.SearchResult{}
|
||||
counter := int8(1)
|
||||
limited := false
|
||||
for rows.Next() {
|
||||
if counter > limit {
|
||||
limited = true
|
||||
break
|
||||
}
|
||||
var r searchtypes.SearchResult
|
||||
err = rows.Scan(
|
||||
&r.UserId, &r.DisplayName, &r.AvatarUrl,
|
||||
)
|
||||
r.UserId = userutil.MakeUserID(r.UserId, serverName)
|
||||
if err != nil {
|
||||
return &searchResults, false, err
|
||||
}
|
||||
searchResults = append(searchResults, r)
|
||||
counter++
|
||||
}
|
||||
|
||||
return &searchResults, limited, nil
|
||||
}
|
||||
|
||||
func (s *profilesStatements) setAvatarURL(
|
||||
ctx context.Context, localpart string, avatarURL string,
|
||||
) (err error) {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ import (
|
|||
"database/sql"
|
||||
"errors"
|
||||
|
||||
searchtypes "github.com/matrix-org/dendrite/userdirectoryapi/types"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||
"github.com/matrix-org/dendrite/common"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
|
|
@ -28,6 +30,69 @@ import (
|
|||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
type AccountDatabase interface {
|
||||
GetAccountByPassword(
|
||||
ctx context.Context, localpart, plaintextPassword string,
|
||||
) (*authtypes.Account, error)
|
||||
GetProfileByLocalpart(
|
||||
ctx context.Context, localpart string,
|
||||
) (*authtypes.Profile, error)
|
||||
SetAvatarURL(
|
||||
ctx context.Context, localpart string, avatarURL string,
|
||||
) error
|
||||
SetDisplayName(
|
||||
ctx context.Context, localpart string, displayName string,
|
||||
) error
|
||||
CreateAccount(
|
||||
ctx context.Context, localpart, plaintextPassword, appserviceID string,
|
||||
) (*authtypes.Account, error)
|
||||
UpdateMemberships(
|
||||
ctx context.Context, eventsToAdd []gomatrixserverlib.Event, idsToRemove []string,
|
||||
) error
|
||||
GetMembershipInRoomByLocalpart(
|
||||
ctx context.Context, localpart, roomID string,
|
||||
) (authtypes.Membership, error)
|
||||
GetMembershipsByLocalpart(
|
||||
ctx context.Context, localpart string,
|
||||
) (memberships []authtypes.Membership, err error)
|
||||
SaveAccountData(
|
||||
ctx context.Context, localpart, roomID, dataType, content string,
|
||||
) error
|
||||
GetAccountData(ctx context.Context, localpart string) (
|
||||
global []gomatrixserverlib.ClientEvent,
|
||||
rooms map[string][]gomatrixserverlib.ClientEvent,
|
||||
err error,
|
||||
)
|
||||
GetAccountDataByType(
|
||||
ctx context.Context, localpart, roomID, dataType string,
|
||||
) (data *gomatrixserverlib.ClientEvent, err error)
|
||||
GetNewNumericLocalpart(
|
||||
ctx context.Context,
|
||||
) (int64, error)
|
||||
SaveThreePIDAssociation(
|
||||
ctx context.Context, threepid, localpart, medium string,
|
||||
) (err error)
|
||||
RemoveThreePIDAssociation(
|
||||
ctx context.Context, threepid string, medium string,
|
||||
) (err error)
|
||||
GetLocalpartForThreePID(
|
||||
ctx context.Context, threepid string, medium string,
|
||||
) (localpart string, err error)
|
||||
GetThreePIDsForLocalpart(
|
||||
ctx context.Context, localpart string,
|
||||
) (threepids []authtypes.ThreePID, err error)
|
||||
GetFilter(
|
||||
ctx context.Context, localpart string, filterID string,
|
||||
) (*gomatrixserverlib.Filter, error)
|
||||
PutFilter(
|
||||
ctx context.Context, localpart string, filter *gomatrixserverlib.Filter,
|
||||
) (string, error)
|
||||
CheckAccountAvailability(ctx context.Context, localpart string) (bool, error)
|
||||
GetAccountByLocalpart(ctx context.Context, localpart string,
|
||||
) (*authtypes.Account, error)
|
||||
SearchUserIdAndDisplayName(ctx context.Context, searchTerm string, limit int8) (*[]searchtypes.SearchResult, bool, error)
|
||||
}
|
||||
|
||||
// Database represents an account database
|
||||
type Database struct {
|
||||
db *sql.DB
|
||||
|
|
@ -379,3 +444,7 @@ func (d *Database) GetAccountByLocalpart(ctx context.Context, localpart string,
|
|||
) (*authtypes.Account, error) {
|
||||
return d.accounts.selectAccountByLocalpart(ctx, localpart)
|
||||
}
|
||||
|
||||
func (d *Database) SearchUserIdAndDisplayName(ctx context.Context, searchTerm string, limit int8) (*[]searchtypes.SearchResult, bool, error) {
|
||||
return d.profiles.selectSearchTermLikeLocalpartOrDisplayName(ctx, searchTerm, limit, d.serverName)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -517,7 +517,6 @@ func handleRegistrationFlow(
|
|||
deviceDB *devices.Database,
|
||||
) util.JSONResponse {
|
||||
// TODO: Shared secret registration (create new user scripts)
|
||||
// TODO: Enable registration config flag
|
||||
// TODO: Guest account upgrading
|
||||
|
||||
// TODO: Handle loading of previous session parameters from database.
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ import (
|
|||
"flag"
|
||||
"net/http"
|
||||
|
||||
"github.com/matrix-org/dendrite/userdirectoryapi"
|
||||
|
||||
"github.com/matrix-org/dendrite/appservice"
|
||||
"github.com/matrix-org/dendrite/clientapi"
|
||||
"github.com/matrix-org/dendrite/common"
|
||||
|
|
@ -71,6 +73,7 @@ func main() {
|
|||
mediaapi.SetupMediaAPIComponent(base, deviceDB)
|
||||
publicroomsapi.SetupPublicRoomsAPIComponent(base, deviceDB)
|
||||
syncapi.SetupSyncAPIComponent(base, deviceDB, accountDB, query)
|
||||
userdirectoryapi.SetupUserDirectoryApi(base, accountDB, deviceDB)
|
||||
|
||||
httpHandler := common.WrapHandlerInCORS(base.APIMux)
|
||||
|
||||
|
|
|
|||
34
cmd/dendrite-userdirectory-api-server/main.go
Normal file
34
cmd/dendrite-userdirectory-api-server/main.go
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright 2017 Vector Creations Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/matrix-org/dendrite/common/basecomponent"
|
||||
"github.com/matrix-org/dendrite/userdirectoryapi"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cfg := basecomponent.ParseFlags()
|
||||
base := basecomponent.NewBaseDendrite(cfg, "PublicRoomsAPI")
|
||||
defer base.Close() // nolint: errcheck
|
||||
|
||||
deviceDB := base.CreateDeviceDB()
|
||||
accountDB := base.CreateAccountsDB()
|
||||
|
||||
userdirectoryapi.SetupUserDirectoryApi(base, accountDB, deviceDB)
|
||||
|
||||
base.SetupAndServeHTTP(string(base.Cfg.Bind.UserDirectoryAPI), string(base.Cfg.Listen.UserDirectoryAPI))
|
||||
|
||||
}
|
||||
|
|
@ -30,7 +30,7 @@ import (
|
|||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
jaegerconfig "github.com/uber/jaeger-client-go/config"
|
||||
jaegermetrics "github.com/uber/jaeger-lib/metrics"
|
||||
|
|
@ -206,6 +206,7 @@ type Dendrite struct {
|
|||
RoomServer Address `yaml:"room_server"`
|
||||
FederationSender Address `yaml:"federation_sender"`
|
||||
PublicRoomsAPI Address `yaml:"public_rooms_api"`
|
||||
UserDirectoryAPI Address `yaml:"user_directory_api"`
|
||||
TypingServer Address `yaml:"typing_server"`
|
||||
} `yaml:"bind"`
|
||||
|
||||
|
|
@ -219,6 +220,7 @@ type Dendrite struct {
|
|||
RoomServer Address `yaml:"room_server"`
|
||||
FederationSender Address `yaml:"federation_sender"`
|
||||
PublicRoomsAPI Address `yaml:"public_rooms_api"`
|
||||
UserDirectoryAPI Address `yaml:"user_directory_api"`
|
||||
TypingServer Address `yaml:"typing_server"`
|
||||
} `yaml:"listen"`
|
||||
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ listen:
|
|||
media_api: "localhost:7774"
|
||||
appservice_api: "localhost:7777"
|
||||
typing_server: "localhost:7778"
|
||||
user_directory_api: "localhost:7779"
|
||||
logging:
|
||||
- type: "file"
|
||||
level: "info"
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ listen:
|
|||
federation_sender: "localhost:7776"
|
||||
appservice_api: "localhost:7777"
|
||||
typing_server: "localhost:7778"
|
||||
user_directory_api: "localhost:7779"
|
||||
|
||||
# The configuration for tracing the dendrite components.
|
||||
tracing:
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ listen:
|
|||
public_rooms_api: "public_rooms_api:7775"
|
||||
federation_sender: "federation_sender:7776"
|
||||
typing_server: "typing_server:7777"
|
||||
user_directory_api: "localhost:7779"
|
||||
|
||||
# The configuration for tracing the dendrite components.
|
||||
tracing:
|
||||
|
|
|
|||
|
|
@ -134,6 +134,18 @@ services:
|
|||
networks:
|
||||
- internal
|
||||
|
||||
userdirectory_api:
|
||||
container_name: dendrite_userdirectory_api
|
||||
hostname: userdirectory_api
|
||||
entrypoint: ["bash", "./docker/services/userdirectory-api.sh"]
|
||||
build: ./
|
||||
volumes:
|
||||
- ..:/build
|
||||
depends_on:
|
||||
- postgres
|
||||
networks:
|
||||
- internal
|
||||
|
||||
federation_sender:
|
||||
container_name: dendrite_federation_sender
|
||||
hostname: federation_sender
|
||||
|
|
|
|||
5
docker/services/userdirectory-api.sh
Normal file
5
docker/services/userdirectory-api.sh
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/bash
|
||||
|
||||
bash ./docker/build.sh
|
||||
|
||||
./bin/dendrite-userdirectory-api-server --config dendrite.yaml
|
||||
5
userdirectoryapi/README.md
Normal file
5
userdirectoryapi/README.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# User Directory API
|
||||
|
||||
This server is responsible for serving requests hitting `/user_directory` as per:
|
||||
|
||||
https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-user-directory-search
|
||||
50
userdirectoryapi/routing/routing.go
Normal file
50
userdirectoryapi/routing/routing.go
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright 2019 Anton Stuetz
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package routing
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/matrix-org/dendrite/userdirectoryapi/search"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/auth/storage/devices"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/auth"
|
||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||
"github.com/matrix-org/dendrite/common"
|
||||
"github.com/matrix-org/util"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/matrix-org/dendrite/clientapi/auth/storage/accounts"
|
||||
)
|
||||
|
||||
const pathPrefixR0 = "/_matrix/client/r0"
|
||||
|
||||
func Setup(apiMux *mux.Router, accountDB *accounts.Database, deviceDB *devices.Database) {
|
||||
|
||||
r0mux := apiMux.PathPrefix(pathPrefixR0).Subrouter()
|
||||
|
||||
authData := auth.Data{
|
||||
AccountDB: nil,
|
||||
DeviceDB: deviceDB,
|
||||
AppServices: nil,
|
||||
}
|
||||
|
||||
r0mux.Handle("/user_directory/search",
|
||||
common.MakeAuthAPI("user_directory_search", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse {
|
||||
return search.Search(req, accountDB)
|
||||
})).Methods(http.MethodPost)
|
||||
|
||||
}
|
||||
80
userdirectoryapi/search/search.go
Normal file
80
userdirectoryapi/search/search.go
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
// Copyright 2019 Anton Stuetz
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package search
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/httputil"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||
searchtypes "github.com/matrix-org/dendrite/userdirectoryapi/types"
|
||||
"github.com/matrix-org/util"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/auth/storage/accounts"
|
||||
)
|
||||
|
||||
const (
|
||||
DEFAULT_LIMIT = 10
|
||||
)
|
||||
|
||||
type userSearchRequest struct {
|
||||
SearchTerm string `json:"search_term"`
|
||||
Limit int8 `json:"limit"`
|
||||
}
|
||||
type userSearchResponse struct {
|
||||
Results *[]searchtypes.SearchResult `json:"results"`
|
||||
Limited bool `json:"limited"`
|
||||
}
|
||||
|
||||
func Search(req *http.Request, accountDB accounts.AccountDatabase) util.JSONResponse {
|
||||
var r userSearchRequest
|
||||
resErr := httputil.UnmarshalJSONRequest(req, &r)
|
||||
if resErr != nil {
|
||||
return *resErr
|
||||
}
|
||||
|
||||
err := validateSearchTerm(r.SearchTerm)
|
||||
if err != nil {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: jsonerror.InvalidArgumentValue(err.Error()),
|
||||
}
|
||||
}
|
||||
if r.Limit == 0 {
|
||||
r.Limit = DEFAULT_LIMIT
|
||||
}
|
||||
results, limited, err := accountDB.SearchUserIdAndDisplayName(req.Context(), r.SearchTerm+"%", r.Limit)
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
}
|
||||
return mapProfilesToResponse(results, limited)
|
||||
}
|
||||
|
||||
func validateSearchTerm(search_term string) error {
|
||||
if len(search_term) == 0 {
|
||||
return errors.New("Search term is empty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func mapProfilesToResponse(results *[]searchtypes.SearchResult, limited bool) util.JSONResponse {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusOK,
|
||||
JSON: userSearchResponse{results, limited},
|
||||
}
|
||||
|
||||
}
|
||||
224
userdirectoryapi/search/search_test.go
Normal file
224
userdirectoryapi/search/search_test.go
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
package search
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/auth/storage/accounts"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||
searchtypes "github.com/matrix-org/dendrite/userdirectoryapi/types"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
type MockAccountDatabase struct {
|
||||
limited bool
|
||||
wantError bool
|
||||
}
|
||||
|
||||
func (d *MockAccountDatabase) GetAccountByPassword(
|
||||
ctx context.Context, localpart, plaintextPassword string,
|
||||
) (*authtypes.Account, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (d *MockAccountDatabase) GetProfileByLocalpart(
|
||||
ctx context.Context, localpart string,
|
||||
) (*authtypes.Profile, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (d *MockAccountDatabase) SetAvatarURL(
|
||||
ctx context.Context, localpart string, avatarURL string,
|
||||
) error {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (d *MockAccountDatabase) SetDisplayName(
|
||||
ctx context.Context, localpart string, displayName string,
|
||||
) error {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (d *MockAccountDatabase) CreateAccount(
|
||||
ctx context.Context, localpart, plaintextPassword, appserviceID string,
|
||||
) (*authtypes.Account, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (d *MockAccountDatabase) UpdateMemberships(
|
||||
ctx context.Context, eventsToAdd []gomatrixserverlib.Event, idsToRemove []string,
|
||||
) error {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (d *MockAccountDatabase) GetMembershipInRoomByLocalpart(
|
||||
ctx context.Context, localpart, roomID string,
|
||||
) (authtypes.Membership, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (d *MockAccountDatabase) GetMembershipsByLocalpart(
|
||||
ctx context.Context, localpart string,
|
||||
) (memberships []authtypes.Membership, err error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (d *MockAccountDatabase) SaveAccountData(
|
||||
ctx context.Context, localpart, roomID, dataType, content string,
|
||||
) error {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (d *MockAccountDatabase) GetAccountData(ctx context.Context, localpart string) (
|
||||
global []gomatrixserverlib.ClientEvent,
|
||||
rooms map[string][]gomatrixserverlib.ClientEvent,
|
||||
err error,
|
||||
) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (d *MockAccountDatabase) GetAccountDataByType(
|
||||
ctx context.Context, localpart, roomID, dataType string,
|
||||
) (data *gomatrixserverlib.ClientEvent, err error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (d *MockAccountDatabase) GetNewNumericLocalpart(
|
||||
ctx context.Context,
|
||||
) (int64, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (d *MockAccountDatabase) SaveThreePIDAssociation(
|
||||
ctx context.Context, threepid, localpart, medium string,
|
||||
) (err error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (d *MockAccountDatabase) RemoveThreePIDAssociation(
|
||||
ctx context.Context, threepid string, medium string,
|
||||
) (err error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (d *MockAccountDatabase) GetLocalpartForThreePID(
|
||||
ctx context.Context, threepid string, medium string,
|
||||
) (localpart string, err error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (d *MockAccountDatabase) GetThreePIDsForLocalpart(
|
||||
ctx context.Context, localpart string,
|
||||
) (threepids []authtypes.ThreePID, err error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (d *MockAccountDatabase) GetFilter(
|
||||
ctx context.Context, localpart string, filterID string,
|
||||
) (*gomatrixserverlib.Filter, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (d *MockAccountDatabase) PutFilter(
|
||||
ctx context.Context, localpart string, filter *gomatrixserverlib.Filter,
|
||||
) (string, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (d *MockAccountDatabase) CheckAccountAvailability(ctx context.Context, localpart string) (bool, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (d *MockAccountDatabase) GetAccountByLocalpart(ctx context.Context, localpart string,
|
||||
) (*authtypes.Account, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (d *MockAccountDatabase) SearchUserIdAndDisplayName(ctx context.Context, searchTerm string, limit int8) (*[]searchtypes.SearchResult, bool, error) {
|
||||
var searchResults []searchtypes.SearchResult
|
||||
searchResults = append(searchResults, searchtypes.SearchResult{UserId: "alice:localhost", AvatarUrl: "", DisplayName: ""})
|
||||
if d.wantError {
|
||||
return nil, false, fmt.Errorf("")
|
||||
} else {
|
||||
return &searchResults, d.limited, nil
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearch(t *testing.T) {
|
||||
type testCase struct {
|
||||
name string
|
||||
wantError bool
|
||||
errorCode int
|
||||
results []string
|
||||
limited bool
|
||||
requestBody string
|
||||
}
|
||||
tt := []testCase{
|
||||
{
|
||||
"Find user do not hit limit",
|
||||
false,
|
||||
0,
|
||||
[]string{"alice:localhost"},
|
||||
false,
|
||||
"{ \"search_term\": \"alice\" }",
|
||||
},
|
||||
{
|
||||
"Find user do hit limit",
|
||||
false,
|
||||
0,
|
||||
[]string{"alice:localhost"},
|
||||
true,
|
||||
"{ \"search_term\": \"alice\" }",
|
||||
},
|
||||
{
|
||||
"Find user and fail",
|
||||
true,
|
||||
500,
|
||||
[]string{"alice:localhost"},
|
||||
false,
|
||||
"{ \"search_term\": \"alice\" }",
|
||||
},
|
||||
{
|
||||
"Find user and fail to parse request",
|
||||
true,
|
||||
400,
|
||||
[]string{"alice:localhost"},
|
||||
false,
|
||||
"{ \"search_term\": \"alicINVALID }",
|
||||
},
|
||||
}
|
||||
|
||||
setupAccountDb := func(limited bool, wantError bool) accounts.AccountDatabase {
|
||||
return &MockAccountDatabase{limited, wantError}
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
fmt.Printf("Executing %s", tc.name)
|
||||
requestBody := ioutil.NopCloser(strings.NewReader(tc.requestBody))
|
||||
req := &http.Request{Body: requestBody}
|
||||
searchResults := Search(req, setupAccountDb(tc.limited, tc.wantError))
|
||||
if tc.wantError {
|
||||
assert.EqualValues(t, searchResults.Code, tc.errorCode)
|
||||
} else {
|
||||
response := searchResults.JSON.(userSearchResponse)
|
||||
for _, result := range tc.results {
|
||||
exists := false
|
||||
for _, item := range *response.Results {
|
||||
if item.UserId == result {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.EqualValues(t, true, exists, "Expected value not found in response")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
21
userdirectoryapi/types/searchtypes.go
Normal file
21
userdirectoryapi/types/searchtypes.go
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2019 Anton Stuetz
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package searchtypes
|
||||
|
||||
type SearchResult struct {
|
||||
UserId string `json:"user_id"`
|
||||
DisplayName string `json:"display_name,omitempty"`
|
||||
AvatarUrl string `json:"avatar_url,omitempty"`
|
||||
}
|
||||
28
userdirectoryapi/userdirectoryapi.go
Normal file
28
userdirectoryapi/userdirectoryapi.go
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright 2019 Anton Stuetz
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package userdirectoryapi
|
||||
|
||||
import (
|
||||
"github.com/matrix-org/dendrite/clientapi/auth/storage/accounts"
|
||||
"github.com/matrix-org/dendrite/clientapi/auth/storage/devices"
|
||||
"github.com/matrix-org/dendrite/common/basecomponent"
|
||||
"github.com/matrix-org/dendrite/userdirectoryapi/routing"
|
||||
)
|
||||
|
||||
func SetupUserDirectoryApi(base *basecomponent.BaseDendrite, accountDB *accounts.Database, deviceDB *devices.Database) {
|
||||
|
||||
routing.Setup(base.APIMux, accountDB, deviceDB)
|
||||
|
||||
}
|
||||
Loading…
Reference in a new issue