aboutsummaryrefslogtreecommitdiff
path: root/publicroomsapi/storage/storage.go
diff options
context:
space:
mode:
Diffstat (limited to 'publicroomsapi/storage/storage.go')
-rw-r--r--publicroomsapi/storage/storage.go252
1 files changed, 252 insertions, 0 deletions
diff --git a/publicroomsapi/storage/storage.go b/publicroomsapi/storage/storage.go
new file mode 100644
index 00000000..eab27041
--- /dev/null
+++ b/publicroomsapi/storage/storage.go
@@ -0,0 +1,252 @@
+// 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 storage
+
+import (
+ "context"
+ "database/sql"
+ "encoding/json"
+
+ "github.com/matrix-org/dendrite/common"
+ "github.com/matrix-org/dendrite/publicroomsapi/types"
+
+ "github.com/matrix-org/gomatrixserverlib"
+)
+
+// PublicRoomsServerDatabase represents a public rooms server database.
+type PublicRoomsServerDatabase struct {
+ db *sql.DB
+ common.PartitionOffsetStatements
+ statements publicRoomsStatements
+}
+
+type attributeValue interface{}
+
+// NewPublicRoomsServerDatabase creates a new public rooms server database.
+func NewPublicRoomsServerDatabase(dataSourceName string) (*PublicRoomsServerDatabase, error) {
+ var db *sql.DB
+ var err error
+ if db, err = sql.Open("postgres", dataSourceName); err != nil {
+ return nil, err
+ }
+ partitions := common.PartitionOffsetStatements{}
+ if err = partitions.Prepare(db, "publicroomsapi"); err != nil {
+ return nil, err
+ }
+ statements := publicRoomsStatements{}
+ if err = statements.prepare(db); err != nil {
+ return nil, err
+ }
+ return &PublicRoomsServerDatabase{db, partitions, statements}, nil
+}
+
+// GetRoomVisibility returns the room visibility as a boolean: true if the room
+// is publicly visible, false if not.
+// Returns an error if the retrieval failed.
+func (d *PublicRoomsServerDatabase) GetRoomVisibility(
+ ctx context.Context, roomID string,
+) (bool, error) {
+ return d.statements.selectRoomVisibility(ctx, roomID)
+}
+
+// SetRoomVisibility updates the visibility attribute of a room. This attribute
+// must be set to true if the room is publicly visible, false if not.
+// Returns an error if the update failed.
+func (d *PublicRoomsServerDatabase) SetRoomVisibility(
+ ctx context.Context, visible bool, roomID string,
+) error {
+ return d.statements.updateRoomAttribute(ctx, "visibility", visible, roomID)
+}
+
+// CountPublicRooms returns the number of room set as publicly visible on the server.
+// Returns an error if the retrieval failed.
+func (d *PublicRoomsServerDatabase) CountPublicRooms(ctx context.Context) (int64, error) {
+ return d.statements.countPublicRooms(ctx)
+}
+
+// GetPublicRooms returns an array containing the local rooms set as publicly visible, ordered by their number
+// of joined members. This array can be limited by a given number of elements, and offset by a given value.
+// If the limit is 0, doesn't limit the number of results. If the offset is 0 too, the array contains all
+// the rooms set as publicly visible on the server.
+// Returns an error if the retrieval failed.
+func (d *PublicRoomsServerDatabase) GetPublicRooms(
+ ctx context.Context, offset int64, limit int16, filter string,
+) ([]types.PublicRoom, error) {
+ return d.statements.selectPublicRooms(ctx, offset, limit, filter)
+}
+
+// UpdateRoomFromEvents iterate over a slice of state events and call
+// UpdateRoomFromEvent on each of them to update the database representation of
+// the rooms updated by each event.
+// The slice of events to remove is used to update the number of joined members
+// for the room in the database.
+// If the update triggered by one of the events failed, aborts the process and
+// returns an error.
+func (d *PublicRoomsServerDatabase) UpdateRoomFromEvents(
+ ctx context.Context,
+ eventsToAdd []gomatrixserverlib.Event,
+ eventsToRemove []gomatrixserverlib.Event,
+) error {
+ for _, event := range eventsToAdd {
+ if err := d.UpdateRoomFromEvent(ctx, event); err != nil {
+ return err
+ }
+ }
+
+ for _, event := range eventsToRemove {
+ if event.Type() == "m.room.member" {
+ if err := d.updateNumJoinedUsers(ctx, event, true); err != nil {
+ return err
+ }
+ }
+ }
+
+ return nil
+}
+
+// UpdateRoomFromEvent updates the database representation of a room from a Matrix event, by
+// checking the event's type to know which attribute to change and using the event's content
+// to define the new value of the attribute.
+// If the event doesn't match with any property used to compute the public room directory,
+// does nothing.
+// If something went wrong during the process, returns an error.
+func (d *PublicRoomsServerDatabase) UpdateRoomFromEvent(
+ ctx context.Context, event gomatrixserverlib.Event,
+) error {
+ // Process the event according to its type
+ switch event.Type() {
+ case "m.room.create":
+ return d.statements.insertNewRoom(ctx, event.RoomID())
+ case "m.room.member":
+ return d.updateNumJoinedUsers(ctx, event, false)
+ case "m.room.aliases":
+ return d.updateRoomAliases(ctx, event)
+ case "m.room.canonical_alias":
+ var content common.CanonicalAliasContent
+ field := &(content.Alias)
+ attrName := "canonical_alias"
+ return d.updateStringAttribute(ctx, attrName, event, &content, field)
+ case "m.room.name":
+ var content common.NameContent
+ field := &(content.Name)
+ attrName := "name"
+ return d.updateStringAttribute(ctx, attrName, event, &content, field)
+ case "m.room.topic":
+ var content common.TopicContent
+ field := &(content.Topic)
+ attrName := "topic"
+ return d.updateStringAttribute(ctx, attrName, event, &content, field)
+ case "m.room.avatar":
+ var content common.AvatarContent
+ field := &(content.URL)
+ attrName := "avatar_url"
+ return d.updateStringAttribute(ctx, attrName, event, &content, field)
+ case "m.room.history_visibility":
+ var content common.HistoryVisibilityContent
+ field := &(content.HistoryVisibility)
+ attrName := "world_readable"
+ strForTrue := "world_readable"
+ return d.updateBooleanAttribute(ctx, attrName, event, &content, field, strForTrue)
+ case "m.room.guest_access":
+ var content common.GuestAccessContent
+ field := &(content.GuestAccess)
+ attrName := "guest_can_join"
+ strForTrue := "can_join"
+ return d.updateBooleanAttribute(ctx, attrName, event, &content, field, strForTrue)
+ }
+
+ // If the event type didn't match, return with no error
+ return nil
+}
+
+// updateNumJoinedUsers updates the number of joined user in the database representation
+// of a room using a given "m.room.member" Matrix event.
+// If the membership property of the event isn't "join", ignores it and returs nil.
+// If the remove parameter is set to false, increments the joined members counter in the
+// database, if set to truem decrements it.
+// Returns an error if the update failed.
+func (d *PublicRoomsServerDatabase) updateNumJoinedUsers(
+ ctx context.Context, membershipEvent gomatrixserverlib.Event, remove bool,
+) error {
+ membership, err := membershipEvent.Membership()
+ if err != nil {
+ return err
+ }
+
+ if membership != "join" {
+ return nil
+ }
+
+ if remove {
+ return d.statements.decrementJoinedMembersInRoom(ctx, membershipEvent.RoomID())
+ }
+ return d.statements.incrementJoinedMembersInRoom(ctx, membershipEvent.RoomID())
+}
+
+// updateStringAttribute updates a given string attribute in the database
+// representation of a room using a given string data field from content of the
+// Matrix event triggering the update.
+// Returns an error if decoding the Matrix event's content or updating the attribute
+// failed.
+func (d *PublicRoomsServerDatabase) updateStringAttribute(
+ ctx context.Context, attrName string, event gomatrixserverlib.Event,
+ content interface{}, field *string,
+) error {
+ if err := json.Unmarshal(event.Content(), content); err != nil {
+ return err
+ }
+
+ return d.statements.updateRoomAttribute(ctx, attrName, *field, event.RoomID())
+}
+
+// updateBooleanAttribute updates a given boolean attribute in the database
+// representation of a room using a given string data field from content of the
+// Matrix event triggering the update.
+// The attribute is set to true if the field matches a given string, false if not.
+// Returns an error if decoding the Matrix event's content or updating the attribute
+// failed.
+func (d *PublicRoomsServerDatabase) updateBooleanAttribute(
+ ctx context.Context, attrName string, event gomatrixserverlib.Event,
+ content interface{}, field *string, strForTrue string,
+) error {
+ if err := json.Unmarshal(event.Content(), content); err != nil {
+ return err
+ }
+
+ var attrValue bool
+ if *field == strForTrue {
+ attrValue = true
+ } else {
+ attrValue = false
+ }
+
+ return d.statements.updateRoomAttribute(ctx, attrName, attrValue, event.RoomID())
+}
+
+// updateRoomAliases decodes the content of a "m.room.aliases" Matrix event and update the list of aliases of
+// a given room with it.
+// Returns an error if decoding the Matrix event or updating the list failed.
+func (d *PublicRoomsServerDatabase) updateRoomAliases(
+ ctx context.Context, aliasesEvent gomatrixserverlib.Event,
+) error {
+ var content common.AliasesContent
+ if err := json.Unmarshal(aliasesEvent.Content(), &content); err != nil {
+ return err
+ }
+
+ return d.statements.updateRoomAttribute(
+ ctx, "aliases", content.Aliases, aliasesEvent.RoomID(),
+ )
+}