From 3d6f2165bd6bd9daaad0b6ece40e66f15a1b8eae Mon Sep 17 00:00:00 2001 From: Denis Arh Date: Thu, 26 Jul 2018 23:59:29 +0200 Subject: [PATCH] Stabilize SAM repositroy. - aligned repository code, db schema and spec.json - add (not -short) tests for SAM repository --- .../schema/mysql/20180704080000.base.up.sql | 70 +++++---- sam/docs/src/spec.json | 53 +++++-- sam/docs/src/spec/channel.json | 16 +- sam/docs/src/spec/message.json | 50 +++++- sam/docs/src/spec/organisation.json | 21 ++- sam/docs/src/spec/team.json | 22 +-- sam/docs/src/spec/user.json | 16 +- sam/repository/attachment.go | 24 ++- sam/repository/attachment_test.go | 52 +++++++ sam/repository/channel.go | 23 ++- sam/repository/channel_test.go | 56 +++++++ sam/repository/generics.go | 20 +-- sam/repository/main_test.go | 31 ++++ sam/repository/message.go | 17 +- sam/repository/message_test.go | 54 +++++++ sam/repository/organisation.go | 15 +- sam/repository/organisation_test.go | 56 +++++++ sam/repository/reaction.go | 15 +- sam/repository/reaction_test.go | 38 +++++ sam/repository/team.go | 15 +- sam/repository/team_test.go | 56 +++++++ sam/repository/user.go | 21 ++- sam/repository/user_test.go | 54 +++++++ sam/types/channel.go | 30 +++- sam/types/message.go | 146 ++++++++++++++++-- sam/types/organisation.go | 45 +++++- sam/types/team.go | 51 +++--- sam/types/user.go | 30 +++- 28 files changed, 939 insertions(+), 158 deletions(-) create mode 100644 sam/repository/attachment_test.go create mode 100644 sam/repository/channel_test.go create mode 100644 sam/repository/main_test.go create mode 100644 sam/repository/message_test.go create mode 100644 sam/repository/organisation_test.go create mode 100644 sam/repository/reaction_test.go create mode 100644 sam/repository/team_test.go create mode 100644 sam/repository/user_test.go diff --git a/sam/db/schema/mysql/20180704080000.base.up.sql b/sam/db/schema/mysql/20180704080000.base.up.sql index e651e4017..5c68f484f 100644 --- a/sam/db/schema/mysql/20180704080000.base.up.sql +++ b/sam/db/schema/mysql/20180704080000.base.up.sql @@ -2,8 +2,10 @@ CREATE TABLE organisations ( id BIGINT UNSIGNED NOT NULL, fqn TEXT NOT NULL, -- fully qualified name of the organisation - label TEXT NOT NULL, -- display name of the organisation + name TEXT NOT NULL, -- display name of the organisation + created_at DATETIME NOT NULL DEFAULT NOW(), + updated_at DATETIME NULL, archived_at DATETIME NULL, deleted_at DATETIME NULL, -- organisation soft delete @@ -13,9 +15,11 @@ CREATE TABLE organisations ( -- Keeps all known teams CREATE TABLE teams ( id BIGINT UNSIGNED NOT NULL, - label TEXT NOT NULL, -- display name of the team + name TEXT NOT NULL, -- display name of the team handle TEXT NOT NULL, -- team handle string + created_at DATETIME NOT NULL DEFAULT NOW(), + updated_at DATETIME NULL, archived_at DATETIME NULL, deleted_at DATETIME NULL, -- team soft delete @@ -28,6 +32,8 @@ CREATE TABLE channels ( name TEXT NOT NULL, -- display name of the channel meta JSON NOT NULL, + created_at DATETIME NOT NULL DEFAULT NOW(), + updated_at DATETIME NULL, archived_at DATETIME NULL, deleted_at DATETIME NULL, -- channel soft delete @@ -46,6 +52,8 @@ CREATE TABLE users ( rel_organisation BIGINT UNSIGNED NOT NULL REFERENCES organisation(id), + created_at DATETIME NOT NULL DEFAULT NOW(), + updated_at DATETIME NULL, suspended_at DATETIME NULL, deleted_at DATETIME NULL, -- user soft delete @@ -69,6 +77,8 @@ CREATE TABLE channel_members ( messages_since DATETIME NULL, messages_until DATETIME NULL, + created_at DATETIME NOT NULL DEFAULT NOW(), + PRIMARY KEY (rel_channel, rel_user) ); @@ -87,49 +97,57 @@ CREATE TABLE channel_views ( CREATE TABLE channel_pins ( rel_channel BIGINT UNSIGNED NOT NULL REFERENCES channels(id), - rel_message BIGINT UNSIGNED NULL REFERENCES messages(id), + rel_message BIGINT UNSIGNED NOT NULL REFERENCES messages(id), rel_user BIGINT UNSIGNED NOT NULL REFERENCES users(id), + created_at DATETIME NOT NULL DEFAULT NOW(), + PRIMARY KEY (rel_channel, rel_message) ); CREATE TABLE messages ( - id BIGINT UNSIGNED NOT NULL, + id BIGINT UNSIGNED NOT NULL, type TEXT, - message TEXT NOT NULL, + message TEXT NOT NULL, meta JSON, - rel_user BIGINT UNSIGNED NOT NULL REFERENCES users(id), - rel_channel BIGINT UNSIGNED NOT NULL REFERENCES channels(id), - reply_to BIGINT UNSIGNED NULL REFERENCES messages(id), + rel_user BIGINT UNSIGNED NOT NULL REFERENCES users(id), + rel_channel BIGINT UNSIGNED NOT NULL REFERENCES channels(id), + reply_to BIGINT UNSIGNED NULL REFERENCES messages(id), - updated_at DATETIME NULL, - deleted_at DATETIME NULL, + created_at DATETIME NOT NULL DEFAULT NOW(), + updated_at DATETIME NULL, + deleted_at DATETIME NULL, PRIMARY KEY (id) ); CREATE TABLE reactions ( - id BIGINT UNSIGNED NOT NULL, - rel_user BIGINT UNSIGNED NOT NULL REFERENCES users(id), - rel_message BIGINT UNSIGNED NOT NULL REFERENCES messages(id), - rel_channel BIGINT UNSIGNED NOT NULL REFERENCES channels(id), - reaction TEXT NOT NULL, + id BIGINT UNSIGNED NOT NULL, + rel_user BIGINT UNSIGNED NOT NULL REFERENCES users(id), + rel_message BIGINT UNSIGNED NOT NULL REFERENCES messages(id), + rel_channel BIGINT UNSIGNED NOT NULL REFERENCES channels(id), + reaction TEXT NOT NULL, + + created_at DATETIME NOT NULL DEFAULT NOW(), PRIMARY KEY (id) ); CREATE TABLE attachments ( - id BIGINT UNSIGNED NOT NULL, - rel_user BIGINT UNSIGNED NOT NULL REFERENCES users(id), - rel_message BIGINT UNSIGNED NOT NULL REFERENCES messages(id), - rel_channel BIGINT UNSIGNED NOT NULL REFERENCES channels(id), - url TEXT NOT NULL, - preview_url TEXT NOT NULL, - size INT UNSIGNED NOT NULL, - mimetype TEXT NOT NULL, - name TEXT NOT NULL, - attachment JSON NOT NULL, - deleted_at DATETIME NULL, + id BIGINT UNSIGNED NOT NULL, + rel_user BIGINT UNSIGNED NOT NULL REFERENCES users(id), + rel_message BIGINT UNSIGNED NOT NULL REFERENCES messages(id), + rel_channel BIGINT UNSIGNED NOT NULL REFERENCES channels(id), + url TEXT, + preview_url TEXT, + size INT UNSIGNED, + mimetype TEXT, + name TEXT, + attachment JSON NOT NULL, + + created_at DATETIME NOT NULL DEFAULT NOW(), + updated_at DATETIME NULL, + deleted_at DATETIME NULL, PRIMARY KEY (id) ); diff --git a/sam/docs/src/spec.json b/sam/docs/src/spec.json index 223a1ef8b..3c7ece322 100644 --- a/sam/docs/src/spec.json +++ b/sam/docs/src/spec.json @@ -11,10 +11,14 @@ "name": "Organisation", "imports": ["time"], "fields": [ - { "name": "ID", "type": "uint64" }, + { "name": "ID", "type": "uint64" }, + { "name": "FQN", "type": "string", "dbname": "fqn" }, { "name": "Name", "type": "string" }, - { "type": "*time.Time", "name": "ArchivedAt", "tag": "json:\",omitempty\"", "complex": true }, - { "type": "*time.Time", "name": "DeletedAt", "tag": "json:\",omitempty\"", "complex": true } + + { "name": "CreatedAt", "type": "time.Time", "tag": "json:\"created_at,omitempty\"", "complex": true }, + { "name": "UpdatedAt", "type": "*time.Time", "tag": "json:\"updated_at,omitempty\"", "complex": true }, + { "name": "ArchivedAt", "type": "*time.Time", "tag": "json:\"archived_at,omitempty\"", "complex": true }, + { "name": "DeletedAt", "type": "*time.Time", "tag": "json:\"deleted_at,omitempty\"", "complex": true } ] } ], @@ -104,10 +108,12 @@ "fields": [ { "type": "uint64", "name": "ID" }, { "type": "string", "name": "Name" }, - { "type": "[]uint64", "name": "MemberIDs", "tag": "json:\"-\"", "complex": true }, - { "type": "[]User", "name": "Members", "tag": "json:\",omitempty\"", "complex": true }, - { "type": "*time.Time", "name": "ArchivedAt", "tag": "json:\",omitempty\"", "complex": true }, - { "type": "*time.Time", "name": "DeletedAt", "tag": "json:\",omitempty\"", "complex": true } + { "type": "string", "name": "Handle" }, + + { "name": "CreatedAt", "type": "time.Time", "tag": "json:\"created_at,omitempty\"", "complex": true }, + { "name": "UpdatedAt", "type": "*time.Time", "tag": "json:\"updated_at,omitempty\"", "complex": true }, + { "name": "ArchivedAt", "type": "*time.Time", "tag": "json:\"archived_at,omitempty\"", "complex": true }, + { "name": "DeletedAt", "type": "*time.Time", "tag": "json:\"deleted_at,omitempty\"", "complex": true } ] } ], @@ -233,8 +239,11 @@ { "type": "string", "name": "Topic", "dbname": "-" }, { "type": "json.RawMessage", "name": "Meta", "complex": true}, { "type": "uint64", "name": "LastMessageID", "tag": "json:\",omitempty\"", "dbname": "rel_last_message" }, - { "type": "*time.Time", "name": "ArchivedAt", "tag": "json:\",omitempty\"", "complex": true }, - { "type": "*time.Time", "name": "DeletedAt", "tag": "json:\",omitempty\"", "complex": true } + + { "name": "CreatedAt", "type": "time.Time", "tag": "json:\"created_at,omitempty\"", "complex": true }, + { "name": "UpdatedAt", "type": "*time.Time", "tag": "json:\"updated_at,omitempty\"", "complex": true }, + { "name": "ArchivedAt", "type": "*time.Time", "tag": "json:\"archived_at,omitempty\"", "complex": true }, + { "name": "DeletedAt", "type": "*time.Time", "tag": "json:\"deleted_at,omitempty\"", "complex": true } ] } ], @@ -325,9 +334,10 @@ { "name": "UserID", "type": "uint64", "dbname": "rel_user" }, { "name": "ChannelID", "type": "uint64", "dbname": "rel_channel"}, { "name": "ReplyTo", "type": "uint64", "dbname": "reply_to" }, - { "name": "UpdatedAt", "type": "*time.Time", "tag": "json:\",omitempty\"", "complex": true }, - { "name": "DeletedAt", "type": "*time.Time", "tag": "json:\",omitempty\"", "complex": true } + { "name": "CreatedAt", "type": "time.Time", "tag": "json:\"created_at,omitempty\"", "complex": true }, + { "name": "UpdatedAt", "type": "*time.Time", "tag": "json:\"updated_at,omitempty\"", "complex": true }, + { "name": "DeletedAt", "type": "*time.Time", "tag": "json:\"deleted_at,omitempty\"", "complex": true } ] }, { @@ -339,7 +349,8 @@ { "name": "MessageID", "type": "uint64", "dbname": "rel_message"}, { "name": "ChannelID", "type": "uint64", "dbname": "rel_channel"}, { "name": "Reaction", "type": "string" }, - { "name": "DeletedAt", "type": "*time.Time", "tag": "json:\",omitempty\"", "complex": true } + + { "name": "CreatedAt", "type": "time.Time", "tag": "json:\"created_at,omitempty\"", "complex": true } ] }, { @@ -351,7 +362,16 @@ { "name": "MessageID", "type": "uint64", "dbname": "rel_message"}, { "name": "ChannelID", "type": "uint64", "dbname": "rel_channel"}, { "name": "Attachment", "type": "json.RawMessage" , "complex": true}, - { "name": "DeletedAt", "type": "*time.Time", "tag": "json:\",omitempty\"", "complex": true } + + { "name": "Url", "type": "string" }, + { "name": "PreviewUrl", "type": "string" }, + { "name": "Size", "type": "uint" }, + { "name": "Mimetype", "type": "string" }, + { "name": "Name", "type": "string" }, + + { "name": "CreatedAt", "type": "time.Time", "tag": "json:\"created_at,omitempty\"", "complex": true }, + { "name": "UpdatedAt", "type": "*time.Time", "tag": "json:\"updated_at,omitempty\"", "complex": true }, + { "name": "DeletedAt", "type": "*time.Time", "tag": "json:\"deleted_at,omitempty\"", "complex": true } ] } ], @@ -521,8 +541,11 @@ { "type": "interface{}", "name": "Meta", "tag": "json:\"-\"" }, { "type": "uint64", "name": "OrganisationID", "dbname": "rel_organisation" }, { "type": "[]byte", "name": "Password", "tag": "json:\"-\"", "complex": true }, - { "type": "*time.Time", "name": "SuspendedAt", "tag": "json:\",omitempty\"", "complex": true }, - { "type": "*time.Time", "name": "DeletedAt", "tag": "json:\",omitempty\"", "complex": true } + + { "name": "CreatedAt", "type": "time.Time", "tag": "json:\"created_at,omitempty\"", "complex": true }, + { "name": "UpdatedAt", "type": "*time.Time", "tag": "json:\"updated_at,omitempty\"", "complex": true }, + { "name": "SuspendedAt","type": "*time.Time", "tag": "json:\"suspended_at,omitempty\"","complex": true }, + { "name": "DeletedAt", "type": "*time.Time", "tag": "json:\"deleted_at,omitempty\"", "complex": true } ] } ], diff --git a/sam/docs/src/spec/channel.json b/sam/docs/src/spec/channel.json index 53c462523..67bd541b0 100644 --- a/sam/docs/src/spec/channel.json +++ b/sam/docs/src/spec/channel.json @@ -30,16 +30,28 @@ "tag": "json:\",omitempty\"", "type": "uint64" }, + { + "complex": true, + "name": "CreatedAt", + "tag": "json:\"created_at,omitempty\"", + "type": "time.Time" + }, + { + "complex": true, + "name": "UpdatedAt", + "tag": "json:\"updated_at,omitempty\"", + "type": "*time.Time" + }, { "complex": true, "name": "ArchivedAt", - "tag": "json:\",omitempty\"", + "tag": "json:\"archived_at,omitempty\"", "type": "*time.Time" }, { "complex": true, "name": "DeletedAt", - "tag": "json:\",omitempty\"", + "tag": "json:\"deleted_at,omitempty\"", "type": "*time.Time" } ], diff --git a/sam/docs/src/spec/message.json b/sam/docs/src/spec/message.json index f868b1a27..f72841ad1 100644 --- a/sam/docs/src/spec/message.json +++ b/sam/docs/src/spec/message.json @@ -32,16 +32,22 @@ "name": "ReplyTo", "type": "uint64" }, + { + "complex": true, + "name": "CreatedAt", + "tag": "json:\"created_at,omitempty\"", + "type": "time.Time" + }, { "complex": true, "name": "UpdatedAt", - "tag": "json:\",omitempty\"", + "tag": "json:\"updated_at,omitempty\"", "type": "*time.Time" }, { "complex": true, "name": "DeletedAt", - "tag": "json:\",omitempty\"", + "tag": "json:\"deleted_at,omitempty\"", "type": "*time.Time" } ], @@ -77,9 +83,9 @@ }, { "complex": true, - "name": "DeletedAt", - "tag": "json:\",omitempty\"", - "type": "*time.Time" + "name": "CreatedAt", + "tag": "json:\"created_at,omitempty\"", + "type": "time.Time" } ], "imports": [ @@ -113,10 +119,42 @@ "name": "Attachment", "type": "json.RawMessage" }, + { + "name": "Url", + "type": "string" + }, + { + "name": "PreviewUrl", + "type": "string" + }, + { + "name": "Size", + "type": "uint" + }, + { + "name": "Mimetype", + "type": "string" + }, + { + "name": "Name", + "type": "string" + }, + { + "complex": true, + "name": "CreatedAt", + "tag": "json:\"created_at,omitempty\"", + "type": "time.Time" + }, + { + "complex": true, + "name": "UpdatedAt", + "tag": "json:\"updated_at,omitempty\"", + "type": "*time.Time" + }, { "complex": true, "name": "DeletedAt", - "tag": "json:\",omitempty\"", + "tag": "json:\"deleted_at,omitempty\"", "type": "*time.Time" } ], diff --git a/sam/docs/src/spec/organisation.json b/sam/docs/src/spec/organisation.json index 84f4c216c..79740bb48 100644 --- a/sam/docs/src/spec/organisation.json +++ b/sam/docs/src/spec/organisation.json @@ -10,20 +10,37 @@ "name": "ID", "type": "uint64" }, + { + "dbname": "fqn", + "name": "FQN", + "type": "string" + }, { "name": "Name", "type": "string" }, + { + "complex": true, + "name": "CreatedAt", + "tag": "json:\"created_at,omitempty\"", + "type": "time.Time" + }, + { + "complex": true, + "name": "UpdatedAt", + "tag": "json:\"updated_at,omitempty\"", + "type": "*time.Time" + }, { "complex": true, "name": "ArchivedAt", - "tag": "json:\",omitempty\"", + "tag": "json:\"archived_at,omitempty\"", "type": "*time.Time" }, { "complex": true, "name": "DeletedAt", - "tag": "json:\",omitempty\"", + "tag": "json:\"deleted_at,omitempty\"", "type": "*time.Time" } ], diff --git a/sam/docs/src/spec/team.json b/sam/docs/src/spec/team.json index 0c6869f03..4616ded0b 100644 --- a/sam/docs/src/spec/team.json +++ b/sam/docs/src/spec/team.json @@ -15,27 +15,31 @@ "type": "string" }, { - "complex": true, - "name": "MemberIDs", - "tag": "json:\"-\"", - "type": "[]uint64" + "name": "Handle", + "type": "string" }, { "complex": true, - "name": "Members", - "tag": "json:\",omitempty\"", - "type": "[]User" + "name": "CreatedAt", + "tag": "json:\"created_at,omitempty\"", + "type": "time.Time" + }, + { + "complex": true, + "name": "UpdatedAt", + "tag": "json:\"updated_at,omitempty\"", + "type": "*time.Time" }, { "complex": true, "name": "ArchivedAt", - "tag": "json:\",omitempty\"", + "tag": "json:\"archived_at,omitempty\"", "type": "*time.Time" }, { "complex": true, "name": "DeletedAt", - "tag": "json:\",omitempty\"", + "tag": "json:\"deleted_at,omitempty\"", "type": "*time.Time" } ], diff --git a/sam/docs/src/spec/user.json b/sam/docs/src/spec/user.json index 01901a347..1173351cb 100644 --- a/sam/docs/src/spec/user.json +++ b/sam/docs/src/spec/user.json @@ -29,16 +29,28 @@ "tag": "json:\"-\"", "type": "[]byte" }, + { + "complex": true, + "name": "CreatedAt", + "tag": "json:\"created_at,omitempty\"", + "type": "time.Time" + }, + { + "complex": true, + "name": "UpdatedAt", + "tag": "json:\"updated_at,omitempty\"", + "type": "*time.Time" + }, { "complex": true, "name": "SuspendedAt", - "tag": "json:\",omitempty\"", + "tag": "json:\"suspended_at,omitempty\"", "type": "*time.Time" }, { "complex": true, "name": "DeletedAt", - "tag": "json:\",omitempty\"", + "tag": "json:\"deleted_at,omitempty\"", "type": "*time.Time" } ], diff --git a/sam/repository/attachment.go b/sam/repository/attachment.go index 58c1c6ae8..51c0342b2 100644 --- a/sam/repository/attachment.go +++ b/sam/repository/attachment.go @@ -4,10 +4,11 @@ import ( "context" "github.com/crusttech/crust/sam/types" "github.com/titpetric/factory" + "time" ) const ( - sqlAttachmentScope = "deleted_at IS NULL AND archived_at IS NULL" + sqlAttachmentScope = "deleted_at IS NULL" ErrAttachmentNotFound = repositoryError("AttachmentNotFound") ) @@ -25,7 +26,7 @@ func (r attachment) FindByID(ctx context.Context, id uint64) (*types.Attachment, mod := &types.Attachment{} if err := db.GetContext(ctx, mod, "SELECT * FROM attachments WHERE id = ? AND "+sqlAttachmentScope, id); err != nil { - return nil, ErrDatabaseError + return nil, err } else if mod.ID == 0 { return nil, ErrAttachmentNotFound } else { @@ -39,13 +40,13 @@ func (r attachment) FindByRange(ctx context.Context, channelID, fromAttachmentID sql := ` SELECT * FROM attachments - WHERE rel_attachment BETWEEN ? AND ? + WHERE id BETWEEN ? AND ? AND rel_channel = ? AND deleted_at IS NULL` rval := make([]*types.Attachment, 0) - if err := db.Select(&rval, sql, fromAttachmentID, toAttachmentID, channelID); err != nil { - return nil, ErrDatabaseError + if err := db.SelectContext(ctx, &rval, sql, fromAttachmentID, toAttachmentID, channelID); err != nil { + return nil, err } return rval, nil @@ -55,8 +56,14 @@ func (r attachment) Create(ctx context.Context, mod *types.Attachment) (*types.A db := factory.Database.MustGet() mod.SetID(factory.Sonyflake.NextID()) + mod.SetCreatedAt(time.Now()) + + if mod.Attachment == nil { + mod.SetAttachment([]byte("{}")) + } + if err := db.Insert("attachments", mod); err != nil { - return nil, ErrDatabaseError + return nil, err } else { return mod, nil } @@ -65,8 +72,11 @@ func (r attachment) Create(ctx context.Context, mod *types.Attachment) (*types.A func (r attachment) Update(ctx context.Context, mod *types.Attachment) (*types.Attachment, error) { db := factory.Database.MustGet() + now := time.Now() + mod.SetUpdatedAt(&now) + if err := db.Replace("attachments", mod); err != nil { - return nil, ErrDatabaseError + return nil, err } else { return mod, nil } diff --git a/sam/repository/attachment_test.go b/sam/repository/attachment_test.go new file mode 100644 index 000000000..7cab17952 --- /dev/null +++ b/sam/repository/attachment_test.go @@ -0,0 +1,52 @@ +package repository + +import ( + "context" + "github.com/crusttech/crust/sam/types" + "testing" +) + +func TestAttachment(t *testing.T) { + var err error + + if testing.Short() { + t.Skip("skipping test in short mode.") + return + } + + rpo := Attachment() + ctx := context.Background() + att := types.Attachment{}.New() + + var aa []*types.Attachment + + att.SetChannelID(1) + + att, err = rpo.Create(ctx, att) + must(t, err) + if att.ChannelID != 1 { + t.Fatal("Changes were not stored") + } + + att.SetChannelID(2) + + att, err = rpo.Update(ctx, att) + must(t, err) + if att.ChannelID != 2 { + t.Fatal("Changes were not stored") + } + + att, err = rpo.FindByID(ctx, att.ID) + must(t, err) + if att.ChannelID != 2 { + t.Fatal("Changes were not stored") + } + + aa, err = rpo.FindByRange(ctx, 2, 0, att.ID) + must(t, err) + if len(aa) == 0 { + t.Fatal("No results found") + } + + must(t, rpo.Delete(ctx, att.ID)) +} diff --git a/sam/repository/channel.go b/sam/repository/channel.go index 7983c22b8..8c4803b95 100644 --- a/sam/repository/channel.go +++ b/sam/repository/channel.go @@ -25,8 +25,8 @@ func (r channel) FindByID(ctx context.Context, id uint64) (*types.Channel, error db := factory.Database.MustGet() mod := &types.Channel{} - if err := db.Get(mod, "SELECT * FROM channels WHERE id = ? AND "+sqlChannelScope, id); err != nil { - return nil, ErrDatabaseError + if err := db.GetContext(ctx, mod, "SELECT * FROM channels WHERE id = ? AND "+sqlChannelScope, id); err != nil { + return nil, err } else if mod.ID == 0 { return nil, ErrChannelNotFound } else { @@ -42,7 +42,7 @@ func (r channel) Find(ctx context.Context, filter *types.ChannelFilter) ([]*type if filter != nil { if filter.Query != "" { - sql += "AND name LIKE ?" + sql += " AND name LIKE ?" params = append(params, filter.Query+"%") } } @@ -50,8 +50,8 @@ func (r channel) Find(ctx context.Context, filter *types.ChannelFilter) ([]*type sql += " ORDER BY name ASC" rval := make([]*types.Channel, 0) - if err := db.Select(&rval, sql, params...); err != nil { - return nil, ErrDatabaseError + if err := db.SelectContext(ctx, &rval, sql, params...); err != nil { + return nil, err } else { return rval, nil } @@ -61,8 +61,14 @@ func (r channel) Create(ctx context.Context, mod *types.Channel) (*types.Channel db := factory.Database.MustGet() mod.SetID(factory.Sonyflake.NextID()) + mod.SetCreatedAt(time.Now()) + + if mod.Meta == nil { + mod.SetMeta([]byte("{}")) + } + if err := db.Insert("channels", mod); err != nil { - return nil, ErrDatabaseError + return nil, err } else { return mod, nil } @@ -71,8 +77,11 @@ func (r channel) Create(ctx context.Context, mod *types.Channel) (*types.Channel func (r channel) Update(ctx context.Context, mod *types.Channel) (*types.Channel, error) { db := factory.Database.MustGet() + now := time.Now() + mod.SetUpdatedAt(&now) + if err := db.Replace("channels", mod); err != nil { - return nil, ErrDatabaseError + return nil, err } else { return mod, nil } diff --git a/sam/repository/channel_test.go b/sam/repository/channel_test.go new file mode 100644 index 000000000..fd8be0d14 --- /dev/null +++ b/sam/repository/channel_test.go @@ -0,0 +1,56 @@ +package repository + +import ( + "context" + "github.com/crusttech/crust/sam/types" + "testing" +) + +func TestChannel(t *testing.T) { + var err error + + if testing.Short() { + t.Skip("skipping test in short mode.") + return + } + + rpo := Channel() + ctx := context.Background() + att := types.Channel{}.New() + + var name1, name2 = "Test channel v1", "Test channel v2" + + var aa []*types.Channel + + att.SetName(name1) + + att, err = rpo.Create(ctx, att) + must(t, err) + if att.Name != name1 { + t.Fatal("Changes were not stored") + } + + att.SetName(name2) + + att, err = rpo.Update(ctx, att) + must(t, err) + if att.Name != name2 { + t.Fatal("Changes were not stored") + } + + att, err = rpo.FindByID(ctx, att.ID) + must(t, err) + if att.Name != name2 { + t.Fatal("Changes were not stored") + } + + aa, err = rpo.Find(ctx, &types.ChannelFilter{Query: name2}) + must(t, err) + if len(aa) == 0 { + t.Fatal("No results found") + } + + must(t, rpo.Archive(ctx, att.ID)) + must(t, rpo.Unarchive(ctx, att.ID)) + must(t, rpo.Delete(ctx, att.ID)) +} diff --git a/sam/repository/generics.go b/sam/repository/generics.go index 3ac7f539f..3365dda3c 100644 --- a/sam/repository/generics.go +++ b/sam/repository/generics.go @@ -6,26 +6,20 @@ import ( "github.com/titpetric/factory" ) -func simpleUpdate(ctx context.Context, tableName, columnName string, value interface{}, id uint64) error { +func simpleUpdate(ctx context.Context, tableName, columnName string, value interface{}, id uint64) (err error) { db := factory.Database.MustGet() sql := fmt.Sprintf("UPDATE %s SET %s = ? WHERE id = ?", tableName, columnName) - if _, err := db.ExecContext(ctx, sql, value, id); err != nil { - return ErrDatabaseError - } else { - return nil - } + _, err = db.ExecContext(ctx, sql, value, id) + return err } -func simpleDelete(ctx context.Context, tableName string, id uint64) error { +func simpleDelete(ctx context.Context, tableName string, id uint64) (err error) { db := factory.Database.MustGet() - sql := fmt.Sprintf("DELETE %s WHERE id = ?", tableName) + sql := fmt.Sprintf("DELETE FROM %s WHERE id = ?", tableName) - if _, err := db.ExecContext(ctx, sql, id); err != nil { - return ErrDatabaseError - } else { - return nil - } + _, err = db.ExecContext(ctx, sql, id) + return err } diff --git a/sam/repository/main_test.go b/sam/repository/main_test.go new file mode 100644 index 000000000..f0fd57b5c --- /dev/null +++ b/sam/repository/main_test.go @@ -0,0 +1,31 @@ +package repository + +import ( + "flag" + "github.com/joho/godotenv" + _ "github.com/joho/godotenv" + "github.com/titpetric/factory" + "os" + "testing" +) + +func TestMain(m *testing.M) { + flag.Parse() + + if testing.Short() { + return + } + + // @todo this is a very optimistic initialization, make it more robust + godotenv.Load("../../.env") + factory.Database.Add("default", os.Getenv("SAM_DB_DSN")) + factory.Database.MustGet().Profiler = &factory.Database.ProfilerStdout + + os.Exit(m.Run()) +} + +func must(t *testing.T, err error) { + if err != nil { + t.Fatalf("Error: %v", err) + } +} diff --git a/sam/repository/message.go b/sam/repository/message.go index 5a712ae22..352bc4b44 100644 --- a/sam/repository/message.go +++ b/sam/repository/message.go @@ -3,8 +3,8 @@ package repository import ( "context" "github.com/crusttech/crust/sam/types" - "github.com/pkg/errors" "github.com/titpetric/factory" + "time" ) const ( @@ -28,7 +28,7 @@ func (r message) FindByID(ctx context.Context, id uint64) (*types.Message, error mod := &types.Message{} if err := db.GetContext(ctx, mod, sql, id); err != nil { - return nil, errors.Wrap(err, ErrDatabaseError.String()) + return nil, err } else if mod.ID == 0 { return nil, ErrMessageNotFound } else { @@ -44,7 +44,7 @@ func (r message) Find(ctx context.Context, filter *types.MessageFilter) ([]*type if filter != nil { if filter.Query != "" { - sql += "AND name LIKE ?" + sql += " AND message LIKE ?" params = append(params, filter.Query+"%") } } @@ -74,7 +74,7 @@ func (r message) Find(ctx context.Context, filter *types.MessageFilter) ([]*type rval := make([]*types.Message, 0) if err := db.SelectContext(ctx, &rval, sql, params...); err != nil { - return nil, errors.Wrap(err, ErrDatabaseError.String()) + return nil, err } else { return rval, nil } @@ -84,8 +84,10 @@ func (r message) Create(ctx context.Context, mod *types.Message) (*types.Message db := factory.Database.MustGet() mod.SetID(factory.Sonyflake.NextID()) + mod.SetCreatedAt(time.Now()) + if err := db.Insert("messages", mod); err != nil { - return nil, ErrDatabaseError + return nil, err } else { return mod, nil } @@ -94,8 +96,11 @@ func (r message) Create(ctx context.Context, mod *types.Message) (*types.Message func (r message) Update(ctx context.Context, mod *types.Message) (*types.Message, error) { db := factory.Database.MustGet() + now := time.Now() + mod.SetUpdatedAt(&now) + if err := db.Replace("messages", mod); err != nil { - return nil, ErrDatabaseError + return nil, err } else { return mod, nil } diff --git a/sam/repository/message_test.go b/sam/repository/message_test.go new file mode 100644 index 000000000..61513a5e0 --- /dev/null +++ b/sam/repository/message_test.go @@ -0,0 +1,54 @@ +package repository + +import ( + "context" + "github.com/crusttech/crust/sam/types" + "testing" +) + +func TestMessage(t *testing.T) { + var err error + + if testing.Short() { + t.Skip("skipping test in short mode.") + return + } + + rpo := Message() + ctx := context.Background() + att := types.Message{}.New() + + var msg1, msg2 = "Test message v1", "Test message v2" + + var aa []*types.Message + + att.SetMessage(msg1) + + att, err = rpo.Create(ctx, att) + must(t, err) + if att.Message != msg1 { + t.Fatal("Changes were not stored") + } + + att.SetMessage(msg2) + + att, err = rpo.Update(ctx, att) + must(t, err) + if att.Message != msg2 { + t.Fatal("Changes were not stored") + } + + att, err = rpo.FindByID(ctx, att.ID) + must(t, err) + if att.Message != msg2 { + t.Fatal("Changes were not stored") + } + + aa, err = rpo.Find(ctx, &types.MessageFilter{Query: msg2}) + must(t, err) + if len(aa) == 0 { + t.Fatal("No results found") + } + + must(t, rpo.Delete(ctx, att.ID)) +} diff --git a/sam/repository/organisation.go b/sam/repository/organisation.go index f7f5d47ae..cb34eea63 100644 --- a/sam/repository/organisation.go +++ b/sam/repository/organisation.go @@ -26,7 +26,7 @@ func (r organisation) FindByID(ctx context.Context, id uint64) (*types.Organisat mod := &types.Organisation{} if err := db.Get(mod, "SELECT * FROM organisations WHERE id = ? AND "+sqlOrganisationScope, id); err != nil { - return nil, ErrDatabaseError + return nil, err } else if mod.ID == 0 { return nil, ErrOrganisationNotFound } else { @@ -42,7 +42,7 @@ func (r organisation) Find(ctx context.Context, filter *types.OrganisationFilter if filter != nil { if filter.Query != "" { - sql += "AND ame LIKE ?" + sql += " AND name LIKE ?" params = append(params, filter.Query+"%") } } @@ -51,7 +51,7 @@ func (r organisation) Find(ctx context.Context, filter *types.OrganisationFilter rval := make([]*types.Organisation, 0) if err := db.Select(&rval, sql, params...); err != nil { - return nil, ErrDatabaseError + return nil, err } else { return rval, nil } @@ -61,8 +61,10 @@ func (r organisation) Create(ctx context.Context, mod *types.Organisation) (*typ db := factory.Database.MustGet() mod.SetID(factory.Sonyflake.NextID()) + mod.SetCreatedAt(time.Now()) + if err := db.Insert("organisations", mod); err != nil { - return nil, ErrDatabaseError + return nil, err } else { return mod, nil } @@ -71,8 +73,11 @@ func (r organisation) Create(ctx context.Context, mod *types.Organisation) (*typ func (r organisation) Update(ctx context.Context, mod *types.Organisation) (*types.Organisation, error) { db := factory.Database.MustGet() + now := time.Now() + mod.SetUpdatedAt(&now) + if err := db.Replace("organisations", mod); err != nil { - return nil, ErrDatabaseError + return nil, err } else { return mod, nil } diff --git a/sam/repository/organisation_test.go b/sam/repository/organisation_test.go new file mode 100644 index 000000000..aa96321ee --- /dev/null +++ b/sam/repository/organisation_test.go @@ -0,0 +1,56 @@ +package repository + +import ( + "context" + "github.com/crusttech/crust/sam/types" + "testing" +) + +func TestOrganisation(t *testing.T) { + var err error + + if testing.Short() { + t.Skip("skipping test in short mode.") + return + } + + rpo := Organisation() + ctx := context.Background() + att := types.Organisation{}.New() + + var name1, name2 = "Test organisation v1", "Test organisation v2" + + var aa []*types.Organisation + + att.SetName(name1) + + att, err = rpo.Create(ctx, att) + must(t, err) + if att.Name != name1 { + t.Fatal("Changes were not stored") + } + + att.SetName(name2) + + att, err = rpo.Update(ctx, att) + must(t, err) + if att.Name != name2 { + t.Fatal("Changes were not stored") + } + + att, err = rpo.FindByID(ctx, att.ID) + must(t, err) + if att.Name != name2 { + t.Fatal("Changes were not stored") + } + + aa, err = rpo.Find(ctx, &types.OrganisationFilter{Query: name2}) + must(t, err) + if len(aa) == 0 { + t.Fatal("No results found") + } + + must(t, rpo.Archive(ctx, att.ID)) + must(t, rpo.Unarchive(ctx, att.ID)) + must(t, rpo.Delete(ctx, att.ID)) +} diff --git a/sam/repository/reaction.go b/sam/repository/reaction.go index f314454eb..26559c965 100644 --- a/sam/repository/reaction.go +++ b/sam/repository/reaction.go @@ -4,11 +4,10 @@ import ( "context" "github.com/crusttech/crust/sam/types" "github.com/titpetric/factory" + "time" ) const ( - sqlReactionScope = "deleted_at IS NULL AND archived_at IS NULL" - ErrReactionNotFound = repositoryError("ReactionNotFound") ) @@ -24,8 +23,8 @@ func (r reaction) FindByID(ctx context.Context, id uint64) (*types.Reaction, err db := factory.Database.MustGet() mod := &types.Reaction{} - if err := db.GetContext(ctx, mod, "SELECT * FROM reactions WHERE id = ? AND "+sqlReactionScope, id); err != nil { - return nil, ErrDatabaseError + if err := db.GetContext(ctx, mod, "SELECT * FROM reactions WHERE id = ?", id); err != nil { + return nil, err } else if mod.ID == 0 { return nil, ErrReactionNotFound } else { @@ -44,7 +43,7 @@ func (r reaction) FindByRange(ctx context.Context, channelID, fromReactionID, to rval := make([]*types.Reaction, 0) if err := db.Select(&rval, sql, fromReactionID, toReactionID, channelID); err != nil { - return nil, ErrDatabaseError + return nil, err } return rval, nil @@ -54,8 +53,10 @@ func (r reaction) Create(ctx context.Context, mod *types.Reaction) (*types.React db := factory.Database.MustGet() mod.SetID(factory.Sonyflake.NextID()) + mod.SetCreatedAt(time.Now()) + if err := db.Insert("reactions", mod); err != nil { - return nil, ErrDatabaseError + return nil, err } else { return mod, nil } @@ -65,7 +66,7 @@ func (r reaction) Delete(ctx context.Context, id uint64) error { db := factory.Database.MustGet() if _, err := db.ExecContext(ctx, "DELETE FROM reactions WHERE id = ?", id); err != nil { - return ErrDatabaseError + return err } else { return nil } diff --git a/sam/repository/reaction_test.go b/sam/repository/reaction_test.go new file mode 100644 index 000000000..f83486d66 --- /dev/null +++ b/sam/repository/reaction_test.go @@ -0,0 +1,38 @@ +package repository + +import ( + "context" + "github.com/crusttech/crust/sam/types" + "testing" +) + +func TestReaction(t *testing.T) { + var err error + + if testing.Short() { + t.Skip("skipping test in short mode.") + return + } + + rpo := Reaction() + ctx := context.Background() + att := types.Reaction{}.New() + + var reaction = ":laugh:" + + att.SetReaction(reaction) + + att, err = rpo.Create(ctx, att) + must(t, err) + if att.Reaction != reaction { + t.Fatal("Changes were not stored") + } + + att, err = rpo.FindByID(ctx, att.ID) + must(t, err) + if att.Reaction != reaction { + t.Fatal("Changes were not stored") + } + + must(t, rpo.Delete(ctx, att.ID)) +} diff --git a/sam/repository/team.go b/sam/repository/team.go index 71c64df9b..eae0312d0 100644 --- a/sam/repository/team.go +++ b/sam/repository/team.go @@ -26,7 +26,7 @@ func (r team) FindByID(ctx context.Context, id uint64) (*types.Team, error) { mod := &types.Team{} if err := db.Get(mod, "SELECT * FROM teams WHERE id = ? AND "+sqlTeamScope, id); err != nil { - return nil, ErrDatabaseError + return nil, err } else if mod.ID == 0 { return nil, ErrTeamNotFound } else { @@ -42,7 +42,7 @@ func (r team) Find(ctx context.Context, filter *types.TeamFilter) ([]*types.Team if filter != nil { if filter.Query != "" { - sql += "AND name LIKE ?" + sql += " AND name LIKE ?" params = append(params, filter.Query+"%") } } @@ -51,7 +51,7 @@ func (r team) Find(ctx context.Context, filter *types.TeamFilter) ([]*types.Team rval := make([]*types.Team, 0) if err := db.Select(&rval, sql, params...); err != nil { - return nil, ErrDatabaseError + return nil, err } else { return rval, nil } @@ -61,8 +61,10 @@ func (r team) Create(ctx context.Context, mod *types.Team) (*types.Team, error) db := factory.Database.MustGet() mod.SetID(factory.Sonyflake.NextID()) + mod.SetCreatedAt(time.Now()) + if err := db.Insert("teams", mod); err != nil { - return nil, ErrDatabaseError + return nil, err } else { return mod, nil } @@ -71,8 +73,11 @@ func (r team) Create(ctx context.Context, mod *types.Team) (*types.Team, error) func (r team) Update(ctx context.Context, mod *types.Team) (*types.Team, error) { db := factory.Database.MustGet() + now := time.Now() + mod.SetUpdatedAt(&now) + if err := db.Replace("teams", mod); err != nil { - return nil, ErrDatabaseError + return nil, err } else { return mod, nil } diff --git a/sam/repository/team_test.go b/sam/repository/team_test.go new file mode 100644 index 000000000..0a044721b --- /dev/null +++ b/sam/repository/team_test.go @@ -0,0 +1,56 @@ +package repository + +import ( + "context" + "github.com/crusttech/crust/sam/types" + "testing" +) + +func TestTeam(t *testing.T) { + var err error + + if testing.Short() { + t.Skip("skipping test in short mode.") + return + } + + rpo := Team() + ctx := context.Background() + att := types.Team{}.New() + + var name1, name2 = "Test team v1", "Test team v2" + + var aa []*types.Team + + att.SetName(name1) + + att, err = rpo.Create(ctx, att) + must(t, err) + if att.Name != name1 { + t.Fatal("Changes were not stored") + } + + att.SetName(name2) + + att, err = rpo.Update(ctx, att) + must(t, err) + if att.Name != name2 { + t.Fatal("Changes were not stored") + } + + att, err = rpo.FindByID(ctx, att.ID) + must(t, err) + if att.Name != name2 { + t.Fatal("Changes were not stored") + } + + aa, err = rpo.Find(ctx, &types.TeamFilter{Query: name2}) + must(t, err) + if len(aa) == 0 { + t.Fatal("No results found") + } + + must(t, rpo.Archive(ctx, att.ID)) + must(t, rpo.Unarchive(ctx, att.ID)) + must(t, rpo.Delete(ctx, att.ID)) +} diff --git a/sam/repository/user.go b/sam/repository/user.go index 15e9cd12d..4989ef3d0 100644 --- a/sam/repository/user.go +++ b/sam/repository/user.go @@ -27,7 +27,7 @@ func (r user) FindByUsername(ctx context.Context, username string) (*types.User, mod := &types.User{} if err := db.Get(mod, "SELECT * FROM users WHERE username = ? AND "+sqlUserScope, username); err != nil { - return nil, ErrDatabaseError + return nil, err } else if mod.ID == 0 { return nil, ErrUserNotFound } else { @@ -40,7 +40,7 @@ func (r user) FindByID(ctx context.Context, id uint64) (*types.User, error) { mod := &types.User{} if err := db.Get(mod, "SELECT * FROM users WHERE id = ? AND "+sqlUserScope, id); err != nil { - return nil, ErrDatabaseError + return nil, err } else if mod.ID == 0 { return nil, ErrUserNotFound } else { @@ -56,7 +56,7 @@ func (r user) Find(ctx context.Context, filter *types.UserFilter) ([]*types.User if filter != nil { if filter.Query != "" { - sql += "AND username LIKE ?" + sql += " AND username LIKE ?" params = append(params, filter.Query+"%") } } @@ -65,7 +65,7 @@ func (r user) Find(ctx context.Context, filter *types.UserFilter) ([]*types.User rval := make([]*types.User, 0) if err := db.Select(&rval, sql, params...); err != nil { - return nil, ErrDatabaseError + return nil, err } else { return rval, nil } @@ -75,8 +75,14 @@ func (r user) Create(ctx context.Context, mod *types.User) (*types.User, error) db := factory.Database.MustGet() mod.SetID(factory.Sonyflake.NextID()) + mod.SetCreatedAt(time.Now()) + + if mod.Meta == nil { + mod.SetMeta([]byte("{}")) + } + if err := db.Insert("users", mod); err != nil { - return nil, ErrDatabaseError + return nil, err } else { return mod, nil } @@ -85,8 +91,11 @@ func (r user) Create(ctx context.Context, mod *types.User) (*types.User, error) func (r user) Update(ctx context.Context, mod *types.User) (*types.User, error) { db := factory.Database.MustGet() + now := time.Now() + mod.SetUpdatedAt(&now) + if err := db.Replace("users", mod); err != nil { - return nil, ErrDatabaseError + return nil, err } else { return mod, nil } diff --git a/sam/repository/user_test.go b/sam/repository/user_test.go new file mode 100644 index 000000000..6aa9b916f --- /dev/null +++ b/sam/repository/user_test.go @@ -0,0 +1,54 @@ +package repository + +import ( + "context" + "github.com/crusttech/crust/sam/types" + "testing" +) + +func TestUser(t *testing.T) { + var err error + + if testing.Short() { + t.Skip("skipping test in short mode.") + return + } + + rpo := User() + ctx := context.Background() + att := types.User{}.New() + + var name1, name2 = "Test user v1", "Test user v2" + + var aa []*types.User + + att.SetUsername(name1) + + att, err = rpo.Create(ctx, att) + must(t, err) + if att.Username != name1 { + t.Fatal("Changes were not stored") + } + + att.SetUsername(name2) + + att, err = rpo.Update(ctx, att) + must(t, err) + if att.Username != name2 { + t.Fatal("Changes were not stored") + } + + att, err = rpo.FindByID(ctx, att.ID) + must(t, err) + if att.Username != name2 { + t.Fatal("Changes were not stored") + } + + aa, err = rpo.Find(ctx, &types.UserFilter{Query: name2}) + must(t, err) + if len(aa) == 0 { + t.Fatal("No results found") + } + + must(t, rpo.Delete(ctx, att.ID)) +} diff --git a/sam/types/channel.go b/sam/types/channel.go index 9b38ced03..c0fb56c6c 100644 --- a/sam/types/channel.go +++ b/sam/types/channel.go @@ -28,8 +28,10 @@ type ( Topic string `db:"-"` Meta json.RawMessage `db:"meta"` LastMessageID uint64 `json:",omitempty" db:"rel_last_message"` - ArchivedAt *time.Time `json:",omitempty" db:"archived_at"` - DeletedAt *time.Time `json:",omitempty" db:"deleted_at"` + CreatedAt time.Time `json:"created_at,omitempty" db:"created_at"` + UpdatedAt *time.Time `json:"updated_at,omitempty" db:"updated_at"` + ArchivedAt *time.Time `json:"archived_at,omitempty" db:"archived_at"` + DeletedAt *time.Time `json:"deleted_at,omitempty" db:"deleted_at"` changed []string } @@ -108,6 +110,30 @@ func (c *Channel) SetLastMessageID(value uint64) *Channel { return c } +// Get the value of CreatedAt +func (c *Channel) GetCreatedAt() time.Time { + return c.CreatedAt +} + +// Set the value of CreatedAt +func (c *Channel) SetCreatedAt(value time.Time) *Channel { + c.changed = append(c.changed, "CreatedAt") + c.CreatedAt = value + return c +} + +// Get the value of UpdatedAt +func (c *Channel) GetUpdatedAt() *time.Time { + return c.UpdatedAt +} + +// Set the value of UpdatedAt +func (c *Channel) SetUpdatedAt(value *time.Time) *Channel { + c.changed = append(c.changed, "UpdatedAt") + c.UpdatedAt = value + return c +} + // Get the value of ArchivedAt func (c *Channel) GetArchivedAt() *time.Time { return c.ArchivedAt diff --git a/sam/types/message.go b/sam/types/message.go index 1fd167f45..64fc1b810 100644 --- a/sam/types/message.go +++ b/sam/types/message.go @@ -29,20 +29,21 @@ type ( UserID uint64 `db:"rel_user"` ChannelID uint64 `db:"rel_channel"` ReplyTo uint64 `db:"reply_to"` - UpdatedAt *time.Time `json:",omitempty" db:"updated_at"` - DeletedAt *time.Time `json:",omitempty" db:"deleted_at"` + CreatedAt time.Time `json:"created_at,omitempty" db:"created_at"` + UpdatedAt *time.Time `json:"updated_at,omitempty" db:"updated_at"` + DeletedAt *time.Time `json:"deleted_at,omitempty" db:"deleted_at"` changed []string } // Messages - Reaction struct { - ID uint64 `db:"id"` - UserID uint64 `db:"rel_user"` - MessageID uint64 `db:"rel_message"` - ChannelID uint64 `db:"rel_channel"` - Reaction string `db:"reaction"` - DeletedAt *time.Time `json:",omitempty" db:"deleted_at"` + ID uint64 `db:"id"` + UserID uint64 `db:"rel_user"` + MessageID uint64 `db:"rel_message"` + ChannelID uint64 `db:"rel_channel"` + Reaction string `db:"reaction"` + CreatedAt time.Time `json:"created_at,omitempty" db:"created_at"` changed []string } @@ -54,7 +55,14 @@ type ( MessageID uint64 `db:"rel_message"` ChannelID uint64 `db:"rel_channel"` Attachment json.RawMessage `db:"attachment"` - DeletedAt *time.Time `json:",omitempty" db:"deleted_at"` + Url string `db:"url"` + PreviewUrl string `db:"preview_url"` + Size uint `db:"size"` + Mimetype string `db:"mimetype"` + Name string `db:"name"` + CreatedAt time.Time `json:"created_at,omitempty" db:"created_at"` + UpdatedAt *time.Time `json:"updated_at,omitempty" db:"updated_at"` + DeletedAt *time.Time `json:"deleted_at,omitempty" db:"deleted_at"` changed []string } @@ -159,6 +167,18 @@ func (m *Message) SetReplyTo(value uint64) *Message { return m } +// Get the value of CreatedAt +func (m *Message) GetCreatedAt() time.Time { + return m.CreatedAt +} + +// Set the value of CreatedAt +func (m *Message) SetCreatedAt(value time.Time) *Message { + m.changed = append(m.changed, "CreatedAt") + m.CreatedAt = value + return m +} + // Get the value of UpdatedAt func (m *Message) GetUpdatedAt() *time.Time { return m.UpdatedAt @@ -258,15 +278,15 @@ func (m *Reaction) SetReaction(value string) *Reaction { return m } -// Get the value of DeletedAt -func (m *Reaction) GetDeletedAt() *time.Time { - return m.DeletedAt +// Get the value of CreatedAt +func (m *Reaction) GetCreatedAt() time.Time { + return m.CreatedAt } -// Set the value of DeletedAt -func (m *Reaction) SetDeletedAt(value *time.Time) *Reaction { - m.changed = append(m.changed, "DeletedAt") - m.DeletedAt = value +// Set the value of CreatedAt +func (m *Reaction) SetCreatedAt(value time.Time) *Reaction { + m.changed = append(m.changed, "CreatedAt") + m.CreatedAt = value return m } @@ -343,6 +363,100 @@ func (m *Attachment) SetAttachment(value json.RawMessage) *Attachment { return m } +// Get the value of Url +func (m *Attachment) GetUrl() string { + return m.Url +} + +// Set the value of Url +func (m *Attachment) SetUrl(value string) *Attachment { + if m.Url != value { + m.changed = append(m.changed, "Url") + m.Url = value + } + return m +} + +// Get the value of PreviewUrl +func (m *Attachment) GetPreviewUrl() string { + return m.PreviewUrl +} + +// Set the value of PreviewUrl +func (m *Attachment) SetPreviewUrl(value string) *Attachment { + if m.PreviewUrl != value { + m.changed = append(m.changed, "PreviewUrl") + m.PreviewUrl = value + } + return m +} + +// Get the value of Size +func (m *Attachment) GetSize() uint { + return m.Size +} + +// Set the value of Size +func (m *Attachment) SetSize(value uint) *Attachment { + if m.Size != value { + m.changed = append(m.changed, "Size") + m.Size = value + } + return m +} + +// Get the value of Mimetype +func (m *Attachment) GetMimetype() string { + return m.Mimetype +} + +// Set the value of Mimetype +func (m *Attachment) SetMimetype(value string) *Attachment { + if m.Mimetype != value { + m.changed = append(m.changed, "Mimetype") + m.Mimetype = value + } + return m +} + +// Get the value of Name +func (m *Attachment) GetName() string { + return m.Name +} + +// Set the value of Name +func (m *Attachment) SetName(value string) *Attachment { + if m.Name != value { + m.changed = append(m.changed, "Name") + m.Name = value + } + return m +} + +// Get the value of CreatedAt +func (m *Attachment) GetCreatedAt() time.Time { + return m.CreatedAt +} + +// Set the value of CreatedAt +func (m *Attachment) SetCreatedAt(value time.Time) *Attachment { + m.changed = append(m.changed, "CreatedAt") + m.CreatedAt = value + return m +} + +// Get the value of UpdatedAt +func (m *Attachment) GetUpdatedAt() *time.Time { + return m.UpdatedAt +} + +// Set the value of UpdatedAt +func (m *Attachment) SetUpdatedAt(value *time.Time) *Attachment { + m.changed = append(m.changed, "UpdatedAt") + m.UpdatedAt = value + return m +} + // Get the value of DeletedAt func (m *Attachment) GetDeletedAt() *time.Time { return m.DeletedAt diff --git a/sam/types/organisation.go b/sam/types/organisation.go index 21454beb0..c58e00285 100644 --- a/sam/types/organisation.go +++ b/sam/types/organisation.go @@ -23,9 +23,12 @@ type ( // Organisations - Organisations represent a top-level grouping entity. There may be many organisations defined in a single deployment. Organisation struct { ID uint64 `db:"id"` + FQN string `db:"fqn"` Name string `db:"name"` - ArchivedAt *time.Time `json:",omitempty" db:"archived_at"` - DeletedAt *time.Time `json:",omitempty" db:"deleted_at"` + CreatedAt time.Time `json:"created_at,omitempty" db:"created_at"` + UpdatedAt *time.Time `json:"updated_at,omitempty" db:"updated_at"` + ArchivedAt *time.Time `json:"archived_at,omitempty" db:"archived_at"` + DeletedAt *time.Time `json:"deleted_at,omitempty" db:"deleted_at"` changed []string } @@ -50,6 +53,20 @@ func (o *Organisation) SetID(value uint64) *Organisation { return o } +// Get the value of FQN +func (o *Organisation) GetFQN() string { + return o.FQN +} + +// Set the value of FQN +func (o *Organisation) SetFQN(value string) *Organisation { + if o.FQN != value { + o.changed = append(o.changed, "FQN") + o.FQN = value + } + return o +} + // Get the value of Name func (o *Organisation) GetName() string { return o.Name @@ -64,6 +81,30 @@ func (o *Organisation) SetName(value string) *Organisation { return o } +// Get the value of CreatedAt +func (o *Organisation) GetCreatedAt() time.Time { + return o.CreatedAt +} + +// Set the value of CreatedAt +func (o *Organisation) SetCreatedAt(value time.Time) *Organisation { + o.changed = append(o.changed, "CreatedAt") + o.CreatedAt = value + return o +} + +// Get the value of UpdatedAt +func (o *Organisation) GetUpdatedAt() *time.Time { + return o.UpdatedAt +} + +// Set the value of UpdatedAt +func (o *Organisation) SetUpdatedAt(value *time.Time) *Organisation { + o.changed = append(o.changed, "UpdatedAt") + o.UpdatedAt = value + return o +} + // Get the value of ArchivedAt func (o *Organisation) GetArchivedAt() *time.Time { return o.ArchivedAt diff --git a/sam/types/team.go b/sam/types/team.go index 90584a354..7c8c0f029 100644 --- a/sam/types/team.go +++ b/sam/types/team.go @@ -24,10 +24,11 @@ type ( Team struct { ID uint64 `db:"id"` Name string `db:"name"` - MemberIDs []uint64 `json:"-" db:"member_i_ds"` - Members []User `json:",omitempty" db:"members"` - ArchivedAt *time.Time `json:",omitempty" db:"archived_at"` - DeletedAt *time.Time `json:",omitempty" db:"deleted_at"` + Handle string `db:"handle"` + CreatedAt time.Time `json:"created_at,omitempty" db:"created_at"` + UpdatedAt *time.Time `json:"updated_at,omitempty" db:"updated_at"` + ArchivedAt *time.Time `json:"archived_at,omitempty" db:"archived_at"` + DeletedAt *time.Time `json:"deleted_at,omitempty" db:"deleted_at"` changed []string } @@ -66,27 +67,41 @@ func (t *Team) SetName(value string) *Team { return t } -// Get the value of MemberIDs -func (t *Team) GetMemberIDs() []uint64 { - return t.MemberIDs +// Get the value of Handle +func (t *Team) GetHandle() string { + return t.Handle } -// Set the value of MemberIDs -func (t *Team) SetMemberIDs(value []uint64) *Team { - t.changed = append(t.changed, "MemberIDs") - t.MemberIDs = value +// Set the value of Handle +func (t *Team) SetHandle(value string) *Team { + if t.Handle != value { + t.changed = append(t.changed, "Handle") + t.Handle = value + } return t } -// Get the value of Members -func (t *Team) GetMembers() []User { - return t.Members +// Get the value of CreatedAt +func (t *Team) GetCreatedAt() time.Time { + return t.CreatedAt } -// Set the value of Members -func (t *Team) SetMembers(value []User) *Team { - t.changed = append(t.changed, "Members") - t.Members = value +// Set the value of CreatedAt +func (t *Team) SetCreatedAt(value time.Time) *Team { + t.changed = append(t.changed, "CreatedAt") + t.CreatedAt = value + return t +} + +// Get the value of UpdatedAt +func (t *Team) GetUpdatedAt() *time.Time { + return t.UpdatedAt +} + +// Set the value of UpdatedAt +func (t *Team) SetUpdatedAt(value *time.Time) *Team { + t.changed = append(t.changed, "UpdatedAt") + t.UpdatedAt = value return t } diff --git a/sam/types/user.go b/sam/types/user.go index 4918f9613..b84747f35 100644 --- a/sam/types/user.go +++ b/sam/types/user.go @@ -27,8 +27,10 @@ type ( Meta interface{} `json:"-" db:"meta"` OrganisationID uint64 `db:"rel_organisation"` Password []byte `json:"-" db:"password"` - SuspendedAt *time.Time `json:",omitempty" db:"suspended_at"` - DeletedAt *time.Time `json:",omitempty" db:"deleted_at"` + CreatedAt time.Time `json:"created_at,omitempty" db:"created_at"` + UpdatedAt *time.Time `json:"updated_at,omitempty" db:"updated_at"` + SuspendedAt *time.Time `json:"suspended_at,omitempty" db:"suspended_at"` + DeletedAt *time.Time `json:"deleted_at,omitempty" db:"deleted_at"` changed []string } @@ -107,6 +109,30 @@ func (u *User) SetPassword(value []byte) *User { return u } +// Get the value of CreatedAt +func (u *User) GetCreatedAt() time.Time { + return u.CreatedAt +} + +// Set the value of CreatedAt +func (u *User) SetCreatedAt(value time.Time) *User { + u.changed = append(u.changed, "CreatedAt") + u.CreatedAt = value + return u +} + +// Get the value of UpdatedAt +func (u *User) GetUpdatedAt() *time.Time { + return u.UpdatedAt +} + +// Set the value of UpdatedAt +func (u *User) SetUpdatedAt(value *time.Time) *User { + u.changed = append(u.changed, "UpdatedAt") + u.UpdatedAt = value + return u +} + // Get the value of SuspendedAt func (u *User) GetSuspendedAt() *time.Time { return u.SuspendedAt