Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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.EditChannel))
Comment thread
ACF37 marked this conversation as resolved.
Outdated
}
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
62 changes: 62 additions & 0 deletions service/channel/manager_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,68 @@ 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
}

m.T.Lock()
defer m.T.Unlock()
_, 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)
}

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
}
}

// スレッド名検証
if !validator.ChannelRegex.MatchString(args.Name.V) {
Comment thread
ACF37 marked this conversation as resolved.
Outdated
return ErrInvalidChannelName
}
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.