Get room versions from database (#918)
* Retrieve room version where known in roomserver * Get room versions in alias code * Increase gocyclothreshold to 13, since we hit that number a lot * Remove gocyclo nolint from StoreEvent * Update interface to get room version from room ID instead of NID * Remove new API * Fixed this query for SQLite but not for Postgresmain
parent
1467cc10d8
commit
c2bd0b97b3
|
@ -102,7 +102,7 @@ linters-settings:
|
||||||
#local-prefixes: github.com/org/project
|
#local-prefixes: github.com/org/project
|
||||||
gocyclo:
|
gocyclo:
|
||||||
# minimal code complexity to report, 30 by default (but we recommend 10-20)
|
# minimal code complexity to report, 30 by default (but we recommend 10-20)
|
||||||
min-complexity: 12
|
min-complexity: 13
|
||||||
maligned:
|
maligned:
|
||||||
# print struct with more effective memory layout or not, false by default
|
# print struct with more effective memory layout or not, false by default
|
||||||
suggest-new: true
|
suggest-new: true
|
||||||
|
|
|
@ -46,6 +46,10 @@ type RoomserverAliasAPIDatabase interface {
|
||||||
// Remove a given room alias.
|
// Remove a given room alias.
|
||||||
// Returns an error if there was a problem talking to the database.
|
// Returns an error if there was a problem talking to the database.
|
||||||
RemoveRoomAlias(ctx context.Context, alias string) error
|
RemoveRoomAlias(ctx context.Context, alias string) error
|
||||||
|
// Look up the room version for a given room.
|
||||||
|
GetRoomVersionForRoom(
|
||||||
|
ctx context.Context, roomID string,
|
||||||
|
) (gomatrixserverlib.RoomVersion, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RoomserverAliasAPI is an implementation of alias.RoomserverAliasAPI
|
// RoomserverAliasAPI is an implementation of alias.RoomserverAliasAPI
|
||||||
|
@ -240,6 +244,11 @@ func (r *RoomserverAliasAPI) sendUpdatedAliasesEvent(
|
||||||
}
|
}
|
||||||
builder.AuthEvents = refs
|
builder.AuthEvents = refs
|
||||||
|
|
||||||
|
roomVersion, err := r.DB.GetRoomVersionForRoom(ctx, roomID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Build the event
|
// Build the event
|
||||||
eventID := fmt.Sprintf("$%s:%s", util.RandomString(16), r.Cfg.Matrix.ServerName)
|
eventID := fmt.Sprintf("$%s:%s", util.RandomString(16), r.Cfg.Matrix.ServerName)
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
@ -250,9 +259,6 @@ func (r *RoomserverAliasAPI) sendUpdatedAliasesEvent(
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Room version here
|
|
||||||
roomVersion := gomatrixserverlib.RoomVersionV1
|
|
||||||
|
|
||||||
// Create the request
|
// Create the request
|
||||||
ire := roomserverAPI.InputRoomEvent{
|
ire := roomserverAPI.InputRoomEvent{
|
||||||
Kind: roomserverAPI.KindNew,
|
Kind: roomserverAPI.KindNew,
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
|
|
||||||
appserviceAPI "github.com/matrix-org/dendrite/appservice/api"
|
appserviceAPI "github.com/matrix-org/dendrite/appservice/api"
|
||||||
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MockRoomserverAliasAPIDatabase struct {
|
type MockRoomserverAliasAPIDatabase struct {
|
||||||
|
@ -49,6 +50,12 @@ func (db *MockRoomserverAliasAPIDatabase) GetCreatorIDForAlias(
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *MockRoomserverAliasAPIDatabase) GetRoomVersionForRoom(
|
||||||
|
ctx context.Context, roomID string,
|
||||||
|
) (gomatrixserverlib.RoomVersion, error) {
|
||||||
|
return gomatrixserverlib.RoomVersionV1, nil
|
||||||
|
}
|
||||||
|
|
||||||
// This method needs to change depending on test case
|
// This method needs to change depending on test case
|
||||||
func (db *MockRoomserverAliasAPIDatabase) GetRoomIDForAlias(
|
func (db *MockRoomserverAliasAPIDatabase) GetRoomIDForAlias(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
|
|
|
@ -72,6 +72,10 @@ type RoomEventDatabase interface {
|
||||||
ctx context.Context, transactionID string,
|
ctx context.Context, transactionID string,
|
||||||
sessionID int64, userID string,
|
sessionID int64, userID string,
|
||||||
) (string, error)
|
) (string, error)
|
||||||
|
// Look up the room version for a given room.
|
||||||
|
GetRoomVersionForRoom(
|
||||||
|
ctx context.Context, roomID string,
|
||||||
|
) (gomatrixserverlib.RoomVersion, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OutputRoomEventWriter has the APIs needed to write an event to the output logs.
|
// OutputRoomEventWriter has the APIs needed to write an event to the output logs.
|
||||||
|
|
|
@ -253,8 +253,10 @@ func (u *latestEventsUpdater) makeOutputNewRoomEvent() (*api.OutputEvent, error)
|
||||||
latestEventIDs[i] = u.latest[i].EventID
|
latestEventIDs[i] = u.latest[i].EventID
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Room version here
|
roomVersion, err := u.db.GetRoomVersionForRoom(u.ctx, u.event.RoomID())
|
||||||
roomVersion := gomatrixserverlib.RoomVersionV1
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
ore := api.OutputNewRoomEvent{
|
ore := api.OutputNewRoomEvent{
|
||||||
Event: u.event.Headered(roomVersion),
|
Event: u.event.Headered(roomVersion),
|
||||||
|
|
|
@ -91,7 +91,7 @@ type RoomserverQueryAPIDatabase interface {
|
||||||
) (map[types.EventStateKeyNID]string, error)
|
) (map[types.EventStateKeyNID]string, error)
|
||||||
// Look up the room version for a given room.
|
// Look up the room version for a given room.
|
||||||
GetRoomVersionForRoom(
|
GetRoomVersionForRoom(
|
||||||
ctx context.Context, roomNID types.RoomNID,
|
ctx context.Context, roomID string,
|
||||||
) (gomatrixserverlib.RoomVersion, error)
|
) (gomatrixserverlib.RoomVersion, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ func (r *RoomserverQueryAPI) QueryLatestEventsAndState(
|
||||||
}
|
}
|
||||||
response.RoomExists = true
|
response.RoomExists = true
|
||||||
|
|
||||||
roomVersion, err := r.DB.GetRoomVersionForRoom(ctx, roomNID)
|
roomVersion, err := r.DB.GetRoomVersionForRoom(ctx, request.RoomID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -174,7 +174,7 @@ func (r *RoomserverQueryAPI) QueryStateAfterEvents(
|
||||||
}
|
}
|
||||||
response.RoomExists = true
|
response.RoomExists = true
|
||||||
|
|
||||||
roomVersion, err := r.DB.GetRoomVersionForRoom(ctx, roomNID)
|
roomVersion, err := r.DB.GetRoomVersionForRoom(ctx, request.RoomID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -234,8 +234,10 @@ func (r *RoomserverQueryAPI) QueryEventsByID(
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, event := range events {
|
for _, event := range events {
|
||||||
// TODO: Room version here
|
roomVersion, verr := r.DB.GetRoomVersionForRoom(ctx, event.RoomID())
|
||||||
roomVersion := gomatrixserverlib.RoomVersionV1
|
if verr != nil {
|
||||||
|
return verr
|
||||||
|
}
|
||||||
|
|
||||||
response.Events = append(response.Events, event.Headered(roomVersion))
|
response.Events = append(response.Events, event.Headered(roomVersion))
|
||||||
}
|
}
|
||||||
|
@ -516,8 +518,10 @@ func (r *RoomserverQueryAPI) QueryMissingEvents(
|
||||||
response.Events = make([]gomatrixserverlib.HeaderedEvent, 0, len(loadedEvents)-len(eventsToFilter))
|
response.Events = make([]gomatrixserverlib.HeaderedEvent, 0, len(loadedEvents)-len(eventsToFilter))
|
||||||
for _, event := range loadedEvents {
|
for _, event := range loadedEvents {
|
||||||
if !eventsToFilter[event.EventID()] {
|
if !eventsToFilter[event.EventID()] {
|
||||||
// TODO: Room version here
|
roomVersion, verr := r.DB.GetRoomVersionForRoom(ctx, event.RoomID())
|
||||||
roomVersion := gomatrixserverlib.RoomVersionV1
|
if verr != nil {
|
||||||
|
return verr
|
||||||
|
}
|
||||||
|
|
||||||
response.Events = append(response.Events, event.Headered(roomVersion))
|
response.Events = append(response.Events, event.Headered(roomVersion))
|
||||||
}
|
}
|
||||||
|
@ -562,8 +566,10 @@ func (r *RoomserverQueryAPI) QueryBackfill(
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, event := range loadedEvents {
|
for _, event := range loadedEvents {
|
||||||
// TODO: Room version here
|
roomVersion, verr := r.DB.GetRoomVersionForRoom(ctx, event.RoomID())
|
||||||
roomVersion := gomatrixserverlib.RoomVersionV1
|
if verr != nil {
|
||||||
|
return verr
|
||||||
|
}
|
||||||
|
|
||||||
response.Events = append(response.Events, event.Headered(roomVersion))
|
response.Events = append(response.Events, event.Headered(roomVersion))
|
||||||
}
|
}
|
||||||
|
@ -647,6 +653,11 @@ func (r *RoomserverQueryAPI) QueryStateAndAuthChain(
|
||||||
}
|
}
|
||||||
response.RoomExists = true
|
response.RoomExists = true
|
||||||
|
|
||||||
|
roomVersion, err := r.DB.GetRoomVersionForRoom(ctx, request.RoomID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
stateEvents, err := r.loadStateAtEventIDs(ctx, request.PrevEventIDs)
|
stateEvents, err := r.loadStateAtEventIDs(ctx, request.PrevEventIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -667,16 +678,10 @@ func (r *RoomserverQueryAPI) QueryStateAndAuthChain(
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, event := range stateEvents {
|
for _, event := range stateEvents {
|
||||||
// TODO: Room version here
|
|
||||||
roomVersion := gomatrixserverlib.RoomVersionV1
|
|
||||||
|
|
||||||
response.StateEvents = append(response.StateEvents, event.Headered(roomVersion))
|
response.StateEvents = append(response.StateEvents, event.Headered(roomVersion))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, event := range authEvents {
|
for _, event := range authEvents {
|
||||||
// TODO: Room version here
|
|
||||||
roomVersion := gomatrixserverlib.RoomVersionV1
|
|
||||||
|
|
||||||
response.AuthChainEvents = append(response.AuthChainEvents, event.Headered(roomVersion))
|
response.AuthChainEvents = append(response.AuthChainEvents, event.Headered(roomVersion))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,5 +45,5 @@ type Database interface {
|
||||||
GetMembership(ctx context.Context, roomNID types.RoomNID, requestSenderUserID string) (membershipEventNID types.EventNID, stillInRoom bool, err error)
|
GetMembership(ctx context.Context, roomNID types.RoomNID, requestSenderUserID string) (membershipEventNID types.EventNID, stillInRoom bool, err error)
|
||||||
GetMembershipEventNIDsForRoom(ctx context.Context, roomNID types.RoomNID, joinOnly bool) ([]types.EventNID, error)
|
GetMembershipEventNIDsForRoom(ctx context.Context, roomNID types.RoomNID, joinOnly bool) ([]types.EventNID, error)
|
||||||
EventsFromIDs(ctx context.Context, eventIDs []string) ([]types.Event, error)
|
EventsFromIDs(ctx context.Context, eventIDs []string) ([]types.Event, error)
|
||||||
GetRoomVersionForRoom(ctx context.Context, roomNID types.RoomNID) (gomatrixserverlib.RoomVersion, error)
|
GetRoomVersionForRoom(ctx context.Context, roomID string) (gomatrixserverlib.RoomVersion, error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,8 +65,8 @@ const selectLatestEventNIDsForUpdateSQL = "" +
|
||||||
const updateLatestEventNIDsSQL = "" +
|
const updateLatestEventNIDsSQL = "" +
|
||||||
"UPDATE roomserver_rooms SET latest_event_nids = $2, last_event_sent_nid = $3, state_snapshot_nid = $4 WHERE room_nid = $1"
|
"UPDATE roomserver_rooms SET latest_event_nids = $2, last_event_sent_nid = $3, state_snapshot_nid = $4 WHERE room_nid = $1"
|
||||||
|
|
||||||
const selectRoomVersionForRoomNIDSQL = "" +
|
const selectRoomVersionForRoomIDSQL = "" +
|
||||||
"SELECT room_version FROM roomserver_rooms WHERE room_nid = $1"
|
"SELECT room_version FROM roomserver_rooms WHERE room_id = $1"
|
||||||
|
|
||||||
type roomStatements struct {
|
type roomStatements struct {
|
||||||
insertRoomNIDStmt *sql.Stmt
|
insertRoomNIDStmt *sql.Stmt
|
||||||
|
@ -74,7 +74,7 @@ type roomStatements struct {
|
||||||
selectLatestEventNIDsStmt *sql.Stmt
|
selectLatestEventNIDsStmt *sql.Stmt
|
||||||
selectLatestEventNIDsForUpdateStmt *sql.Stmt
|
selectLatestEventNIDsForUpdateStmt *sql.Stmt
|
||||||
updateLatestEventNIDsStmt *sql.Stmt
|
updateLatestEventNIDsStmt *sql.Stmt
|
||||||
selectRoomVersionForRoomNIDStmt *sql.Stmt
|
selectRoomVersionForRoomIDStmt *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *roomStatements) prepare(db *sql.DB) (err error) {
|
func (s *roomStatements) prepare(db *sql.DB) (err error) {
|
||||||
|
@ -88,7 +88,7 @@ func (s *roomStatements) prepare(db *sql.DB) (err error) {
|
||||||
{&s.selectLatestEventNIDsStmt, selectLatestEventNIDsSQL},
|
{&s.selectLatestEventNIDsStmt, selectLatestEventNIDsSQL},
|
||||||
{&s.selectLatestEventNIDsForUpdateStmt, selectLatestEventNIDsForUpdateSQL},
|
{&s.selectLatestEventNIDsForUpdateStmt, selectLatestEventNIDsForUpdateSQL},
|
||||||
{&s.updateLatestEventNIDsStmt, updateLatestEventNIDsSQL},
|
{&s.updateLatestEventNIDsStmt, updateLatestEventNIDsSQL},
|
||||||
{&s.selectRoomVersionForRoomNIDStmt, selectRoomVersionForRoomNIDSQL},
|
{&s.selectRoomVersionForRoomIDStmt, selectRoomVersionForRoomIDSQL},
|
||||||
}.prepare(db)
|
}.prepare(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,11 +165,11 @@ func (s *roomStatements) updateLatestEventNIDs(
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *roomStatements) selectRoomVersionForRoomNID(
|
func (s *roomStatements) selectRoomVersionForRoomID(
|
||||||
ctx context.Context, txn *sql.Tx, roomNID types.RoomNID,
|
ctx context.Context, txn *sql.Tx, roomID string,
|
||||||
) (gomatrixserverlib.RoomVersion, error) {
|
) (gomatrixserverlib.RoomVersion, error) {
|
||||||
var roomVersion gomatrixserverlib.RoomVersion
|
var roomVersion gomatrixserverlib.RoomVersion
|
||||||
stmt := common.TxStmt(txn, s.selectRoomVersionForRoomNIDStmt)
|
stmt := common.TxStmt(txn, s.selectRoomVersionForRoomIDStmt)
|
||||||
err := stmt.QueryRowContext(ctx, roomNID).Scan(&roomVersion)
|
err := stmt.QueryRowContext(ctx, roomID).Scan(&roomVersion)
|
||||||
return roomVersion, err
|
return roomVersion, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -740,10 +740,10 @@ func (d *Database) EventsFromIDs(ctx context.Context, eventIDs []string) ([]type
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Database) GetRoomVersionForRoom(
|
func (d *Database) GetRoomVersionForRoom(
|
||||||
ctx context.Context, roomNID types.RoomNID,
|
ctx context.Context, roomID string,
|
||||||
) (gomatrixserverlib.RoomVersion, error) {
|
) (gomatrixserverlib.RoomVersion, error) {
|
||||||
return d.statements.selectRoomVersionForRoomNID(
|
return d.statements.selectRoomVersionForRoomID(
|
||||||
ctx, nil, roomNID,
|
ctx, nil, roomID,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,8 +54,8 @@ const selectLatestEventNIDsForUpdateSQL = "" +
|
||||||
const updateLatestEventNIDsSQL = "" +
|
const updateLatestEventNIDsSQL = "" +
|
||||||
"UPDATE roomserver_rooms SET latest_event_nids = $1, last_event_sent_nid = $2, state_snapshot_nid = $3 WHERE room_nid = $4"
|
"UPDATE roomserver_rooms SET latest_event_nids = $1, last_event_sent_nid = $2, state_snapshot_nid = $3 WHERE room_nid = $4"
|
||||||
|
|
||||||
const selectRoomVersionForRoomNIDSQL = "" +
|
const selectRoomVersionForRoomIDSQL = "" +
|
||||||
"SELECT room_version FROM roomserver_rooms WHERE room_nid = $1"
|
"SELECT room_version FROM roomserver_rooms WHERE room_id = $1"
|
||||||
|
|
||||||
type roomStatements struct {
|
type roomStatements struct {
|
||||||
insertRoomNIDStmt *sql.Stmt
|
insertRoomNIDStmt *sql.Stmt
|
||||||
|
@ -63,7 +63,7 @@ type roomStatements struct {
|
||||||
selectLatestEventNIDsStmt *sql.Stmt
|
selectLatestEventNIDsStmt *sql.Stmt
|
||||||
selectLatestEventNIDsForUpdateStmt *sql.Stmt
|
selectLatestEventNIDsForUpdateStmt *sql.Stmt
|
||||||
updateLatestEventNIDsStmt *sql.Stmt
|
updateLatestEventNIDsStmt *sql.Stmt
|
||||||
selectRoomVersionForRoomNIDStmt *sql.Stmt
|
selectRoomVersionForRoomIDStmt *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *roomStatements) prepare(db *sql.DB) (err error) {
|
func (s *roomStatements) prepare(db *sql.DB) (err error) {
|
||||||
|
@ -77,7 +77,7 @@ func (s *roomStatements) prepare(db *sql.DB) (err error) {
|
||||||
{&s.selectLatestEventNIDsStmt, selectLatestEventNIDsSQL},
|
{&s.selectLatestEventNIDsStmt, selectLatestEventNIDsSQL},
|
||||||
{&s.selectLatestEventNIDsForUpdateStmt, selectLatestEventNIDsForUpdateSQL},
|
{&s.selectLatestEventNIDsForUpdateStmt, selectLatestEventNIDsForUpdateSQL},
|
||||||
{&s.updateLatestEventNIDsStmt, updateLatestEventNIDsSQL},
|
{&s.updateLatestEventNIDsStmt, updateLatestEventNIDsSQL},
|
||||||
{&s.selectRoomVersionForRoomNIDStmt, selectRoomVersionForRoomNIDSQL},
|
{&s.selectRoomVersionForRoomIDStmt, selectRoomVersionForRoomIDSQL},
|
||||||
}.prepare(db)
|
}.prepare(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,11 +157,11 @@ func (s *roomStatements) updateLatestEventNIDs(
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *roomStatements) selectRoomVersionForRoomNID(
|
func (s *roomStatements) selectRoomVersionForRoomID(
|
||||||
ctx context.Context, txn *sql.Tx, roomNID types.RoomNID,
|
ctx context.Context, txn *sql.Tx, roomID string,
|
||||||
) (gomatrixserverlib.RoomVersion, error) {
|
) (gomatrixserverlib.RoomVersion, error) {
|
||||||
var roomVersion gomatrixserverlib.RoomVersion
|
var roomVersion gomatrixserverlib.RoomVersion
|
||||||
stmt := common.TxStmt(txn, s.selectRoomVersionForRoomNIDStmt)
|
stmt := common.TxStmt(txn, s.selectRoomVersionForRoomIDStmt)
|
||||||
err := stmt.QueryRowContext(ctx, roomNID).Scan(&roomVersion)
|
err := stmt.QueryRowContext(ctx, roomID).Scan(&roomVersion)
|
||||||
return roomVersion, err
|
return roomVersion, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,6 @@ func Open(dataSourceName string) (*Database, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// StoreEvent implements input.EventDatabase
|
// StoreEvent implements input.EventDatabase
|
||||||
// nolint:gocyclo
|
|
||||||
func (d *Database) StoreEvent(
|
func (d *Database) StoreEvent(
|
||||||
ctx context.Context, event gomatrixserverlib.Event,
|
ctx context.Context, event gomatrixserverlib.Event,
|
||||||
txnAndSessionID *api.TransactionID, authEventNIDs []types.EventNID,
|
txnAndSessionID *api.TransactionID, authEventNIDs []types.EventNID,
|
||||||
|
@ -895,10 +894,10 @@ func (d *Database) EventsFromIDs(ctx context.Context, eventIDs []string) ([]type
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Database) GetRoomVersionForRoom(
|
func (d *Database) GetRoomVersionForRoom(
|
||||||
ctx context.Context, roomNID types.RoomNID,
|
ctx context.Context, roomID string,
|
||||||
) (gomatrixserverlib.RoomVersion, error) {
|
) (gomatrixserverlib.RoomVersion, error) {
|
||||||
return d.statements.selectRoomVersionForRoomNID(
|
return d.statements.selectRoomVersionForRoomID(
|
||||||
ctx, nil, roomNID,
|
ctx, nil, roomID,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue