thread-master/backend/internal/model/threads_account/usecase/api_smoke.go

169 lines
5.6 KiB
Go
Raw Normal View History

package usecase
import (
"context"
"fmt"
"strings"
app "haixun-backend/internal/library/errors"
"haixun-backend/internal/library/errors/code"
libthreads "haixun-backend/internal/library/threadsapi"
domusecase "haixun-backend/internal/model/threads_account/domain/usecase"
)
func (u *threadsAccountUseCase) RunAPISmokeTest(
ctx context.Context,
tenantID, ownerUID, accountID string,
) ([]domusecase.APISmokeTestItem, error) {
account, err := u.assertOwned(ctx, tenantID, ownerUID, accountID)
if err != nil {
return nil, err
}
token, err := u.ResolveAccountAPIAccessToken(ctx, tenantID, ownerUID, accountID)
if err != nil {
return nil, err
}
client := libthreads.NewClient(token)
if !client.Enabled() {
return nil, app.For(code.ThreadsAccount).InputMissingRequired("Threads API token 無效")
}
var out []domusecase.APISmokeTestItem
add := func(scope, name, status, message string, count int) {
out = append(out, domusecase.APISmokeTestItem{
Scope: scope, Name: name, Status: status, Message: message, Count: count,
})
}
run := func(scope, name string, fn func() (int, string, error)) {
count, detail, err := fn()
if err != nil {
msg := err.Error()
if libthreads.IsPermissionError(err) {
msg += "(請確認 Meta App 已授權此 scope且帳號在測試人員名單"
}
add(scope, name, "error", msg, 0)
return
}
if detail != "" {
add(scope, name, "ok", detail, count)
return
}
add(scope, name, "ok", fmt.Sprintf("成功(%d 筆)", count), count)
}
profile, profileErr := libthreads.GetMeProfile(ctx, token)
userID := strings.TrimSpace(account.ThreadsUserID)
username := strings.TrimSpace(account.Username)
if profileErr == nil && profile != nil {
if userID == "" {
userID = profile.ID.String()
}
if username == "" {
username = profile.Username
}
}
run("threads_basic", "GET /me 個人資料", func() (int, string, error) {
if profileErr != nil {
return 0, "", profileErr
}
return 1, "user_id=" + profile.ID.String() + " @" + profile.Username, nil
})
if userID == "" {
add("threads_basic", "GET /{user-id}/threads 貼文列表", "error", "缺少 threads_user_id請重新 OAuth", 0)
} else {
var sampleMediaID string
run("threads_basic", "GET /{user-id}/threads 貼文列表", func() (int, string, error) {
items, err := client.UserThreads(ctx, userID, 5)
if err != nil {
return 0, "", err
}
if len(items) > 0 {
sampleMediaID = items[0].ID
}
return len(items), "", nil
})
if sampleMediaID != "" {
run("threads_manage_insights", "GET /{media-id}/insights 貼文互動", func() (int, string, error) {
stats, err := client.GetMediaStats(ctx, sampleMediaID)
if err != nil {
return 0, "", err
}
return 1, fmt.Sprintf("likes=%d replies=%d views=%d", stats.LikeCount, stats.ReplyCount, stats.Views), nil
})
run("threads_read_replies", "GET /{media-id}/replies 留言列表", func() (int, string, error) {
items, err := client.MediaReplies(ctx, sampleMediaID, 5)
if err != nil {
return 0, "", err
}
return len(items), "", nil
})
} else {
add("threads_manage_insights", "GET /{media-id}/insights 貼文互動", "skip", "帳號尚無貼文,略過", 0)
add("threads_read_replies", "GET /{media-id}/replies 留言列表", "skip", "帳號尚無貼文,略過", 0)
}
}
run("threads_keyword_search", "GET /keyword_search 關鍵字搜尋", func() (int, string, error) {
items, err := client.KeywordSearch(ctx, libthreads.KeywordSearchOptions{
Query: "threads", Limit: 5, SearchType: "TOP",
})
if err != nil {
return 0, "", err
}
return len(items), "", nil
})
if username != "" {
run("threads_profile_discovery", "GET /profile_lookup 帳號查詢", func() (int, string, error) {
res, err := client.ProfileLookup(ctx, username)
if err != nil {
return 0, "", err
}
return 1, "@" + res.Username + " name=" + res.Name, nil
})
} else {
add("threads_profile_discovery", "GET /profile_lookup 帳號查詢", "skip", "缺少 username略過", 0)
}
if userID != "" {
run("threads_manage_mentions", "GET /{user-id}/mentions 提及", func() (int, string, error) {
items, err := client.UserMentions(ctx, userID, 5)
if err != nil {
return 0, "", err
}
return len(items), "", nil
})
run("threads_manage_insights", "GET /{user-id}/threads_insights 洞察", func() (int, string, error) {
items, err := client.UserInsights(ctx, userID, nil)
if err != nil {
return 0, "", err
}
return len(items), fmt.Sprintf("%d 項指標", len(items)), nil
})
} else {
add("threads_manage_mentions", "GET /{user-id}/mentions 提及", "skip", "缺少 threads_user_id", 0)
add("threads_manage_insights", "GET /{user-id}/threads_insights 洞察", "skip", "缺少 threads_user_id", 0)
}
run("threads_location_tagging", "GET /location_search 地點搜尋", func() (int, string, error) {
items, err := client.LocationSearch(ctx, "Taipei", 5)
if err != nil {
return 0, "", err
}
return len(items), "", nil
})
add("threads_content_publish", "POST 發文 / 回覆", "manual",
"請用產品 APIPOST /personas/:id/copy-drafts/:draftId/publish 或 outreach-drafts/publish", 0)
add("threads_delete", "DELETE /{media-id} 刪文", "manual",
"會真的刪除貼文,請在 Threads 後台或自行呼叫 DeleteMedia 測試", 0)
add("threads_manage_replies", "POST 隱藏留言", "manual",
"會改變留言狀態,請用 HideReply(reply_id) 手動測試", 0)
add("threads_share_to_instagram", "POST 同步到 IG", "manual",
"需已連結 IG 且會真的 crosspost請用 ShareToInstagram(media_id) 手動測試", 0)
return out, nil
}