Upgrade to Pro — share decks privately, control downloads, hide ads and more …

GraphQLの静的解析ツールを作った - tenntenn Conference 2022

GraphQLの静的解析ツールを作った - tenntenn Conference 2022

この資料はtenntenn Conference 2022にて発表を行った際に用いた資料です。

■ tenntenn Conferenceとは
tenntenn Conferenceはtenntennが主催し、そしてすべてのセッションがtenntennによる登壇のカンファレンスです。

イベントページ:https://tenntenn.connpass.com/event/226562/
ハッシュタグ:#tennconn
資料(Google スライド):https://tenn.in/gqlanalysis
動画:https://tenn.in/gqlanalysis-video
再生リスト:https://tenn.in/conn22-videolist

■ 内容
これまでGraphQLのスキーマやクエリに対する静的解析ツールを開発したい場合にはGoを使うという選択肢はありませんでした。そこでgqlanalysisというGoでGraphQLの静的解析を行うためのライブラリを開発しました。このセッションではGraphQLの静的解析の必要性やgqlanalysisの解説を行います。

・GraphQL
・静的解析
・スキーマ/クエリ
・gqlanalysis

■ 登壇者&主催者

・名前:tenntenn / 上田拓也
・HP:https://tenntenn.dev
・Twitter:https://twitter.com/tenntenn

メルカリ/メルペイ所属。バックエンドエンジニアとして日々Goを書いている。Google Developer Expert (Go)。一般社団法人Gophers Japan代表。Go Conference主催者。大学時代にGoに出会い、それ以来のめり込む。人類をGopherにしたいと考え、Goの普及に取り組んでいる。複数社でGoに関する技術アドバイザーをしている。大学においてGoに関する集中講義も担当している。マスコットのGopherの絵を描くのも好き。

■ Gopher道場 自習室

https://gopherdojo.org/studyroom/

Gopher道場とは、実践的なGoを体系的に学べる場です。
Gopher道場 自習室では、以下のようなコンテンツや学びの場を提供します。

・Gopher道場の講義を録画した動画(10時間以上分)
・Slackにおける受講者同士のコミュニティ
・Gopher道場卒業生による課題のレビュー(ボランティアでご協力頂いているのでベストエフォートです)

■ Meety(カジュアル面談)

・ソフトウェアエンジニアの地方移住ってどうなの?:https://meety.net/matches/jyZgDkEEwmMk
・メルカリグループにおけるGoの使いどころ:https://meety.net/matches/LbeVbIACxLqk
・地方からの技術コミュニティへの貢献:https://meety.net/matches/gVeMtImLkWJE

■ お仕事の依頼について

副業にて技術顧問やアドバイザーなどを行っています。過去の実績や問い合わせフォームは以下のURLからご確認ください。
https://tenntenn.dev/ja/job/

tenntenn - Takuya Ueda

January 22, 2022
Tweet

More Decks by tenntenn - Takuya Ueda

Other Decks in Programming

Transcript

  1. The Go gopher was designed by Renée French. The gopher

    stickers was made by Takuya Ueda. Licensed under the Creative Commons 3.0 Attributions license. GraphQLの静的解析 ツールを作った 2022/01/15(土) tenntenn Conference 2022 資料:https://tenn.in/gqlanalysis 動画:https://tenn.in/gqlanalysis-video
  2. 上田拓也 Go ビギナーズ
 Go Conference
 @tenntenn tenntenn.dev Google Developer Expert

    (Go) 一般社団法人 Gophers Japan 代表理事 Experts Team
  3. GraphQLの静的解析 ▪ スキーマとクエリの静的解析 • https://github.com/gqlgo/gqlanalysis/ • Goで書ける ◦ GraphQLの静的解析ツールはほとんどがJavaScript ◦

    開発言語にGoを使っている場合はGoで書きたい • パースにgqlparserを使用 • go/analysisパッケージと似た形で開発できる ◦ モジュール化(Analyzer単位での開発) ◦ テスト • gqlskeletonコマンド ◦ スケルトンコードを作れる
  4. GraphQLの静的解析ツールの例 ▪ lackid • https://github.com/gqlgo/lackid • スキーマにidフィールドを持つのにクエリに指定されていないもの query GetA() {

    a { # want "type A has id field but selection a does not have id field" name } } $ lackid -schema="server/graphql/schema/**/*.graphql" -query="client/**/*.graphql" # イントロスペクションにも対応 $ lackid -schema="https://example.com" -query="client/**/*.graphql"
  5. gqlanalysis.Pass構造体 ▪ 静的解析に使う情報が入った構造体 • Analyzer.Runフィールドセットされた関数の引数で用いる type Pass struct { Analyzer

    *Analyzer Schema *ast.Schema // スキーマの抽象構文木 Queries []*ast.QueryDocument // クエリの抽象構文木 Comments []*Comment Report func(*Diagnostic) // エラー出力 ResultOf map[*Analyzer]interface{} // 依存するAnalyzerの結果 }
  6. Analyzerの例 var Analyzer = &gqlanalysis.Analyzer{ Name:"simple", Doc:"simple", Run:run } func

    run(pass *gqlanalysis.Pass) (interface{}, error) { for _, q := range pass.Queries { for _, f := range q.Fragments { for _, sel := range f.SelectionSet { switch sel := sel.(type) { case *ast.Field: if sel.Name == "name" { pass.Reportf(sel.Position, "NG") } } } } } return nil, nil }
  7. テストデータ fragment AFragment1 on A { name # want "NG"

    } fragment AFragment2 on A { id name # want "NG" ...BFragment } fragment BFragment on A { name # want "NG" } ▪ wantで始まるコメントで期待する結果を書く • 対応するDiagnosticが出力されていればOK 正規表現が使える
  8. multichecker ▪ multichecker.Main • 複数のAnalyzerからなるmain関数を提供する • 各Analyzerはゴールーチンで1回だけ実行される package main import

    ( "github.com/gqlgo/gqlanalysis/multichecker" "github.com/gqlgo/lackid" "github.com/gqlgo/myanalyzer" ) func main() { multichecker.Main(lackid.Analyzer, myanalyzer.Analyzer) }
  9. gqlskeleton ▪ gqlanalysis用のスケルトンコードジェネレータ • 簡単にGraphQLの静的解析ツールを始めることができる • Analyzer、テストコード、main.goの雛形作ってくれる $ gqlskeleton simple

    simple ├── cmd │ └── simple │ └── main.go ├── go.mod ├── simple.go ├── simple_test.go └── testdata └── a ├── query │ └── query.graphql └── schema ├── model.graphql ├── mutation.graphql ├── query.graphql ├── schema.graphql └── subscription.graphql $ go install github.com/gqlgo/gqlanalysis/cmd/gqlskeleton@latest
  10. gqlskeletonの仕組み ▪ skeletonkitを使用 • https://github.com/gostaticanalysis/skeletonkit • スケルトンコードジェネレーターを作るためのライブラリ ◦ gostaticanalysis/skeletonでも使っている •

    テンプレートを用意してあげるだけ楽ちん 参考:io/fsパッケージを用いた テスタブルなコード生成ツールの開発 - Go Conference Online 2021 Autumn
  11. 今後入る予定の機能 ▪ コード生成ライブラリ • 簡単にGraphQL用コード生成器が作れる(PR#7) var flagOutput string var Generator

    = &codegen.Generator{ Name: "fragment", Doc: "example of codegen", Run: run } func init() { Generator.Flags.StringVar(&flagOutput, "output", "fragment.kt", "output file") } func run(pass *codegen.Pass) (rerr error) { output, path := pass.CreateTemp("fragment.kt") for _, q := range pass.Queries { if len(q.Fragments) == 0 { continue } tmpl := codegen.NewTemplate(pass, "fragment-template") _, err := tmpl.Funcs(funcMap(pass, tmpl)).Parse(tmplStr) if err != nil { return err } if err := tmpl.ExecuteTemplate(output, "fragments", q.Fragments); err != nil { return err } } if err := exec.Command("ktfmt", path).Run(); err != nil { return err } if _, err := output.Seek(0, io.SeekStart); err != nil { return err } if _, err := io.Copy(pass.Output, output); err != nil { return err } return nil }