Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Media Post Implementation #72

Closed
wants to merge 25 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Version 0.3.0
## Changes
- Implemented the support for media in posts (#36)

# Version 0.2.0
## Changes
- Implemented the support for arbitrary data inside a post (#52, #66)
Expand Down
10 changes: 8 additions & 2 deletions x/posts/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ var (

// Types
NewReaction = types.NewReaction
NewPost = types.NewPost
NewTextPost = types.NewTextPost
NewMediaPost = types.NewMediaPost
ParsePostID = types.ParsePostID
DefaultGenesisState = types.DefaultGenesisState
ValidateGenesis = types.ValidateGenesis

// Msgs
NewMsgCreatePost = types.NewMsgCreatePost
NewMsgCreateTextPost = types.NewMsgCreateTextPost
NewMsgCreateMediaPost = types.NewMsgCreateMediaPost
NewMsgEditPost = types.NewMsgEditPost
NewMsgAddPostReaction = types.NewMsgAddPostReaction
NewMsgRemovePostReaction = types.NewMsgRemovePostReaction
Expand All @@ -48,12 +50,16 @@ type (
PostIDs = types.PostIDs
Post = types.Post
Posts = types.Posts
PostMedia = types.PostMedia
PostMedias = types.PostMedias
Reaction = types.Reaction
Reactions = types.Reactions
GenesisState = types.GenesisState

// Msgs
MsgCreatePost = types.MsgCreatePost
MsgCreateTextPost = types.MsgCreateTextPost
MsgCreateMediaPost = types.MsgCreateMediaPost
MsgEditPost = types.MsgEditPost
MsgAddPostReaction = types.MsgAddPostReaction
MsgRemovePostReaction = types.MsgRemovePostReaction
Expand Down
51 changes: 47 additions & 4 deletions x/posts/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cli
import (
"fmt"
"strconv"
"strings"
"time"

"github.com/cosmos/cosmos-sdk/version"
Expand Down Expand Up @@ -43,9 +44,20 @@ func GetTxCmd(_ string, cdc *codec.Codec) *cobra.Command {
// GetCmdCreatePost is the CLI command for creating a post
func GetCmdCreatePost(cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "create [subspace] [message] [allows-comments]",
Use: "create [subspace] [message] [allows-comments] [[[uri],[provider],[mime-type]]...]",
RiccardoM marked this conversation as resolved.
Show resolved Hide resolved
Short: "Create a new post",
Args: cobra.ExactArgs(3),
Long: fmt.Sprintf(`
leobragaz marked this conversation as resolved.
Show resolved Hide resolved
Create a new post, specifying the subspace, message and whether or not it will allow for comments.
Optional media attachments are also supported.
If you with to add one or more media attachment, you have to specify a uri, a provider and a mime type for each.
You can do so by concatenating them together separated by a comma (,).
Usage examples:

- tx posts create "desmos" "Hello world!" true
- tx posts create "demos" "A post with media" true "https://example.com,text/plain"
- tx posts create "desmos" "A post with multiple medias" false "https://example.com/media1,text/plain" "https://example.com/media2,application/json"
`),
Args: cobra.MinimumNArgs(3),
RunE: func(cmd *cobra.Command, args []string) error {

cliCtx := context.NewCLIContext().WithCodec(cdc)
Expand All @@ -67,11 +79,42 @@ func GetCmdCreatePost(cdc *codec.Codec) *cobra.Command {
return err
}

msg := types.NewMsgCreatePost(args[1], parentID, allowsComments, args[0], map[string]string{}, from, time.Now().UTC())
var msg types.MsgCreatePost
msgTextPost := types.NewMsgCreateTextPost(
args[1],
parentID,
allowsComments,
args[0],
map[string]string{},
from,
time.Now().UTC(),
)

// If there are some medias
if len(args) > 3 {
var medias types.PostMedias
var appended bool
// Read each media and add it to the medias if valid
for i := 3; i < len(args); i++ {
arg := strings.Split(args[i], ",")
if len(arg) == 3 {
media := types.NewPostMedia(arg[0], arg[1], arg[2])
medias, appended = medias.AppendIfMissing(media)
if !appended {
return sdk.ErrUnknownRequest("You can't send the same media two times")
}
} else {
return sdk.ErrUnknownRequest(
"If medias are present, you should specify uri, provider and mime type, " +
"if you are confused, please use the --help flag")
}
}
msg = types.NewMsgCreateMediaPost(msgTextPost, medias)
}

RiccardoM marked this conversation as resolved.
Show resolved Hide resolved
if err = msg.ValidateBasic(); err != nil {
return err
}

return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
},
}
Expand Down
2 changes: 2 additions & 0 deletions x/posts/client/rest/rest.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/desmos-labs/desmos/x/posts/internal/types"
"github.com/gorilla/mux"
)

Expand All @@ -23,6 +24,7 @@ type CreatePostReq struct {
Subspace string `json:"subspace"`
OptionalData map[string]string `json:"optional_data"`
CreationTime time.Time `json:"creation_time"`
Medias types.PostMedias `json:"medias,omitempty"`
}

// AddReactionReq defines the properties of a reaction adding request's body.
Expand Down
9 changes: 8 additions & 1 deletion x/posts/client/rest/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,14 @@ func createPostHandler(cliCtx context.CLIContext) http.HandlerFunc {
return
}

msg := types.NewMsgCreatePost(req.Message, parentID, req.AllowsComments, req.Subspace, req.OptionalData, addr, req.CreationTime)
var msg types.MsgCreatePost
msgTextPost := types.NewMsgCreateTextPost(req.Message, parentID, req.AllowsComments, req.Subspace, req.OptionalData,
addr, req.CreationTime)

if len(req.Medias) != 0 {
msg = types.NewMsgCreateMediaPost(msgTextPost, req.Medias)
}

err = msg.ValidateBasic()
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
Expand Down
2 changes: 1 addition & 1 deletion x/posts/internal/keeper/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func testCodec() *codec.Codec {
var testPostOwner, _ = sdk.AccAddressFromBech32("cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47")
var timeZone, _ = time.LoadLocation("UTC")
var testPostCreationDate = time.Date(2020, 1, 1, 15, 15, 00, 000, timeZone)
var testPost = types.NewPost(
var testPost = types.NewTextPost(
types.PostID(3257),
types.PostID(0),
"Post message",
Expand Down
88 changes: 55 additions & 33 deletions x/posts/internal/keeper/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ import (
func NewHandler(keeper Keeper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
switch msg := msg.(type) {
case types.MsgCreatePost:
return handleMsgCreatePost(ctx, keeper, msg)
case types.MsgEditPost:
return handleMsgEditPost(ctx, keeper, msg)
case types.MsgAddPostReaction:
return handleMsgAddPostReaction(ctx, keeper, msg)
case types.MsgRemovePostReaction:
return handleMsgRemovePostReaction(ctx, keeper, msg)
case types.MsgCreatePost:
return handleMsgCreatePost(ctx, keeper, msg)
default:
errMsg := fmt.Sprintf("Unrecognized Posts message type: %v", msg.Type())
return sdk.ErrUnknownRequest(errMsg).Result()
Expand All @@ -28,47 +28,69 @@ func NewHandler(keeper Keeper) sdk.Handler {

// handleMsgCreatePost handles the creation of a new post
func handleMsgCreatePost(ctx sdk.Context, keeper Keeper, msg types.MsgCreatePost) sdk.Result {
post := types.NewPost(
keeper.GetLastPostID(ctx).Next(),
msg.ParentID,
msg.Message,
msg.AllowsComments,
msg.Subspace,
msg.OptionalData,
msg.CreationDate,
msg.Creator,
)

var post types.Post

if textMsg, ok := msg.(types.MsgCreateTextPost); ok {
post = types.NewTextPost(
keeper.GetLastPostID(ctx).Next(),
textMsg.ParentID,
textMsg.Message,
textMsg.AllowsComments,
textMsg.Subspace,
textMsg.OptionalData,
textMsg.CreationDate,
textMsg.Creator,
)
}

if mediaMsg, ok := msg.(types.MsgCreateMediaPost); ok {
textPost := types.NewTextPost(
leobragaz marked this conversation as resolved.
Show resolved Hide resolved
keeper.GetLastPostID(ctx).Next(),
mediaMsg.MsgCreatePost.ParentID,
mediaMsg.MsgCreatePost.Message,
mediaMsg.MsgCreatePost.AllowsComments,
mediaMsg.MsgCreatePost.Subspace,
mediaMsg.MsgCreatePost.OptionalData,
mediaMsg.MsgCreatePost.CreationDate,
mediaMsg.MsgCreatePost.Creator,
)

post = types.NewMediaPost(textPost, mediaMsg.Medias)
}

// Check for double posting
if _, found := keeper.GetPost(ctx, post.PostID); found {
return sdk.ErrUnknownRequest(fmt.Sprintf("Post with id %s already exists", post.PostID)).Result()
if _, found := keeper.GetPost(ctx, post.GetID()); found {
return sdk.ErrUnknownRequest(fmt.Sprintf("Post with id %s already exists", post.GetID())).Result()
}

// If valid, check the parent post
if post.ParentID.Valid() {
parentPost, found := keeper.GetPost(ctx, post.ParentID)
if post.GetParentID().Valid() {
parentPost, found := keeper.GetPost(ctx, post.GetParentID())
if !found {
return sdk.ErrUnknownRequest(fmt.Sprintf("Parent post with id %s not found", post.ParentID)).Result()
return sdk.ErrUnknownRequest(fmt.Sprintf("Parent post with id %s not found",
post.GetParentID())).Result()
}

if !parentPost.AllowsComments {
return sdk.ErrUnknownRequest(fmt.Sprintf("Post with id %s does not allow comments", parentPost.PostID)).Result()
if !parentPost.CanComment() {
return sdk.ErrUnknownRequest(fmt.Sprintf("Post with id %s does not allow comments",
parentPost.GetID())).Result()
}
}

keeper.SavePost(ctx, post)

createEvent := sdk.NewEvent(
types.EventTypePostCreated,
sdk.NewAttribute(types.AttributeKeyPostID, post.PostID.String()),
sdk.NewAttribute(types.AttributeKeyPostParentID, post.ParentID.String()),
sdk.NewAttribute(types.AttributeKeyCreationTime, post.Created.String()),
sdk.NewAttribute(types.AttributeKeyPostOwner, post.Creator.String()),
sdk.NewAttribute(types.AttributeKeyPostID, post.GetID().String()),
sdk.NewAttribute(types.AttributeKeyPostParentID, post.GetParentID().String()),
sdk.NewAttribute(types.AttributeKeyCreationTime, post.CreationTime().String()),
sdk.NewAttribute(types.AttributeKeyPostOwner, post.Owner().String()),
)
ctx.EventManager().EmitEvent(createEvent)

return sdk.Result{
Data: keeper.Cdc.MustMarshalBinaryLengthPrefixed(post.PostID),
Data: keeper.Cdc.MustMarshalBinaryLengthPrefixed(post.GetID()),
Events: sdk.Events{createEvent},
}
}
Expand All @@ -83,29 +105,29 @@ func handleMsgEditPost(ctx sdk.Context, keeper Keeper, msg types.MsgEditPost) sd
}

// Checks if the the msg sender is the same as the current owner
if !msg.Editor.Equals(existing.Creator) {
if !msg.Editor.Equals(existing.Owner()) {
RiccardoM marked this conversation as resolved.
Show resolved Hide resolved
return sdk.ErrUnauthorized("Incorrect owner").Result()
}

// Check the validity of the current block height respect to the creation date of the post
if existing.Created.After(msg.EditDate) {
if existing.CreationTime().After(msg.EditDate) {
return sdk.ErrUnknownRequest("Edit date cannot be before creation date").Result()
}

// Edit the post
existing.Message = msg.Message
existing.LastEdited = msg.EditDate
existing = existing.SetMessage(msg.Message)
existing = existing.SetEditTime(msg.EditDate)
keeper.SavePost(ctx, existing)

editEvent := sdk.NewEvent(
types.EventTypePostEdited,
sdk.NewAttribute(types.AttributeKeyPostID, existing.PostID.String()),
sdk.NewAttribute(types.AttributeKeyPostEditTime, existing.LastEdited.String()),
sdk.NewAttribute(types.AttributeKeyPostID, existing.GetID().String()),
sdk.NewAttribute(types.AttributeKeyPostEditTime, existing.GetEditTime().String()),
)
ctx.EventManager().EmitEvent(editEvent)

return sdk.Result{
Data: keeper.Cdc.MustMarshalBinaryLengthPrefixed(existing.PostID),
Data: keeper.Cdc.MustMarshalBinaryLengthPrefixed(existing.GetID()),
Events: sdk.Events{editEvent},
}
}
Expand All @@ -120,7 +142,7 @@ func handleMsgAddPostReaction(ctx sdk.Context, keeper Keeper, msg types.MsgAddPo

// Create and store the reaction
reaction := types.NewReaction(msg.Value, msg.User)
if err := keeper.SaveReaction(ctx, post.PostID, reaction); err != nil {
if err := keeper.SaveReaction(ctx, post.GetID(), reaction); err != nil {
return err.Result()
}

Expand Down Expand Up @@ -148,7 +170,7 @@ func handleMsgRemovePostReaction(ctx sdk.Context, keeper Keeper, msg types.MsgRe
}

// Remove the reaction
if err := keeper.RemoveReaction(ctx, post.PostID, msg.User, msg.Reaction); err != nil {
if err := keeper.RemoveReaction(ctx, post.GetID(), msg.User, msg.Reaction); err != nil {
return err.Result()
}

Expand Down
Loading