Attachments & Quotes に対応 - #3097
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds optional ChangesAttachments and quotes inclusion feature
Server wiring field selection update
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Search as esEngine
participant Manager as message.Manager
participant Parse as utils/message
participant Repo as repository
Search->>Manager: GetIn(ctx, ids)
Manager->>Repo: GetMessages(ids)
Repo-->>Manager: base messages
Manager->>Parse: Parse(mm.Text)
Parse-->>Manager: attachment IDs, quote IDs
Manager->>Repo: GetFileMeta(attachment IDs)
Repo-->>Manager: FileMeta
Manager->>Repo: GetMessages(quote IDs)
Repo-->>Manager: quoted messages
Manager-->>Search: []Detailed
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (2)
service/message/model.go (1)
25-38: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winReduce duplication by embedding
Message.
MessageNewrepeats every method already declared inMessage. EmbeddingMessageand only adding the two new getters keeps both interfaces in sync and removes the duplication.♻️ Proposed refactor
type MessageNew interface { - GetID() uuid.UUID - GetUserID() uuid.UUID - GetChannelID() uuid.UUID - GetText() string - GetCreatedAt() time.Time - GetUpdatedAt() time.Time - GetStamps() []model.MessageStamp - GetPin() *model.Pin + Message GetAttachments() []*model.FileMeta GetQuotes() []*model.Message - - json.Marshaler }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@service/message/model.go` around lines 25 - 38, Reduce duplication in MessageNew by embedding Message instead of repeating its getters. Update the MessageNew interface in Message to include Message and keep only the additional methods needed by the new type, while preserving json.Marshaler so both interfaces stay in sync automatically.service/search/es_result.go (1)
60-60: 🚀 Performance & Scalability | 🔵 Trivial | 🏗️ Heavy liftUnconditional
true, truetriggers per-message N+1 fetches on every search request.
GetInloops over each returned message to fetch attachments (oneGetFileMetacall per attachment) and separately fetches quotes, per the upstream implementation. Hardcoding both flags totruehere means every search response now pays this cost regardless of whether the caller/UI needs attachments or quotes, which could noticeably slow down search under load with many hits.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@service/search/es_result.go` at line 60, The search path in e.searchMessages is always calling mm.GetIn with both attachment and quote expansion enabled, which forces expensive per-message lookups on every request. Update the GetIn call to pass flags based on the actual search response needs in es_result.go, and only enable attachments/quotes when the caller explicitly requires them. Use the e.mm.GetIn invocation and the surrounding search result assembly to keep the behavior unchanged for clients that don’t need those fields.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@model/messages.go`:
- Around line 26-42: MessageNew is duplicating the full persistence schema from
Message, including GORM tags and constraint names, which can drift and collide
during migration. Refactor MessageNew in messages.go to embed Message instead of
redefining all base fields, and keep only the extra association fields unique to
MessageNew. Update any construction sites that currently populate MessageNew
field-by-field to initialize the embedded Message struct directly.
In `@service/message/manager_impl.go`:
- Around line 77-83: The attachment-loading loop in manager_impl.go currently
stops on the first GetFileMeta error and silently drops the rest of pRa. Update
the loop in the attachment fetch logic to log the GetFileMeta failure with
context, then continue iterating so later valid attachments are still appended
to aR instead of being skipped.
- Around line 73-88: The `ManagerImpl` message-mapping logic is doing
per-message DB lookups in `utils.Map`, causing an N+1 pattern for attachments
and quotes. Refactor the `messageParse.Parse` / `GetFileMeta` / `GetMessages`
flow to first collect all attachment IDs and citation IDs across the batch,
fetch them once with batched repository calls, and then map the returned
`FileMeta` and message quote data back onto each `MessageNew`.
- Around line 73-88: The attachment and quote lookup work in MessageNewList is
running unconditionally, so the ia and iq flags are not respected. Update the
logic in manager_impl.go within MessageNewList to gate the messageParse.Parse,
GetFileMeta loop, and GetMessages quote fetch behind the corresponding ia/iq
checks, using the existing MessagesQuery flags already passed into
repository.MessagesQuery. Keep the current behavior only when the flag is
enabled, and skip parsing/DB lookups entirely when ia or iq is false.
- Around line 84-88: The quote lookup in manager_impl.go currently returns a nil
MessageNew from the mapping path, which can later panic and hides the failure.
Update the GetIn flow in the method that uses parseResult.Citation and
m.R.GetMessages so the error is propagated instead of returning nil, and make
GetIn surface that error to callers rather than always returning ret, nil. Use
the existing MessageNew-related path and utils.Map/GetIn logic to locate the
fix.
In `@service/message/model_impl.go`:
- Around line 237-263: `messageNew.MarshalJSON` is still serializing the base
message shape and omits attachments/quotes entirely. Update the `obj` payload
and the `v` construction in `messageNew.MarshalJSON` to include fields populated
from `GetAttachments()` and `GetQuotes()` alongside `GetStamps()`, using the
existing `messageNew`/`MarshalJSON` symbols so the JSON response actually
carries the extra data fetched by `GetIn`.
In `@service/search/es_result.go`:
- Line 60: The search result parsing path is creating a new background context
instead of using the request-scoped one. Update parseResultFromResponse to
accept context.Context as its first parameter, pass that context through from
its callers, and use it when calling e.mm.GetIn so cancellation, deadlines, and
tracing flow through the message/attachment/quote fetches. Ensure any other I/O
helpers in this path continue forwarding the same context rather than
fabricating a new one.
---
Nitpick comments:
In `@service/message/model.go`:
- Around line 25-38: Reduce duplication in MessageNew by embedding Message
instead of repeating its getters. Update the MessageNew interface in Message to
include Message and keep only the additional methods needed by the new type,
while preserving json.Marshaler so both interfaces stay in sync automatically.
In `@service/search/es_result.go`:
- Line 60: The search path in e.searchMessages is always calling mm.GetIn with
both attachment and quote expansion enabled, which forces expensive per-message
lookups on every request. Update the GetIn call to pass flags based on the
actual search response needs in es_result.go, and only enable attachments/quotes
when the caller explicitly requires them. Use the e.mm.GetIn invocation and the
surrounding search result assembly to keep the behavior unchanged for clients
that don’t need those fields.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: cdeb03cd-d854-410b-9de9-f728339e9ec1
📒 Files selected for processing (8)
docs/v3-api.yamlmodel/messages.gorepository/message.goservice/message/manager.goservice/message/manager_impl.goservice/message/model.goservice/message/model_impl.goservice/search/es_result.go
| for i := 0; i < len(pRa); i++ { | ||
| attachment, err := m.R.GetFileMeta(ctx, pRa[i]) | ||
| if err != nil { | ||
| break | ||
| } | ||
| aR = append(aR, attachment) | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Attachment fetch errors are silently swallowed.
On the first GetFileMeta error, the loop breaks and any remaining attachments in pRa are silently dropped — no logging, no error surfaced to caller. Consider logging the error and using continue instead of break so a single missing file doesn't drop all subsequent valid attachments.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@service/message/manager_impl.go` around lines 77 - 83, The attachment-loading
loop in manager_impl.go currently stops on the first GetFileMeta error and
silently drops the rest of pRa. Update the loop in the attachment fetch logic to
log the GetFileMeta failure with context, then continue iterating so later valid
attachments are still appended to aR instead of being skipped.
There was a problem hiding this comment.
♻️ Duplicate comments (1)
service/search/es_result.go (1)
47-47: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winStill fabricating
context.Background()instead of propagating caller's context.
parseResultFromResponsedoesn't acceptcontext.Contextand callse.mm.GetIn(which performs message, attachment, and quote I/O) withcontext.Background()at Line 60, dropping cancellation/deadline/tracing for the request.As per path instructions, "Functions performing I/O or network requests must accept
context.Contextas their first parameter" and "The receivedcontextmust be passed down to all subsequent calls."Also applies to: 60-60
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@service/search/es_result.go` at line 47, The result parsing path is dropping caller context by using a fabricated background context for I/O inside parseResultFromResponse. Update esEngine.parseResultFromResponse to accept context.Context as the first parameter, then pass that context through to e.mm.GetIn (and any related message/attachment/quote lookups) instead of context.Background(), and ensure the caller in the search flow forwards its existing context into this method.Source: Path instructions
🧹 Nitpick comments (1)
service/message/model.go (1)
25-38: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueReduce duplication by embedding
Message.
MessageWithAttachmentsAndQuotesrepeats every getter fromMessageverbatim. EmbeddingMessageand adding only the new methods avoids drift ifMessagechanges.♻️ Proposed refactor
type MessageWithAttachmentsAndQuotes interface { - GetID() uuid.UUID - GetUserID() uuid.UUID - GetChannelID() uuid.UUID - GetText() string - GetCreatedAt() time.Time - GetUpdatedAt() time.Time - GetStamps() []model.MessageStamp - GetPin() *model.Pin + Message GetAttachments() []*model.FileMeta GetQuotes() []*model.Message - - json.Marshaler }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@service/message/model.go` around lines 25 - 38, Refactor MessageWithAttachmentsAndQuotes to embed Message instead of repeating all of its getter methods, and keep only the additional attachment/quote accessors plus json.Marshaler. Update any implementations or references that rely on the duplicated Message methods so they still satisfy the interface via the embedded Message, preventing drift when Message changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Duplicate comments:
In `@service/search/es_result.go`:
- Line 47: The result parsing path is dropping caller context by using a
fabricated background context for I/O inside parseResultFromResponse. Update
esEngine.parseResultFromResponse to accept context.Context as the first
parameter, then pass that context through to e.mm.GetIn (and any related
message/attachment/quote lookups) instead of context.Background(), and ensure
the caller in the search flow forwards its existing context into this method.
---
Nitpick comments:
In `@service/message/model.go`:
- Around line 25-38: Refactor MessageWithAttachmentsAndQuotes to embed Message
instead of repeating all of its getter methods, and keep only the additional
attachment/quote accessors plus json.Marshaler. Update any implementations or
references that rely on the duplicated Message methods so they still satisfy the
interface via the embedded Message, preventing drift when Message changes.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: d434a2f5-f198-4e4f-9488-e5642d7d656c
📒 Files selected for processing (4)
service/message/manager.goservice/message/manager_impl.goservice/message/model.goservice/search/es_result.go
🚧 Files skipped from review as they are similar to previous changes (2)
- service/message/manager.go
- service/message/manager_impl.go
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
service/message/manager_impl.go (1)
68-88: 🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick winincludeAttachments/includeQuotes are not wired up
router/v3/utils.go:107-117doesn’t bind these query params, sorouter/v3/messages.godrops them before reachingMessageManager.service/message/manager_impl.go:68-88then always parses text and fetches attachments/quotes, so the documented flags can’t skip the extra DB work or change the response shape.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@service/message/manager_impl.go` around lines 68 - 88, The includeAttachments and includeQuotes flags are not being propagated into MessageManager, so GetIn always does attachment and quote lookups. Update the router query binding and the message retrieval flow so these params are carried through from the request into manager.GetIn, then use them inside manager_impl.go’s GetIn method to conditionally skip Parse/GetFileMeta/GetMessages work and omit those fields from Detailed when disabled.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@cmd/serve.go`:
- Around line 201-207: The Wire helper providers provideCancelFunc and
provideShutdownChan are currently unused, while the startup path still creates
the cancel context and shutdown channel inline. Either remove these helpers from
cmd/serve.go if they are not intended to be part of dependency injection, or add
them to the Wire setup in serve_wire/wire_gen so Serve startup actually uses
them instead of the inline setup. Make the change consistently around the serve
startup wiring and any references to context.WithCancel and make(chan struct{}).
---
Outside diff comments:
In `@service/message/manager_impl.go`:
- Around line 68-88: The includeAttachments and includeQuotes flags are not
being propagated into MessageManager, so GetIn always does attachment and quote
lookups. Update the router query binding and the message retrieval flow so these
params are carried through from the request into manager.GetIn, then use them
inside manager_impl.go’s GetIn method to conditionally skip
Parse/GetFileMeta/GetMessages work and omit those fields from Detailed when
disabled.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 64387577-0f8e-4574-a68b-e2e0d24f0ec6
📒 Files selected for processing (6)
cmd/serve.gocmd/serve_wire.goservice/message/manager.goservice/message/manager_impl.goservice/message/model.goservice/search/es_result.go
There was a problem hiding this comment.
🧹 Nitpick comments (1)
docs/v3-api.yaml (1)
4782-4848: 📐 Maintainability & Code Quality | 🔵 TrivialConsider
allOfcomposition to reduce schema duplication.
MessageNewduplicates every property ofMessageverbatim plusattachments/quotes. UsingallOf: [$ref Message, extra properties]would avoid the two schemas drifting out of sync on future edits.♻️ Example refactor using allOf
- MessageNew: - title: MessageNew - type: object - description: メッセージ 詳細 - properties: - id: ... - userId: ... - ... (full duplication) - required: - - id - - userId - ... + MessageNew: + title: MessageNew + description: メッセージ 詳細 + allOf: + - $ref: "`#/components/schemas/Message`" + - type: object + properties: + attachments: + type: array + description: 添付ファイルの配列 + items: + $ref: "`#/components/schemas/FileInfo`" + quotes: + type: array + description: 引用メッセージの配列 + items: + $ref: "`#/components/schemas/Message`" + required: + - attachments + - quotes🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/v3-api.yaml` around lines 4782 - 4848, MessageNew currently duplicates the full Message schema, which can drift out of sync over time. Refactor the MessageNew schema in the v3 API definition to compose from Message using allOf, then add only the extra MessageNew-specific properties such as attachments and quotes. Keep the existing MessageNew identifier and ensure any required fields remain correct after composition.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@docs/v3-api.yaml`:
- Around line 4782-4848: MessageNew currently duplicates the full Message
schema, which can drift out of sync over time. Refactor the MessageNew schema in
the v3 API definition to compose from Message using allOf, then add only the
extra MessageNew-specific properties such as attachments and quotes. Keep the
existing MessageNew identifier and ensure any required fields remain correct
after composition.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: eacc990b-3e7f-4ead-bb55-07fb83faaf3d
📒 Files selected for processing (2)
docs/swagger.yamldocs/v3-api.yaml
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
service/message/timeline_impl.go (1)
93-110: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick winDon’t serialize raw persistence messages as quote payloads.
Quotesis emitted as[]*model.Message, unlike the top-level DTO mapping above. Sincemodel.Messagelacks the lower-camel/content JSON mapping and may include preloaded associations, quoted messages can leak internal shape or extra nested data. Map quotes through the same API DTO/wrapper before assigning them.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@service/message/timeline_impl.go` around lines 93 - 110, The Quotes field in timeline_impl.go is currently assigned raw []*model.Message values, which can leak persistence shape and nested associations. Update the objectWithPreload mapping in the preloaded branch to convert each quote through the same API DTO/wrapper used for top-level messages, so Quotes contains serialized message payloads instead of model.Message instances.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@service/message/manager_impl.go`:
- Around line 130-133: The MessageNew building path in manager_impl.go is always
doing full enrichment even when query.DisablePreload is true, but
timelineMessage.MarshalJSON will skip using attachments/quotes in that case.
Update the loop around buildMessageNew so it takes preload/include flags or
branches early to construct a lightweight MessageNew when preloading is
disabled, avoiding text parsing and attachment/quote DB lookups for those
records.
- Around line 73-80: Apply the caller’s visibility constraints before resolving
any referenced files or messages in the message manager flow. Update the helper
in manager_impl.go so the file metadata lookup via GetFileMeta and the quote
lookup via GetMessages both validate access using the caller’s timeline/scope,
not just the raw IDs from parseResult.Citation. Thread the caller context or
visibility filter through this path, or explicitly authorize each resolved
reference before appending attachments or quotes.
---
Outside diff comments:
In `@service/message/timeline_impl.go`:
- Around line 93-110: The Quotes field in timeline_impl.go is currently assigned
raw []*model.Message values, which can leak persistence shape and nested
associations. Update the objectWithPreload mapping in the preloaded branch to
convert each quote through the same API DTO/wrapper used for top-level messages,
so Quotes contains serialized message payloads instead of model.Message
instances.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 6638078f-1aa1-4bf0-86de-1852c9caad01
📒 Files selected for processing (3)
service/message/manager_impl.goservice/message/model_impl.goservice/message/timeline_impl.go
| - pinned | ||
| - stamps | ||
| - threadId | ||
| DetailedMessage: |
There was a problem hiding this comment.
OpenAPI では allOf を利用するとプロパティのマージができるので、Message を利用できそう
There was a problem hiding this comment.
gogen が場合によって allOf のマージを完全に処理できない場合があるようなので,見送ります
| sync.RWMutex | ||
| } | ||
|
|
||
| type DetailedMessage struct { |
There was a problem hiding this comment.
ここは埋め込みによるメリットがあまりないので,そのまま使用します
There was a problem hiding this comment.
埋め込みを使ったらその下のメソッドの重複も避けられない? (そんなことない?)
cp-20
left a comment
There was a problem hiding this comment.
またいくつかコメントしました 基本的にコードは後から読まれることを意識して書いて欲しくて、そのためにいくつか意識すべき原則的なことを書いておきます
- 変数名とか関数名とか構造体名とかは分かりやすいものにする
- (慣習としてそうなっていない限り) 1文字の変数などは避ける
- 今回の例でいえば
aという名前の変数を後から見た時に、何なのかが分かりますか?
- コードの (本質的な) 重複は避ける
- 後からコードを編集したときに実装漏れなどが生じる可能性があるため
- 今回の例でいえば
Messageに新しいプロパティが付け加えられたときに、DetailedMessageと被っている部分を忘れずに編集することができるでしょうか? (できないことはないんだけど、忘れる可能性がある)
cp-20
left a comment
There was a problem hiding this comment.
そろそろコードも整ってきたので、動作的な部分にも踏み込んでコメントしてみました 特に権限チェックの不備は脆弱性になり得るので確認してみて欲しいです
| IncludeAttachments bool `query:"include-attachments"` | ||
| IncludeQuotes bool `query:"include-quotes"` |
There was a problem hiding this comment.
MessageQuery にはちゃんと値が入るけど、これが TimelineQuery にコピーされない (手元で動かしてみればクエリパラメータによって動作が変わらないことが分かるはず)
| parseResult := messageParser.Parse(mm.Text) | ||
| attachmentsResult := []*model.FileMeta{} | ||
| for _, fid := range parseResult.Attachments { | ||
| attachment, err := m.R.GetFileMeta(ctx, fid) |
There was a problem hiding this comment.
ファイルを取得する権限があるかをチェックしていない (e.g. DMに貼られた画像)
| if includeQuotes { | ||
| citationResult = []*model.QuotedMessage{} | ||
| var err error | ||
| quotes, _, err := m.R.GetMessages(ctx, repository.MessagesQuery{IDIn: optional.From((parseResult.Citation))}) |
There was a problem hiding this comment.
引用を取得する権限があるかをチェックしていない (e.g. DMのメッセージ)
& 引用の順番がメッセージの登場順ではなくなる (GetMessages は内部で created_at の昇順/降順でソートする)
| Pinned bool `json:"pinned"` | ||
| Stamps []model.MessageStamp `json:"stamps"` | ||
| ThreadID optional.Of[uuid.UUID] `json:"threadId"` // TODO | ||
| Attachments []*model.FileMeta `json:"attachments"` |
There was a problem hiding this comment.
FileMeta は DB 用の構造体なので、そのまま JSON にするとキー名がおかしくなるし、外部に露出させる用途ではない 実際に自分で API を叩いてみると OpenAPI スキーマとは違うスキーマ (キー名) で帰ってくることがわかるはず
この場合には FileInfo 構造体などを使うのが良さそう
| attachmentsResult = []*model.FileMeta{} | ||
| for _, fid := range parseResult.Attachments { | ||
| attachment, err := m.R.GetFileMeta(ctx, fid) | ||
| if err != nil { |
There was a problem hiding this comment.
エラーの場合にどうするべきかの挙動は難しいけど、いったん一つでもエラーが起きたら全体をエラーにするという設計で組んでみて欲しい (今は握りつぶしちゃってる)
| type DetailedMessage interface { | ||
| Message | ||
| GetAttachments() []*model.FileMeta | ||
| GetQuotes() []model.QuotedMessage |
Attachments & Quotes に対応
Summary by CodeRabbit
GET /channels/{channelId}/messageswith optionalinclude-attachmentsandinclude-quotesquery parameters.attachmentsandquotes.MessageNewschema) to match the expanded response format.