-
Notifications
You must be signed in to change notification settings - Fork 9
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
Update users channels and messages via API #102
Conversation
そう言えば messages に関しては並列化の余地がありますが
ので保留にしました。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DownloadEntitiesToFile は Go 的な書き方からはほど遠い。APIRequest にどのような値を設定すべきかそれを呼ぶ人が意識しなければいけない(さらにドキュメントもない)のは論外といってよい。この設計は書く手間を減らして読む手間を犠牲にしている感がある。
Go的に考えるなら以下のような構成になる。
- まず conversations.history を正しく1ページ読める関数(メソッド)を作る
- 次にそれを1つのファイルに保存する仕組みを作る
- それを必要な回数イテレーション(=ページネーション)する仕組みを作る
- 必要なら複数ファイルに渡ったデータを1つのファイルにまとめる仕組みを作る
このとき既存のgeneratorの入力フォーマットも再考してよい
conversations.list
や users.list
は多少書く手間がかかっても別の入り口になってるべき。
DownloadConversationHistory
, DownloadConversationList
, DownloadUsersList
みたいに。
それぞれ最小限の引数を受け取るのが好ましい。
いっぺんに3つ作るのではなく1つを正しく作る。それを3回繰り返す。そのうえでまとめられるところはまとめる、というのが正着ではないでしょうか。
以上、かなり辛目なレビューでした。
internal/slacklog/api.go
Outdated
"path/filepath" | ||
) | ||
|
||
type APIRequest struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
公開型はかならずコメントを付けましょう。
golint 入れておいて make lint
で警告がでるんで
それが増えないようにしてください。
internal/slacklog/api.go
Outdated
} | ||
|
||
func reverse(slice []interface{}) { | ||
for i := 0; i < len(slice)/2; i++ { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
毎回 len()/2
はあんま良くないですね。
こんな感じでどうでしょか?
for i := 0; i < len(slice)/2; i++ { | |
for i, j := 0, len(slice)-1; i < j; i++, j-- { |
internal/slacklog/api.go
Outdated
} | ||
} | ||
|
||
func DownloadEntitiesToFile(request APIRequest, destJSONFilePath string) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
公開関数は…以下略
internal/slacklog/api.go
Outdated
if !ok { | ||
return "" | ||
} | ||
metadata := metadataValue.(map[string]interface{}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
キャスト(type assertion)に失敗したケースについて考慮が抜けてます。
他にも何か所かあるみたいですが指摘は省略しますので留意してください。
metadata := metadataValue.(map[string]interface{}) | |
metadata, ok := metadataValue.(map[string]interface{}) | |
if !ok { | |
return "" | |
} |
internal/slacklog/api.go
Outdated
for true { | ||
var json map[string]interface{} | ||
|
||
fmt.Printf("GET %v page %d of %s ... ", request.ExtraParams, pageNum, request.APIMethod) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ログは log パッケージ使ったほうが良いです。
fmt.Printf("GET %v page %d of %s ... ", request.ExtraParams, pageNum, request.APIMethod) | |
log.Printf("GET %v page %d of %s ... ", request.ExtraParams, pageNum, request.APIMethod) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
これ、今のところ場所によってまちまちなので統一したいですね > log or fmt
場所によっては fmt.Fprintln()
で os.Stderr
に出してるとこもあります
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
そうなんですよね。ほんとは脳死で log.Printf
使っておいてほしいんです。
そうすれば後から logging ライブラリを別のやつに差し替えるとかも簡単なんで。
list := json[request.JSONKey].([]interface{}) | ||
fmt.Printf("fetched %s count: %d\n", request.JSONKey, len(list)) | ||
|
||
results = append(results, list...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
patination が必要だと考えてるものを、メモリ上で全部合成するのはあんま嬉しくなさげ。
以下みたいに都度ファイルに出力してしまったほうが良いのでは?
もしくはドキュメントコメントにあんま大きなものは取らないで、みたいに書くべきか?
[
{"entity": "123456"},
{"entity": "123456"},
{"entity": "123456"},
{"entity": "123456"},
{"entity": "123456"},
{"entity": "123456"},
]
reverse との兼ね合いはあるが…そもそもJSONに書き出す段階で reverse する=順序を気にする必要ってなんなんだろう?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
どうせ読み込む時は全部メモリに読みますが…しかも全期間分のものを同時に。ダウンロード時にだけそこを気にする必要ありますかね?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
いまここから手を付けておけばその方向性が示せますよね。
generatorも今の全部メモリに乗せる方式は破綻が見えてるので、直さなきゃいけないですから。
downloaderは SQLiteとかのローカルのDBに逐次乗せる。
generatorはそれを切り出して1ページずつHTML生成
みたいなのが目指すべきゴールかなと。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
generator 側はともかく、こちらは1チャンネル/1日分です。1チャンネルの1日分のログのサイズはたかが知れていると思っているんですが(実際に 400 件以上の発言があった場合でも JSON で 300KB に満たない、メモリに展開しても 1MB も取ることはなさそう)、そこを今頑張る必要あるでしょうか?
この部分は過去の全ログを処理する generator と違って、常に1日分なので今後ログが蓄積していっても処理する量が増えて行ったりはしません。
メモリも基本的に潤沢な今の時代にそこを気にするのは早すぎる最適化を感じてしまいます。
internal/slacklog/api.go
Outdated
return nil, fmt.Errorf("Error response: %v", json) | ||
} | ||
|
||
list := json[request.JSONKey].([]interface{}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
キーがみつからないケース。型アサーションに失敗したケースの考慮もれ。
internal/slacklog/api.go
Outdated
type APIRequest struct { | ||
SlackToken string | ||
APIMethod string | ||
ExtraParams map[string]string | ||
JSONKey string | ||
Reverse bool | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
名前と設計があってない。
APIRequest だと将来的にすべてのAPIを叩けるようになりそうで、そのパラメータをすべて内包しそう。
slackclient.Do(APIRequest)
みたいな使い方が想定される。
また DownloadEntitiesToFile()
(Pagination) 専用だとしても必須パラメータとオプショナルがまざってて、使う法としては困惑する。
仮に自分がやるならこうなる
type DownloadEntitiesOptions struct {
ExtraParams map[string]string
Reverse bool
}
func DownloadEntitiesToFile(token, method, itemKey, dstName string, opts DownloadEntitiesOptions) error {
もしくは token は必須だとしてメソッド生やす形かな。
type SlackClient struct {
token string
c *http.Client
}
type DownloadEntitiesOptions struct {
ExtraParams map[string]string
Reverse bool
}
func (sc *StackClient) DownloadEntitiesToFile(method, itemKey, dstName string, opts DownloadEntitiesOptions) error {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
いい名前が思い付かなくて諦めたやつです…。
愚直に引数を10個でも20個でも並べた方がいい感じなんですね。構造体使うのやめてみます。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
必須パラメータはなるべく引数に倒したほうが使うときにわかりやすいです。
で省略しても問題ないオプショナルパラメーターは構造体に突っ込むと。
もしくは全部の引数を構造体に入れるなら、全フィールドに
「これは必須」
「こっちはオプショナルでこういう時に使ってね」
みたいなドキュメント書くと良いです。
return nextCursorValue.(string) | ||
} | ||
|
||
func httpGetJSON(rawurl string, params map[string]string, dst interface{}) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
APIの汎用性を考えると params
は map[string][]string
のが良いかもしれない。
同じ名前の query string ってなんだよって思うけれど url.Values はそれを考慮した設計になってるし、
まれにそういうAPIもある。
当然 values.Set ではなくて values.Add で地道に構築する形になる。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
そのケースは使わないだろうと判断して省略しました。
subcmd/fetch_messages.go
Outdated
"limit": "200", | ||
}, | ||
JSONKey: "messages", | ||
Reverse: true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
この順序を保証しなきゃいけないのは、実は generator の処理の仕方に問題があるからで、それをココで吸収するのは好ましくない。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
本 PR とは独立した問題ですね。Issue 立てておいてもらえると…。 🙏
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
説明はあとから
internal/slacklog/api.go
Outdated
var nextCursor string | ||
|
||
pageNum := 1 | ||
for true { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ここ、for {
でよかったりします
個人的には |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
レビューありがとうございます。勉強になります。
まず conversations.history を正しく1ページ読める関数(メソッド)を作る
次にそれを1つのファイルに保存する仕組みを作る
これをやってしまうと1日1ファイルで保存すると言う目的が達成できなくなってします。
全体としてどう直すのがいいのか、指摘からだとちょっとわからず…。
とりあえず細かい指摘の部分を直してみます。
list := json[request.JSONKey].([]interface{}) | ||
fmt.Printf("fetched %s count: %d\n", request.JSONKey, len(list)) | ||
|
||
results = append(results, list...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
どうせ読み込む時は全部メモリに読みますが…しかも全期間分のものを同時に。ダウンロード時にだけそこを気にする必要ありますかね?
return nextCursorValue.(string) | ||
} | ||
|
||
func httpGetJSON(rawurl string, params map[string]string, dst interface{}) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
そのケースは使わないだろうと判断して省略しました。
subcmd/fetch_messages.go
Outdated
"limit": "200", | ||
}, | ||
JSONKey: "messages", | ||
Reverse: true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
本 PR とは独立した問題ですね。Issue 立てておいてもらえると…。 🙏
internal/slacklog/api.go
Outdated
type APIRequest struct { | ||
SlackToken string | ||
APIMethod string | ||
ExtraParams map[string]string | ||
JSONKey string | ||
Reverse bool | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
いい名前が思い付かなくて諦めたやつです…。
愚直に引数を10個でも20個でも並べた方がいい感じなんですね。構造体使うのやめてみます。
a1f3b12
to
84780c6
Compare
84780c6
to
ea295a7
Compare
#116 を見ましたが、理解しづらく、今の私ではレベルが足りずに足を引っ張りそうなのでこの件からは手を引きます。お手数をおかけしますが後は任せました。 |
users.json
を更新するupdate-user-list
サブコマンドchannels.json
を更新するupdate-channel-list
サブコマンドfetch-messages
サブコマンドを追加しました。
正直なところ設計とか命名とか全体的にイケてない感じがするので、アドバイスよろしくお願いします!