Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions repository/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ type UpdateChannelArgs struct {
Parent optional.Of[uuid.UUID]
}

// UpdateChannelArgs スレッド情報更新引数
type UpdateThreadArgs struct {
UpdaterID uuid.UUID
Name optional.Of[string]
Visibility optional.Of[bool]
}

// ChannelEventsQuery GetChannelEvents用クエリ
type ChannelEventsQuery struct {
Channel uuid.UUID
Expand Down
39 changes: 39 additions & 0 deletions router/v3/channels.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,45 @@ func (h *Handlers) CreateThreads(c echo.Context) error {
return c.JSON(http.StatusCreated, formatChannel(ch, make([]uuid.UUID, 0)))
}

// PatchThreadChannel Patch /threads/{channelId}
type PatchThreadRequest struct {
Name optional.Of[string] `json:"name"`
Archived optional.Of[bool] `json:"archived"`
}

func (h *Handlers) EditThread(c echo.Context) error {
ctx := c.Request().Context()
userID := getRequestUserID(c)
channelID := getParamAsUUID(c, consts.ParamChannelID)

var req PatchThreadRequest
if err := bindAndValidate(c, &req); err != nil {
return err
}
visibility := optional.Of[bool]{
V: !req.Archived.V,
Valid: req.Archived.Valid,
}
args := repository.UpdateThreadArgs{
UpdaterID: userID,
Name: req.Name,
Visibility: visibility,
}
if err := h.ChannelManager.UpdateThread(ctx, channelID, args); err != nil {
switch err {
case channel.ErrInvalidChannelName:
return herror.BadRequest("invalid channel name")
case channel.ErrChannelNameConflicts:
return herror.Conflict("channel name conflicts")
case channel.ErrInvalidChannelType:
return herror.Conflict("invalid channel type")
default:
return herror.InternalServerError(err)
}
Comment thread
kurao2924 marked this conversation as resolved.
}
return c.NoContent(http.StatusNoContent)
}

// GetChannel GET /channels/:channelID
func (h *Handlers) GetChannel(c echo.Context) error {
ctx := c.Request().Context()
Expand Down
1 change: 1 addition & 0 deletions router/v3/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ func (h *Handlers) Setup(e *echo.Group) {
apiThreads := api.Group("/threads")
{
apiThreads.POST("", h.CreateThreads, requires(permission.CreateChannel))
apiThreads.PATCH("/:channelID", h.EditThread, requires(permission.GetChannel))
}
apiMessages := api.Group("/messages")
{
Expand Down
2 changes: 2 additions & 0 deletions service/channel/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ var (
ErrChannelNameConflicts = errors.New("channel name conflicts")
ErrInvalidChannelName = errors.New("invalid channel name")
ErrInvalidChannelPath = errors.New("invalid channel path")
ErrInvalidChannelType = errors.New("invalid channel type")
ErrInvalidParentChannel = errors.New("invalid parent channel")
ErrTooDeepChannel = errors.New("too deep channel")
ErrChannelArchived = errors.New("channel archived")
Expand All @@ -42,6 +43,7 @@ type Manager interface {
GetDMChannelMapping(ctx context.Context, userID uuid.UUID) (map[uuid.UUID]uuid.UUID, error)

CreateThreadChannel(ctx context.Context, name string, parent, creatorID uuid.UUID) (*model.Channel, error)
UpdateThread(ctx context.Context, id uuid.UUID, args repository.UpdateThreadArgs) error

IsChannelAccessibleToUser(ctx context.Context, userID, channelID uuid.UUID) (bool, error)
IsPublicChannel(ctx context.Context, id uuid.UUID) bool
Expand Down
63 changes: 63 additions & 0 deletions service/channel/manager_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ func (m *managerImpl) UpdateChannel(ctx context.Context, id uuid.UUID, args repo
return ErrChannelNotFound
}

if ch.Type != model.ChannelTypePublic && ch.Type != model.ChannelTypeDM {
return ErrInvalidChannelType
}

// トピックが同じだった場合、トピックの引数自体を無効化
if ch.Topic == args.Topic.V {
args.Topic = optional.New("", false)
Expand Down Expand Up @@ -253,6 +257,65 @@ func (m *managerImpl) UpdateChannel(ctx context.Context, id uuid.UUID, args repo
return nil
}

func (m *managerImpl) UpdateThread(ctx context.Context, id uuid.UUID, args repository.UpdateThreadArgs) error {
ch, err := m.GetChannel(ctx, id)
if err != nil {
return ErrChannelNotFound
}

if ch.Type != model.ChannelTypeThread {
Comment thread
ACF37 marked this conversation as resolved.
return ErrInvalidChannelType
}

_, err = m.GetChannel(ctx, ch.ParentID)
Comment thread
ACF37 marked this conversation as resolved.
if err != nil {
return fmt.Errorf("failed to UpdateThread: %w", err)
}

m.T.Lock()
defer m.T.Unlock()

eventRecords := map[model.ChannelEventType]model.ChannelEventDetail{}
if args.Visibility.Valid && ch.IsVisible != args.Visibility.V {
eventRecords[model.ChannelEventVisibilityChanged] = model.ChannelEventDetail{
"userId": args.UpdaterID,
"visibility": args.Visibility.V,
}
}

if args.Name.Valid {
// スレッド名重複を確認
{
if m.T.isChildPresent(args.Name.V, ch.ParentID) {
return ErrChannelNameConflicts
}
}

eventRecords[model.ChannelEventNameChanged] = model.ChannelEventDetail{
"userId": args.UpdaterID,
"before": ch.Name,
"after": args.Name.V,
}
}

ch, err = m.R.UpdateChannel(ctx, id, repository.UpdateChannelArgs{
UpdaterID: args.UpdaterID,
Name: args.Name,
Visibility: args.Visibility,
})
if err != nil {
return fmt.Errorf("failed to UpdateChannel: %w", err)
}

m.T.updateSingle(id, ch)

updated := time.Now()
for eventType, detail := range eventRecords {
m.recordChannelEvent(id, eventType, detail, updated)
}
return nil
}

func (m *managerImpl) ArchiveChannel(ctx context.Context, id uuid.UUID, updaterID uuid.UUID) error {
ch, err := m.GetChannel(ctx, id)
if err != nil {
Expand Down
14 changes: 14 additions & 0 deletions service/channel/mock_channel/mock_manager.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.