Commit efecfd31 authored by Tit Petric's avatar Tit Petric
Browse files

Merge branch 'master' of https://github.com/crusttech/crust

parents b6481141 ff792462
package {package}
import (
"context"
"github.com/crusttech/crust/sam/rest/server"
"github.com/pkg/errors"
)
......@@ -13,7 +15,7 @@ func ({name}) New() *{name} {
}
{foreach $calls as $call}
func (*{name}) {call.name|capitalize}(r *{name|lcfirst}{call.name|capitalize}Request) (interface{}, error) {
func (ctrl *{name}) {call.name|capitalize}(ctx context.Context, r *server.{name|ucfirst}{call.name|capitalize}Request) (interface{}, error) {
return nil, errors.New("Not implemented: {name}.{call.name}")
}
......
......@@ -10,7 +10,7 @@ import (
func MountRoutes(r chi.Router) {
{foreach $apis as $api}
{api.interface|strtolower} := &server.{api.interface|capitalize}Handlers{{api.interface|capitalize}{ldelim}{rdelim}.New()}
{api.interface|strtolower} := &server.{api.interface|capitalize}Handlers{{api.interface|capitalize}:{api.interface|capitalize}{ldelim}{rdelim}.New()}
{/foreach}
{foreach $apis as $api}
r.Group(func (r chi.Router) {
......@@ -22,4 +22,4 @@ func MountRoutes(r chi.Router) {
})
})
{/foreach}
}
\ No newline at end of file
}
......@@ -22,8 +22,8 @@ import (
)
func MountRoutes(r chi.Router) {
field := &server.FieldHandlers{Field{}.New()}
module := &server.ModuleHandlers{Module{}.New()}
field := &server.FieldHandlers{Field: Field{}.New()}
module := &server.ModuleHandlers{Module: Module{}.New()}
r.Group(func(r chi.Router) {
r.Use(field.Field.Authenticator())
r.Route("/field", func(r chi.Router) {
......
......@@ -72,60 +72,64 @@ CREATE TABLE channel_members (
PRIMARY KEY (rel_channel, rel_user)
);
CREATE TABLE messages (
id BIGINT UNSIGNED NOT NULL,
-- basic set:
-- NULL for common text messages
-- image/*; disposition=inline for inline images (does not follow stdandard http headers, but
-- crust/system body holds one of defined system messages (user joining, parting...)
-- crust/reaction body holds reaction to a message
-- crust/preview body holds preview of the message it relates to in form of metadata (no binary blobs!)
-- crust/url body holds only URL
-- crust/pin message it relates to is pinned, body holds pin details
-- crust/flag message it relates to is flaged, body holds flag information
-- */* body holds reference to the uploaded file and its metadata
--
mimetype VARCHAR(255) NULL,
CREATE TABLE channel_views (
rel_channel BIGINT UNSIGNED NOT NULL REFERENCES channels(id),
rel_user BIGINT UNSIGNED NOT NULL REFERENCES users(id),
-- null body only valid when rel_message is set => message removal
body TEXT NULL,
-- timestamp of last view, should be enough to find out which messaghr
viewed_at DATETIME NOT NULL DEFAULT NOW(),
-- the contributor
rel_user BIGINT UNSIGNED NOT NULL REFERENCES users(id),
-- new messages count since last view
new_since INT UNSIGNED NOT NULL DEFAULT 0,
-- message's channel
rel_channel BIGINT UNSIGNED NOT NULL REFERENCES channels(id),
PRIMARY KEY (rel_user, rel_channel)
);
-- replies, edits, reactions, flags, attachments...
rel_message BIGINT UNSIGNED NOT NULL REFERENCES messages(id),
CREATE TABLE channel_pins (
rel_channel BIGINT UNSIGNED NOT NULL REFERENCES channels(id),
rel_message BIGINT UNSIGNED NULL REFERENCES messages(id),
rel_user BIGINT UNSIGNED NOT NULL REFERENCES users(id),
PRIMARY KEY (id)
PRIMARY KEY (rel_channel, rel_message)
);
CREATE TABLE messages (
id BIGINT UNSIGNED NOT NULL,
type TEXT,
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),
-- temp copy of messages (when they are pushed to the primary organisation, row gets removed)
CREATE TABLE messages_queue (
id BIGINT UNSIGNED NOT NULL,
mimetype VARCHAR(255) NULL,
body TEXT NULL,
rel_user BIGINT UNSIGNED NOT NULL REFERENCES users(id),
rel_channel BIGINT UNSIGNED NOT NULL REFERENCES channels(id),
rel_message BIGINT UNSIGNED NOT NULL REFERENCES messages(id),
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,
CREATE TABLE channel_views (
rel_channel BIGINT UNSIGNED NOT NULL REFERENCES channels(id),
rel_user BIGINT UNSIGNED NOT NULL REFERENCES users(id),
-- timestamp of last view, should be enough to find out which messaghr
viewed_at DATETIME NOT NULL DEFAULT NOW(),
PRIMARY KEY (id)
);
-- new messages count since last view
new_since INT UNSIGNED NOT NULL DEFAULT 0,
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,
PRIMARY KEY (rel_user, rel_channel)
PRIMARY KEY (id)
);
......@@ -303,16 +303,41 @@
"struct": [
{
"name": "Message",
"imports": ["time"],
"fields": [
{ "name": "ID", "type": "uint64" },
{ "name": "Type", "type": "string" },
{ "name": "Message", "type": "string" },
{ "name": "UserId", "type": "uint64", "dbname": "rel_user" },
{ "name": "ChannelId", "type": "uint64", "dbname": "rel_channel"},
{ "name": "ReplyTo", "type": "uint64", "dbname": "rel_message" },
{ "name": "UpdatedAt", "type": "*time.Time", "tag": "json:\",omitempty\"", "complex": true },
{ "name": "DeletedAt", "type": "*time.Time", "tag": "json:\",omitempty\"", "complex": true }
]
},
{
"name": "Reaction",
"imports": ["time"],
"fields": [
{ "name": "ID", "type": "uint64" },
{ "name": "UserId", "type": "uint64", "dbname": "rel_user" },
{ "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": "Attachment",
"imports": ["time", "encoding/json"],
"fields": [
{ "name": "Service", "type": "string" },
{ "name": "Channel", "type": "string" },
{ "name": "UserName", "type": "string" },
{ "name": "UserID", "type": "uint64" },
{ "name": "User", "type": "*User", "complex": true },
{ "name": "UserAvatar", "type": "string" },
{ "name": "Message", "type": "string" },
{ "name": "MessageID", "type": "string" },
{ "name": "Type", "type": "MessageType" }
{ "name": "ID", "type": "uint64" },
{ "name": "UserId", "type": "uint64", "dbname": "rel_user" },
{ "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 }
]
}
],
......
......@@ -6,44 +6,125 @@
{
"fields": [
{
"name": "Service",
"type": "string"
"name": "ID",
"type": "uint64"
},
{
"name": "Channel",
"name": "Type",
"type": "string"
},
{
"name": "UserName",
"name": "Message",
"type": "string"
},
{
"name": "UserID",
"dbname": "rel_user",
"name": "UserId",
"type": "uint64"
},
{
"dbname": "rel_channel",
"name": "ChannelId",
"type": "uint64"
},
{
"dbname": "rel_message",
"name": "ReplyTo",
"type": "uint64"
},
{
"complex": true,
"name": "User",
"type": "*User"
"name": "UpdatedAt",
"tag": "json:\",omitempty\"",
"type": "*time.Time"
},
{
"name": "UserAvatar",
"type": "string"
"complex": true,
"name": "DeletedAt",
"tag": "json:\",omitempty\"",
"type": "*time.Time"
}
],
"imports": [
"time"
],
"name": "Message"
},
{
"fields": [
{
"name": "ID",
"type": "uint64"
},
{
"name": "Message",
"type": "string"
"dbname": "rel_user",
"name": "UserId",
"type": "uint64"
},
{
"name": "MessageID",
"dbname": "rel_message",
"name": "MessageId",
"type": "uint64"
},
{
"dbname": "rel_channel",
"name": "ChannelId",
"type": "uint64"
},
{
"name": "Reaction",
"type": "string"
},
{
"name": "Type",
"type": "MessageType"
"complex": true,
"name": "DeletedAt",
"tag": "json:\",omitempty\"",
"type": "*time.Time"
}
],
"name": "Message"
"imports": [
"time"
],
"name": "Reaction"
},
{
"fields": [
{
"name": "ID",
"type": "uint64"
},
{
"dbname": "rel_user",
"name": "UserId",
"type": "uint64"
},
{
"dbname": "rel_message",
"name": "MessageId",
"type": "uint64"
},
{
"dbname": "rel_channel",
"name": "ChannelId",
"type": "uint64"
},
{
"complex": true,
"name": "Attachment",
"type": "json.RawMessage"
},
{
"complex": true,
"name": "DeletedAt",
"tag": "json:\",omitempty\"",
"type": "*time.Time"
}
],
"imports": [
"time",
"encoding/json"
],
"name": "Attachment"
}
],
"Protocol": "",
......
package repository
import (
"context"
"github.com/crusttech/crust/sam/types"
"github.com/titpetric/factory"
)
const (
sqlAttachmentScope = "deleted_at IS NULL AND archived_at IS NULL"
ErrAttachmentNotFound = repositoryError("AttachmentNotFound")
)
type (
attachment struct{}
)
func Attachment() attachment {
return attachment{}
}
func (r attachment) FindById(ctx context.Context, id uint64) (*types.Attachment, error) {
db := factory.Database.MustGet()
mod := &types.Attachment{}
if err := db.GetContext(ctx, mod, "SELECT * FROM attachments WHERE id = ? AND "+sqlAttachmentScope, id); err != nil {
return nil, ErrDatabaseError
} else if mod.ID == 0 {
return nil, ErrAttachmentNotFound
} else {
return mod, nil
}
}
func (r attachment) FindByRange(ctx context.Context, channelId, fromAttachmentId, toAttachmentId uint64) ([]*types.Attachment, error) {
db := factory.Database.MustGet()
sql := `
SELECT *
FROM attachments
WHERE rel_attachment 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
}
return rval, nil
}
func (r attachment) Create(ctx context.Context, mod *types.Attachment) (*types.Attachment, error) {
db := factory.Database.MustGet()
mod.SetID(factory.Sonyflake.NextID())
if err := db.Insert("attachments", mod); err != nil {
return nil, ErrDatabaseError
} else {
return mod, nil
}
}
func (r attachment) Update(ctx context.Context, mod *types.Attachment) (*types.Attachment, error) {
db := factory.Database.MustGet()
if err := db.Replace("attachments", mod); err != nil {
return nil, ErrDatabaseError
} else {
return mod, nil
}
}
func (r attachment) Delete(ctx context.Context, id uint64) error {
return simpleDelete(ctx, "attachments", id)
}
......@@ -11,7 +11,7 @@ func simpleUpdate(ctx context.Context, tableName, columnName string, value inter
sql := fmt.Sprintf("UPDATE %s SET %s = ? WHERE id = ?", tableName, columnName)
if _, err := db.Exec(sql, value, id); err != nil {
if _, err := db.ExecContext(ctx, sql, value, id); err != nil {
return ErrDatabaseError
} else {
return nil
......@@ -23,7 +23,7 @@ func simpleDelete(ctx context.Context, tableName string, id uint64) error {
sql := fmt.Sprintf("DELETE %s WHERE id = ?", tableName)
if _, err := db.Exec(sql, id); err != nil {
if _, err := db.ExecContext(ctx, sql, id); err != nil {
return ErrDatabaseError
} else {
return nil
......
package repository
import (
"context"
"github.com/crusttech/crust/sam/types"
"github.com/titpetric/factory"
)
const (
sqlMessageScope = "deleted_at IS NULL"
ErrMessageNotFound = repositoryError("MessageNotFound")
)
type (
message struct{}
)
func Message() message {
return message{}
}
func (r message) FindById(ctx context.Context, id uint64) (*types.Message, error) {
db := factory.Database.MustGet()
mod := &types.Message{}
if err := db.GetContext(ctx, mod, "SELECT * FROM messages WHERE id = ? AND "+sqlMessageScope, id); err != nil {
return nil, ErrDatabaseError
} else if mod.ID == 0 {
return nil, ErrMessageNotFound
} else {
return mod, nil
}
}
func (r message) Find(ctx context.Context, filter *types.MessageFilter) ([]*types.Message, error) {
db := factory.Database.MustGet()
var params = make([]interface{}, 0)
sql := "SELECT * FROM messages WHERE " + sqlMessageScope
if filter != nil {
if filter.Query != "" {
sql += "AND name LIKE ?"
params = append(params, filter.Query+"%")
}
}
sql += " ORDER BY name ASC"
rval := make([]*types.Message, 0)
if err := db.SelectContext(ctx, &rval, sql, params...); err != nil {
return nil, ErrDatabaseError
} else {
return rval, nil
}
}
func (r message) Create(ctx context.Context, mod *types.Message) (*types.Message, error) {
db := factory.Database.MustGet()
mod.SetID(factory.Sonyflake.NextID())
if err := db.Insert("messages", mod); err != nil {
return nil, ErrDatabaseError
} else {
return mod, nil
}
}
func (r message) Update(ctx context.Context, mod *types.Message) (*types.Message, error) {
db := factory.Database.MustGet()
if err := db.Replace("messages", mod); err != nil {
return nil, ErrDatabaseError
} else {
return mod, nil
}
}
func (r message) Delete(ctx context.Context, id uint64) error {
return simpleDelete(ctx, "messages", id)
}
package repository
import (
"context"
"github.com/crusttech/crust/sam/types"
"github.com/titpetric/factory"
)
const (
sqlReactionScope = "deleted_at IS NULL AND archived_at IS NULL"
ErrReactionNotFound = repositoryError("ReactionNotFound")
)
type (
reaction struct{}
)
func Reaction() reaction {
return reaction{}
}
func (r reaction) FindById(ctx context.Context, id uint64) (*types.Reaction, error) {
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
} else if mod.ID == 0 {
return nil, ErrReactionNotFound
} else {
return mod, nil
}
}
func (r reaction) FindByRange(ctx context.Context, channelId, fromReactionId, toReactionId uint64) ([]*types.Reaction, error) {
db := factory.Database.MustGet()
sql := `
SELECT *
FROM reactions
WHERE rel_reaction BETWEEN ? AND ?
AND rel_channel = ?`
rval := make([]*types.Reaction, 0)
if err := db.Select(&rval, sql, fromReactionId, toReactionId, channelId); err != nil {
return nil, ErrDatabaseError
}
return rval, nil
}
func (r reaction) Create(ctx context.Context, mod *types.Reaction) (*types.Reaction, error) {
db := factory.Database.MustGet()
mod.SetID(factory.Sonyflake.NextID())
if err := db.Insert("reactions", mod); err != nil {
return nil, ErrDatabaseError
} else {
return mod, nil
}
}
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
} else {
return nil
}
}
......@@ -18,11 +18,11 @@ func (*Team) Authenticator() func(http.Handler) http.Handler {
return pass
}
func (*Channel) Authenticator() func(http.Handler) http.Handler {
func (*Message) Authenticator() func(http.Handler) http.Handler {
return pass
}
func (*Message) Authenticator() func(http.Handler) http.Handler {
func (*Channel) Authenticator() func(http.Handler) http.Handler {
return pass
}
......
......@@ -2,44 +2,42 @@ package rest
import (
"context"
"github.com/pkg/errors"
"github.com/crusttech/crust/sam/rest/server"
_ "github.com/crusttech/crust/sam/types"
"github.com/pkg/errors"
)
var _ = errors.Wrap
type Message struct{}
func (Message) New() *Message {
return &Message{}
}
var _ = errors.Wrap
func (*Message) Edit(ctx context.Context, r *server.MessageEditRequest) (interface{}, error) {
func (ctrl *Message) Edit(ctx context.Context, r *server.MessageEditRequest) (interface{}, error) {
return nil, errors.New("Not implemented: Message.edit")
}
func (*Message) Attach(ctx context.Context, r *server.MessageAttachRequest) (interface{}, error) {
func (ctrl *Message) Attach(ctx context.Context, r *server.MessageAttachRequest) (interface{}, error) {
return nil, errors.New("Not implemented: Message.attach")
}
func (*Message) Remove(ctx context.Context, r *server.MessageRemoveRequest) (interface{}, error) {
func (ctrl *Message) Remove(ctx context.Context, r *server.MessageRemoveRequest) (interface{}, error) {
return nil, errors.New("Not implemented: Message.remove")
}
func (*Message) Read(ctx context.Context, r *server.MessageReadRequest) (interface{}, error) {
func (ctrl *Message) Read(ctx context.Context, r *server.MessageReadRequest) (interface{}, error) {
return nil, errors.New("Not implemented: Message.read")
}
func (*Message) Search(ctx context.Context, r *server.MessageSearchRequest) (interface{}, error) {
func (ctrl *Message) Search(ctx context.Context, r *server.MessageSearchRequest) (interface{}, error) {
return nil, errors.New("Not implemented: Message.search")
}
func (*Message) Pin(ctx context.Context, r *server.MessagePinRequest) (interface{}, error) {
func (ctrl *Message) Pin(ctx context.Context, r *server.MessagePinRequest) (interface{}, error) {
return nil, errors.New("Not implemented: Message.pin")
}
func (*Message) Flag(ctx context.Context, r *server.MessageFlagRequest) (interface{}, error) {
func (ctrl *Message) Flag(ctx context.Context, r *server.MessageFlagRequest) (interface{}, error) {
return nil, errors.New("Not implemented: Message.flag")
}
package server
/*
Hello! This file is auto-generated from `docs/src/spec.json`.
For development:
In order to update the generated files, edit this file under the location,
add your struct fields, imports, API definitions and whatever you want, and:
1. run [spec](https://github.com/titpetric/spec) in the same folder,
2. run `./_gen.php` in this folder.
You may edit `event.go`, `event.util.go` or `event_test.go` to
implement your API calls, helper functions and tests. The file `event.go`
is only generated the first time, and will not be overwritten if it exists.
*/
import (
"context"
"net/http"
)
// HTTP handlers are a superset of internal APIs
type EventHandlers struct {
Event EventAPI
}
// Internal API interface
type EventAPI interface {
Edit(context.Context, *EventEditRequest) (interface{}, error)
Attach(context.Context, *EventAttachRequest) (interface{}, error)
Remove(context.Context, *EventRemoveRequest) (interface{}, error)
Read(context.Context, *EventReadRequest) (interface{}, error)
Search(context.Context, *EventSearchRequest) (interface{}, error)
Pin(context.Context, *EventPinRequest) (interface{}, error)
Flag(context.Context, *EventFlagRequest) (interface{}, error)
// Authenticate API requests
Authenticator() func(http.Handler) http.Handler
}
// HTTP API interface
type EventHandlersAPI interface {
Edit(http.ResponseWriter, *http.Request)
Attach(http.ResponseWriter, *http.Request)
Remove(http.ResponseWriter, *http.Request)
Read(http.ResponseWriter, *http.Request)
Search(http.ResponseWriter, *http.Request)
Pin(http.ResponseWriter, *http.Request)
Flag(http.ResponseWriter, *http.Request)
}
package server
/*
Hello! This file is auto-generated from `docs/src/spec.json`.
For development:
In order to update the generated files, edit this file under the location,
add your struct fields, imports, API definitions and whatever you want, and:
1. run [spec](https://github.com/titpetric/spec) in the same folder,
2. run `./_gen.php` in this folder.
You may edit `event.go`, `event.util.go` or `event_test.go` to
implement your API calls, helper functions and tests. The file `event.go`
is only generated the first time, and will not be overwritten if it exists.
*/
import (
"net/http"
"github.com/titpetric/factory/resputil"
)
func (eh *EventHandlers) Edit(w http.ResponseWriter, r *http.Request) {
params := EventEditRequest{}.new()
resputil.JSON(w, params.Fill(r), func() (interface{}, error) { return eh.Event.Edit(r.Context(), params) })
}
func (eh *EventHandlers) Attach(w http.ResponseWriter, r *http.Request) {
params := EventAttachRequest{}.new()
resputil.JSON(w, params.Fill(r), func() (interface{}, error) { return eh.Event.Attach(r.Context(), params) })
}
func (eh *EventHandlers) Remove(w http.ResponseWriter, r *http.Request) {
params := EventRemoveRequest{}.new()
resputil.JSON(w, params.Fill(r), func() (interface{}, error) { return eh.Event.Remove(r.Context(), params) })
}
func (eh *EventHandlers) Read(w http.ResponseWriter, r *http.Request) {
params := EventReadRequest{}.new()
resputil.JSON(w, params.Fill(r), func() (interface{}, error) { return eh.Event.Read(r.Context(), params) })
}
func (eh *EventHandlers) Search(w http.ResponseWriter, r *http.Request) {
params := EventSearchRequest{}.new()
resputil.JSON(w, params.Fill(r), func() (interface{}, error) { return eh.Event.Search(r.Context(), params) })
}
func (eh *EventHandlers) Pin(w http.ResponseWriter, r *http.Request) {
params := EventPinRequest{}.new()
resputil.JSON(w, params.Fill(r), func() (interface{}, error) { return eh.Event.Pin(r.Context(), params) })
}
func (eh *EventHandlers) Flag(w http.ResponseWriter, r *http.Request) {
params := EventFlagRequest{}.new()
resputil.JSON(w, params.Fill(r), func() (interface{}, error) { return eh.Event.Flag(r.Context(), params) })
}
package server
/*
Hello! This file is auto-generated from `docs/src/spec.json`.
For development:
In order to update the generated files, edit this file under the location,
add your struct fields, imports, API definitions and whatever you want, and:
1. run [spec](https://github.com/titpetric/spec) in the same folder,
2. run `./_gen.php` in this folder.
You may edit `event.go`, `event.util.go` or `event_test.go` to
implement your API calls, helper functions and tests. The file `event.go`
is only generated the first time, and will not be overwritten if it exists.
*/
import (
"github.com/go-chi/chi"
"net/http"
)
var _ = chi.URLParam
// Event edit request parameters
type EventEditRequest struct {
ID uint64
Channel_id uint64
Contents string
}
func (EventEditRequest) new() *EventEditRequest {
return &EventEditRequest{}
}
func (e *EventEditRequest) Fill(r *http.Request) error {
r.ParseForm()
get := map[string]string{}
post := map[string]string{}
urlQuery := r.URL.Query()
for name, param := range urlQuery {
get[name] = string(param[0])
}
postVars := r.Form
for name, param := range postVars {
post[name] = string(param[0])
}
e.ID = parseUInt64(post["id"])
e.Channel_id = parseUInt64(post["channel_id"])
e.Contents = post["contents"]
return nil
}
var _ RequestFiller = EventEditRequest{}.new()
// Event attach request parameters
type EventAttachRequest struct {
}
func (EventAttachRequest) new() *EventAttachRequest {
return &EventAttachRequest{}
}
func (e *EventAttachRequest) Fill(r *http.Request) error {
r.ParseForm()
get := map[string]string{}
post := map[string]string{}
urlQuery := r.URL.Query()
for name, param := range urlQuery {
get[name] = string(param[0])
}
postVars := r.Form
for name, param := range postVars {
post[name] = string(param[0])
}
return nil
}
var _ RequestFiller = EventAttachRequest{}.new()
// Event remove request parameters
type EventRemoveRequest struct {
ID uint64
}
func (EventRemoveRequest) new() *EventRemoveRequest {
return &EventRemoveRequest{}
}
func (e *EventRemoveRequest) Fill(r *http.Request) error {
r.ParseForm()
get := map[string]string{}
post := map[string]string{}
urlQuery := r.URL.Query()
for name, param := range urlQuery {
get[name] = string(param[0])
}
postVars := r.Form
for name, param := range postVars {
post[name] = string(param[0])
}
e.ID = parseUInt64(get["id"])
return nil
}
var _ RequestFiller = EventRemoveRequest{}.new()
// Event read request parameters
type EventReadRequest struct {
Channel_id uint64
}
func (EventReadRequest) new() *EventReadRequest {
return &EventReadRequest{}
}
func (e *EventReadRequest) Fill(r *http.Request) error {
r.ParseForm()
get := map[string]string{}
post := map[string]string{}
urlQuery := r.URL.Query()
for name, param := range urlQuery {
get[name] = string(param[0])
}
postVars := r.Form
for name, param := range postVars {
post[name] = string(param[0])
}
e.Channel_id = parseUInt64(post["channel_id"])
return nil
}
var _ RequestFiller = EventReadRequest{}.new()
// Event search request parameters
type EventSearchRequest struct {
Query string
Message_type string
}
func (EventSearchRequest) new() *EventSearchRequest {
return &EventSearchRequest{}
}
func (e *EventSearchRequest) Fill(r *http.Request) error {
r.ParseForm()
get := map[string]string{}
post := map[string]string{}
urlQuery := r.URL.Query()
for name, param := range urlQuery {
get[name] = string(param[0])
}
postVars := r.Form
for name, param := range postVars {
post[name] = string(param[0])
}
e.Query = get["query"]
e.Message_type = get["message_type"]
return nil
}
var _ RequestFiller = EventSearchRequest{}.new()
// Event pin request parameters
type EventPinRequest struct {
ID uint64
}
func (EventPinRequest) new() *EventPinRequest {
return &EventPinRequest{}
}
func (e *EventPinRequest) Fill(r *http.Request) error {
r.ParseForm()
get := map[string]string{}
post := map[string]string{}
urlQuery := r.URL.Query()
for name, param := range urlQuery {
get[name] = string(param[0])
}
postVars := r.Form
for name, param := range postVars {
post[name] = string(param[0])
}
e.ID = parseUInt64(post["id"])
return nil
}
var _ RequestFiller = EventPinRequest{}.new()
// Event flag request parameters
type EventFlagRequest struct {
ID uint64
}
func (EventFlagRequest) new() *EventFlagRequest {
return &EventFlagRequest{}
}
func (e *EventFlagRequest) Fill(r *http.Request) error {
r.ParseForm()
get := map[string]string{}
post := map[string]string{}
urlQuery := r.URL.Query()
for name, param := range urlQuery {
get[name] = string(param[0])
}
postVars := r.Form
for name, param := range postVars {
post[name] = string(param[0])
}
e.ID = parseUInt64(post["id"])
return nil
}
var _ RequestFiller = EventFlagRequest{}.new()
......@@ -39,7 +39,7 @@ func (svc channel) Find(ctx context.Context, filter *types.ChannelFilter) ([]*ty
}
func (svc channel) Create(ctx context.Context, mod *types.Channel) (*types.Channel, error) {
// @todo: topic message/log entry
// @todo: topic channelEvent/log entry
// @todo: channel name cmessage/log entry
// @todo: permission check if channel can add channel
......@@ -47,8 +47,8 @@ func (svc channel) Create(ctx context.Context, mod *types.Channel) (*types.Chann
}
func (svc channel) Update(ctx context.Context, mod *types.Channel) (*types.Channel, error) {
// @todo: topic change message/log entry
// @todo: channel name change message/log entry
// @todo: topic change channelEvent/log entry
// @todo: channel name change channelEvent/log entry
// @todo: permission check if current user can edit channel
// @todo: make sure archived & deleted entries can not be edited
// @todo: handle channel movinga
......
package service
import (
"context"
"github.com/crusttech/crust/sam/repository"
"github.com/crusttech/crust/sam/types"
)
type (
message struct {
repository struct {
message messageRepository
reaction messageReactionRepository
attachment messageAttachmentRepository
}
}
messageRepository interface {
FindById(context.Context, uint64) (*types.Message, error)
Find(context.Context, *types.MessageFilter) ([]*types.Message, error)
Create(context.Context, *types.Message) (*types.Message, error)
Update(context.Context, *types.Message) (*types.Message, error)
deleter
}
messageReactionRepository interface {
FindById(context.Context, uint64) (*types.Reaction, error)
Create(context.Context, *types.Reaction) (*types.Reaction, error)
Delete(context.Context, uint64) error
}
messageAttachmentRepository interface {
FindById(context.Context, uint64) (*types.Attachment, error)
Create(context.Context, *types.Attachment) (*types.Attachment, error)
Delete(context.Context, uint64) error
}
)
func Message() *message {
m := &message{}
m.repository.message = repository.Message()
m.repository.reaction = repository.Reaction()
m.repository.attachment = repository.Attachment()
return m
}
func (svc message) Find(ctx context.Context, filter *types.MessageFilter) ([]*types.Message, error) {
// @todo get user from context
var currentUserId uint64 = 0
// @todo verify if current user can access & write to this channel
_ = currentUserId
return svc.repository.message.Find(ctx, filter)
}
func (svc message) Create(ctx context.Context, mod *types.Message) (*types.Message, error) {
// @todo get user from context
var currentUserId uint64 = 0
// @todo verify if current user can access & write to this channel
_ = currentUserId
return svc.repository.message.Create(ctx, mod)
}
func (svc message) Update(ctx context.Context, mod *types.Message) (*types.Message, error) {
// @todo get user from context
var currentUserId uint64 = 0
// @todo verify if current user can access & write to this channel
_ = currentUserId
// @todo load current message
// @todo verify ownership
return svc.repository.message.Update(ctx, mod)
}
func (svc message) Delete(ctx context.Context, id uint64) error {
// @todo get user from context
var currentUserId uint64 = 0
// @todo verify if current user can access & write to this channel
_ = currentUserId
// @todo load current message
// @todo verify ownership
return svc.repository.message.Delete(ctx, id)
}
func (svc message) React(ctx context.Context, messageId uint64, reaction string) (*types.Reaction, error) {
// @todo get user from context
var currentUserId uint64 = 0
// @todo verify if current user can access & write to this channel
var m *types.Message
// @todo validate reaction
r := &types.Reaction{
UserId: currentUserId,
MessageId: messageId,
ChannelId: m.ChannelId,
Reaction: reaction,
}
return svc.repository.reaction.Create(ctx, r)
}
func (svc message) Unreact(ctx context.Context, reactionId uint64) error {
// @todo get user from context
var currentUserId uint64 = 0
// @todo verify if current user can access & write to this channel
_ = currentUserId
// @todo load reaction and verify ownership
return svc.repository.reaction.Delete(ctx, reactionId)
}
func (svc message) Attach(ctx context.Context) (*types.Attachment, error) {
// @todo define func signature
// @todo get user from context
var currentUserId uint64 = 0
// @todo verify if current user can access & write to this channel
_ = currentUserId
return nil, nil
}
func (svc message) Detach(ctx context.Context, attachmentId uint64) error {
// @todo get user from context
var currentUserId uint64 = 0
// @todo verify if current user can access & write to this channel
_ = currentUserId
// @todo verify if current user can remove this attachment
return svc.repository.attachment.Delete(ctx, attachmentId)
}
......@@ -15,18 +15,46 @@ package types
is only generated the first time, and will not be overwritten if it exists.
*/
import (
"encoding/json"
"time"
)
type (
// Messages -
Message struct {
Service string `db:"service"`
Channel string `db:"channel"`
UserName string `db:"user_name"`
UserID uint64 `db:"user_id"`
User *User `db:"user"`
UserAvatar string `db:"user_avatar"`
Message string `db:"message"`
MessageID string `db:"message_id"`
Type MessageType `db:"type"`
ID uint64 `db:"id"`
Type string `db:"type"`
Message string `db:"message"`
UserId uint64 `db:"rel_user"`
ChannelId uint64 `db:"rel_channel"`
ReplyTo uint64 `db:"rel_message"`
UpdatedAt *time.Time `json:",omitempty" db:"updated_at"`
DeletedAt *time.Time `json:",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"`
changed []string
}
// Messages -
Attachment struct {
ID uint64 `db:"id"`
UserId uint64 `db:"rel_user"`
MessageId uint64 `db:"rel_message"`
ChannelId uint64 `db:"rel_channel"`
Attachment json.RawMessage `db:"attachment"`
DeletedAt *time.Time `json:",omitempty" db:"deleted_at"`
changed []string
}
......@@ -37,131 +65,297 @@ func (Message) New() *Message {
return &Message{}
}
// Get the value of Service
func (m *Message) GetService() string {
return m.Service
// New constructs a new instance of Reaction
func (Reaction) New() *Reaction {
return &Reaction{}
}
// Set the value of Service
func (m *Message) SetService(value string) *Message {
if m.Service != value {
m.changed = append(m.changed, "Service")
m.Service = value
// New constructs a new instance of Attachment
func (Attachment) New() *Attachment {
return &Attachment{}
}
// Get the value of ID
func (m *Message) GetID() uint64 {
return m.ID
}
// Set the value of ID
func (m *Message) SetID(value uint64) *Message {
if m.ID != value {
m.changed = append(m.changed, "ID")
m.ID = value
}
return m
}
// Get the value of Channel
func (m *Message) GetChannel() string {
return m.Channel
// Get the value of Type
func (m *Message) GetType() string {
return m.Type
}
// Set the value of Channel
func (m *Message) SetChannel(value string) *Message {
if m.Channel != value {
m.changed = append(m.changed, "Channel")
m.Channel = value
// Set the value of Type
func (m *Message) SetType(value string) *Message {
if m.Type != value {
m.changed = append(m.changed, "Type")
m.Type = value
}
return m
}
// Get the value of UserName
func (m *Message) GetUserName() string {
return m.UserName
// Get the value of Message
func (m *Message) GetMessage() string {
return m.Message
}
// Set the value of UserName
func (m *Message) SetUserName(value string) *Message {
if m.UserName != value {
m.changed = append(m.changed, "UserName")
m.UserName = value
// Set the value of Message
func (m *Message) SetMessage(value string) *Message {
if m.Message != value {
m.changed = append(m.changed, "Message")
m.Message = value
}
return m
}
// Get the value of UserID
func (m *Message) GetUserID() uint64 {
return m.UserID
// Get the value of UserId
func (m *Message) GetUserId() uint64 {
return m.UserId
}
// Set the value of UserID
func (m *Message) SetUserID(value uint64) *Message {
if m.UserID != value {
m.changed = append(m.changed, "UserID")
m.UserID = value
// Set the value of UserId
func (m *Message) SetUserId(value uint64) *Message {
if m.UserId != value {
m.changed = append(m.changed, "UserId")
m.UserId = value
}
return m
}
// Get the value of User
func (m *Message) GetUser() *User {
return m.User
// Get the value of ChannelId
func (m *Message) GetChannelId() uint64 {
return m.ChannelId
}
// Set the value of User
func (m *Message) SetUser(value *User) *Message {
m.changed = append(m.changed, "User")
m.User = value
// Set the value of ChannelId
func (m *Message) SetChannelId(value uint64) *Message {
if m.ChannelId != value {
m.changed = append(m.changed, "ChannelId")
m.ChannelId = value
}
return m
}
// Get the value of UserAvatar
func (m *Message) GetUserAvatar() string {
return m.UserAvatar
// Get the value of ReplyTo
func (m *Message) GetReplyTo() uint64 {
return m.ReplyTo
}
// Set the value of UserAvatar
func (m *Message) SetUserAvatar(value string) *Message {
if m.UserAvatar != value {
m.changed = append(m.changed, "UserAvatar")
m.UserAvatar = value
// Set the value of ReplyTo
func (m *Message) SetReplyTo(value uint64) *Message {
if m.ReplyTo != value {
m.changed = append(m.changed, "ReplyTo")
m.ReplyTo = value
}
return m
}
// Get the value of Message
func (m *Message) GetMessage() string {
return m.Message
// Get the value of UpdatedAt
func (m *Message) GetUpdatedAt() *time.Time {
return m.UpdatedAt
}
// Set the value of Message
func (m *Message) SetMessage(value string) *Message {
if m.Message != value {
m.changed = append(m.changed, "Message")
m.Message = value
// Set the value of UpdatedAt
func (m *Message) SetUpdatedAt(value *time.Time) *Message {
m.changed = append(m.changed, "UpdatedAt")
m.UpdatedAt = value
return m
}
// Get the value of DeletedAt
func (m *Message) GetDeletedAt() *time.Time {
return m.DeletedAt
}
// Set the value of DeletedAt
func (m *Message) SetDeletedAt(value *time.Time) *Message {
m.changed = append(m.changed, "DeletedAt")
m.DeletedAt = value
return m
}
// Changes returns the names of changed fields
func (m *Message) Changes() []string {
return m.changed
}
// Get the value of ID
func (m *Reaction) GetID() uint64 {
return m.ID
}
// Set the value of ID
func (m *Reaction) SetID(value uint64) *Reaction {
if m.ID != value {
m.changed = append(m.changed, "ID")
m.ID = value
}
return m
}
// Get the value of MessageID
func (m *Message) GetMessageID() string {
return m.MessageID
// Get the value of UserId
func (m *Reaction) GetUserId() uint64 {
return m.UserId
}
// Set the value of MessageID
func (m *Message) SetMessageID(value string) *Message {
if m.MessageID != value {
m.changed = append(m.changed, "MessageID")
m.MessageID = value
// Set the value of UserId
func (m *Reaction) SetUserId(value uint64) *Reaction {
if m.UserId != value {
m.changed = append(m.changed, "UserId")
m.UserId = value
}
return m
}
// Get the value of Type
func (m *Message) GetType() MessageType {
return m.Type
// Get the value of MessageId
func (m *Reaction) GetMessageId() uint64 {
return m.MessageId
}
// Set the value of Type
func (m *Message) SetType(value MessageType) *Message {
if m.Type != value {
m.changed = append(m.changed, "Type")
m.Type = value
// Set the value of MessageId
func (m *Reaction) SetMessageId(value uint64) *Reaction {
if m.MessageId != value {
m.changed = append(m.changed, "MessageId")
m.MessageId = value
}
return m
}
// Get the value of ChannelId
func (m *Reaction) GetChannelId() uint64 {
return m.ChannelId
}
// Set the value of ChannelId
func (m *Reaction) SetChannelId(value uint64) *Reaction {
if m.ChannelId != value {
m.changed = append(m.changed, "ChannelId")
m.ChannelId = value
}
return m
}
// Get the value of Reaction
func (m *Reaction) GetReaction() string {
return m.Reaction
}
// Set the value of Reaction
func (m *Reaction) SetReaction(value string) *Reaction {
if m.Reaction != value {
m.changed = append(m.changed, "Reaction")
m.Reaction = value
}
return m
}
// Get the value of DeletedAt
func (m *Reaction) GetDeletedAt() *time.Time {
return m.DeletedAt
}
// Set the value of DeletedAt
func (m *Reaction) SetDeletedAt(value *time.Time) *Reaction {
m.changed = append(m.changed, "DeletedAt")
m.DeletedAt = value
return m
}
// Changes returns the names of changed fields
func (m *Message) Changes() []string {
func (m *Reaction) Changes() []string {
return m.changed
}
// Get the value of ID
func (m *Attachment) GetID() uint64 {
return m.ID
}
// Set the value of ID
func (m *Attachment) SetID(value uint64) *Attachment {
if m.ID != value {
m.changed = append(m.changed, "ID")
m.ID = value
}
return m
}
// Get the value of UserId
func (m *Attachment) GetUserId() uint64 {
return m.UserId
}
// Set the value of UserId
func (m *Attachment) SetUserId(value uint64) *Attachment {
if m.UserId != value {
m.changed = append(m.changed, "UserId")
m.UserId = value
}
return m
}
// Get the value of MessageId
func (m *Attachment) GetMessageId() uint64 {
return m.MessageId
}
// Set the value of MessageId
func (m *Attachment) SetMessageId(value uint64) *Attachment {
if m.MessageId != value {
m.changed = append(m.changed, "MessageId")
m.MessageId = value
}
return m
}
// Get the value of ChannelId
func (m *Attachment) GetChannelId() uint64 {
return m.ChannelId
}
// Set the value of ChannelId
func (m *Attachment) SetChannelId(value uint64) *Attachment {
if m.ChannelId != value {
m.changed = append(m.changed, "ChannelId")
m.ChannelId = value
}
return m
}
// Get the value of Attachment
func (m *Attachment) GetAttachment() json.RawMessage {
return m.Attachment
}
// Set the value of Attachment
func (m *Attachment) SetAttachment(value json.RawMessage) *Attachment {
m.changed = append(m.changed, "Attachment")
m.Attachment = value
return m
}
// Get the value of DeletedAt
func (m *Attachment) GetDeletedAt() *time.Time {
return m.DeletedAt
}
// Set the value of DeletedAt
func (m *Attachment) SetDeletedAt(value *time.Time) *Attachment {
m.changed = append(m.changed, "DeletedAt")
m.DeletedAt = value
return m
}
// Changes returns the names of changed fields
func (m *Attachment) Changes() []string {
return m.changed
}
package types
type (
MessageFilter struct {
Query string
ChannelId string
}
)
package types
// MessageType is a type used to determine the CRUD state of a message.
type MessageType string
const (
// MessageTypeCreate is the message type for message creation.
MessageTypeCreate MessageType = "create"
// MessageTypeUpdate is the message type for message updates.
MessageTypeUpdate = "update"
// MessageTypeDelete is the message type for message deletion.
MessageTypeDelete = "delete"
)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment