Lompat ke konten
dokumentasi.id
Commit 493bd1c
v1
Star762

Kamu membaca versi lama. Halaman ini hanya bisa dibaca.

8

Pipeline Generate dengan Go Tools

Seluruh isi api/ dibuat oleh binary Go kecil di tools/. main.go membaca os.Args[1], memilih satu cabang switch, lalu memanggil fungsi run* yang sesuai (tools/main.go:34-71). Tidak ada subcommand yang memanggil subcommand lain, jadi keenam langkah pipeline berjalan sebagai enam proses go run terpisah dan urutannya ditentukan oleh pemanggil, bukan oleh kode. printUsage mencetak daftar perintah beserta deskripsi satu barisnya (tools/main.go:8-23).

Setiap cabang memakai pola error yang sama: kegagalan di dalam fungsi run* dicetak sebagai error: %v ke stderr lalu proses keluar dengan kode 1. Perintah yang tidak dikenal menghasilkan unknown command %q diikuti usage.

Urutan eksekusi

sequenceDiagram
  autonumber
  participant OP as Operator atau CI
  participant MN as tools/main.go
  participant XC as extract-csv
  participant GS as generate-static-api
  participant GB as generate-boundaries
  participant AH as add-has-path
  participant GM as generate-missings
  participant US as update-stats
  participant D as data/
  participant A as api/
  OP->>MN: go run ./tools extract-csv URL
  MN->>XC: runExtractCSV
  XC->>D: writeCSV per tabel
  OP->>MN: go run ./tools generate-static-api
  MN->>GS: runGenerateStaticAPI
  GS->>A: phase A, phase B, stats.json
  OP->>MN: go run ./tools generate-boundaries
  MN->>GB: runGenerateBoundaries
  GB->>A: api/paths/*.json dan lat/lng
  OP->>MN: go run ./tools add-has-path
  MN->>AH: runAddHasPath
  AH->>A: has_path pada setiap object
  OP->>MN: go run ./tools generate-missings
  MN->>GM: runGenerateMissings
  GM->>A: missings.json
  OP->>MN: go run ./tools update-stats
  MN->>US: runUpdateStats
  US->>A: patch stats.json
%% fig: Urutan enam subcommand pipeline
%% desc: Setiap langkah adalah proses go run terpisah, main.go hanya meng-dispatch satu subcommand, sehingga urutan di sini ditentukan oleh pemanggil.
Fig. 1: Urutan enam subcommand pipelineSetiap langkah adalah proses go run terpisah, main.go hanya meng-dispatch satu subcommand, sehingga urutan di sini ditentukan oleh pemanggil.

Rantai ini bukan saran, melainkan syarat. generate-static-api menulis ulang provinces.json, provinces/{kode}.json, dan daftar regencies/{kode}.json, sehingga langkah yang menambal berkas itu harus dijalankan sesudahnya. add-has-path dan generate-missings sama-sama membaca api/paths/, sehingga generate-boundaries harus selesai lebih dulu. update-stats menghitung seluruh berkas *.json di bawah api/, jadi ia harus menjadi langkah terakhir.

Nama perintah dan alias

SubcommandAlias yang diterima
extract-csvextract-sql
generate-static-apigenerate-api
generate-boundaries—
add-has-pathadd_has_path
generate-missingsgenerate-missing, generate_missings, missings
update-statsupdate_stats, generate-stats, generate_stats

Alias di atas berasal langsung dari label case di switch (tools/main.go:34-71).

Peta flag

SubcommandFlagDefaultKeterangan
extract-csv-ddatadirektori keluaran CSV; argumen posisional pertama wajib berisi path berkas SQL atau URL
generate-static-api-datadatadirektori sumber CSV
generate-static-api-outapidirektori keluaran pohon JSON
generate-static-api-vfalselog setiap berkas yang ditulis
generate-boundaries-data-boundariesdata/boundariesdirektori CSV hasil konversi boundary
generate-boundaries-outapidirektori keluaran static JSON API
generate-boundaries-cache.cachetempat zip dan SQL hasil ekstraksi
generate-boundaries-provkosongfilter satu kode provinsi, misalnya 31
generate-boundaries-limit0batasi jumlah berkas SQL yang diproses, 0 berarti tanpa batas
generate-boundaries-skip-downloadfalsepakai zip yang sudah ada di cache
add-has-path-out, -paths, -vapi, kosong, false-paths kosong berarti <out>/paths
generate-missings-out, -paths, -vapi, kosong, false-paths kosong berarti <out>/paths
update-stats-out, -vapi, falsestats.json dibaca dari <out>/stats.json

Flag diambil dari definisi flag.NewFlagSet masing-masing berkas: (tools/extract_csv.go:40-57), (tools/generate_static_api.go:41-49), (tools/generate_boundaries.go:46-54), (tools/add_has_path.go:29-43), (tools/generate_missings.go:38-47), dan (tools/update_stats.go:30-43). Semua path flag yang relatif diselesaikan terhadap akar repository, bukan terhadap direktori kerja: repoRoot menaiki direktori sampai menemukan .git (tools/generate_static_api.go:633-648). Karena itu perintah yang sama bisa dijalankan dari subdirektori mana pun.

1. extract-csv: SQL dump menjadi CSV

Sumber SQL boleh berupa path lokal atau URL http(s); openSource memilih http.Get bila string dimulai http:// atau https://, menolak status selain 200 dengan GET %s: HTTP %d, dan membungkus body unduhan dengan progressReader (tools/extract_csv.go:74-92). Berkas dibaca per baris lewat bufio.Scanner dengan batas token 64 * 1024 * 1024 agar kolom GeoJSON yang sangat panjang tetap masuk (tools/extract_csv.go:36-38, tools/extract_csv.go:138-149).

Setiap tabel yang muncul di INSERT ditulis sebagai satu CSV bernama sanitize(t) + ".csv" di dalam direktori -d; setelah semua tabel selesai, ringkasan dicetak sebagai Summary: %d table(s), %d row(s) (tools/extract_csv.go:253-260). Baris header diambil dari daftar kolom pada pernyataan INSERT; bila INSERT tidak menyebut kolom, header dibuat sintetis col1, col2, dan seterusnya mengikuti jumlah nilai pada baris pertama (tools/extract_csv.go:402-421).

Tiga berkas yang dibutuhkan tahap berikutnya diekstrak dari tiga dump berbeda: wilayah.csv (seluruh level), wilayah_level_1_2.csv (field kaya plus geometri), dan wilayah_kodepos.csv (peta kode ke kode pos) (tools/generate_static_api.go:1-18).

2. generate-static-api: CSV menjadi pohon JSON

Perintah ini bekerja dalam dua fase supaya kolom geometri yang besar tidak pernah ditahan untuk semua wilayah sekaligus: fase A men-stream wilayah.csv dan membaca wilayah_kodepos.csv, fase B men-stream wilayah_level_1_2.csv lalu memperkaya provinsi dan kabupaten/kota (tools/generate_static_api.go:1-18).

graph LR
  WC["data/wilayah.csv"] --> PA["generator.phaseA"]
  KP["data/wilayah_kodepos.csv"] --> PA
  L12["data/wilayah_level_1_2.csv"] --> PB["generator.phaseB"]
  PA --> BASE["api/provinces, regencies, districts, villages, postal-codes"]
  PB --> PATHS["api/paths/*.json"]
  PB --> REGEN["regenerate provinces + regencies"]
  BASE --> STAT["generator.writeStats"]
  REGEN --> STAT
  STAT --> SJ["api/stats.json"]
  style WC fill:#336791,color:#fff
  style KP fill:#336791,color:#fff
  style L12 fill:#336791,color:#fff
  style PA fill:#8b5cf6,color:#fff
  style PB fill:#8b5cf6,color:#fff
  style PATHS fill:#8b5cf6,color:#fff
  style SJ fill:#336791,color:#fff
  style STAT fill:#f59e0b,color:#000
%% fig: Dua fase generate-static-api
%% desc: phaseA membaca wilayah.csv dan wilayah_kodepos.csv, phaseB membaca wilayah_level_1_2.csv lalu menulis paths dan menulis ulang provinces serta regencies sebelum writeStats menutup dengan stats.json.
Fig. 2: Dua fase generate-static-apiphaseA membaca wilayah.csv dan wilayah_kodepos.csv, phaseB membaca wilayah_level_1_2.csv lalu menulis paths dan menulis ulang provinces serta regencies sebelum writeStats menutup dengan stats.json.

runGenerateStaticAPI menyiapkan enam subdirektori keluaran — provinces, regencies, districts, villages, postal-codes, paths — lalu memanggil phaseA, phaseB, dan writeStats berurutan. Tiga baris log penutupnya adalah phase A: %d files, phase B: %d paths written, %d level-1/2 files regenerated, dan total: %d files yang menghitung nA+nB+g.pathCount+1 (tools/generate_static_api.go:70-86).

Fase A mengisi api/provinces.json, api/provinces/{kode}.json, daftar regencies/{provinsi}.json, districts/{kabupaten}.json, villages/{kecamatan}.json, detail tiap wilayah, serta postal-codes/{kode_pos}.json (tools/generate_static_api.go:214-246). Fase B mewajibkan 11 kolom per baris dan berhenti dengan phase B: %s: expected %d fields, got %d bila kurang. Baris dengan kolom path yang bukan JSON valid tidak fatal; ia hanya dilaporkan sebagai phase B: skipping invalid path for %s. Setiap koordinat dibulatkan ke 6 angka desimal lewat roundPathCoords sebelum ditulis (tools/generate_static_api.go:294-353, tools/generate_static_api.go:653-665). Setelah itu fase B menulis ulang provinces.json, provinces/{kode}.json, daftar regencies/{kode}.json, dan detail regencies/{kode}.json supaya berisi field kaya (tools/generate_static_api.go:355-382).

writeStats mengisi api/stats.json dengan total_provinces, total_regencies, total_districts, total_villages, total_postal_codes, total_paths, total_population, dan total_area (tools/generate_static_api.go:384-418).

⚠️ writeGenJSONIndent dan writeGenJSONCompact sama-sama memanggil writeGenJSON dengan indent: false, sehingga seluruh keluaran — termasuk berkas yang namanya menyiratkan indentasi — ditulis satu baris tanpa pretty-print (tools/generate_static_api.go:569-595).

3. generate-boundaries: unduh, konversi, tulis paths

Perintah ini mengunduh defaultZipURL ke .cache/wilayah_boundaries.zip, mengekstraknya ke .cache/extracted, lalu mencari direktori db di dalamnya; tanpa -skip-download unduhan selalu diulang (tools/generate_boundaries.go:98-124). Untuk setiap berkas SQL, ia menulis CSV hasil konversi ke -data-boundaries, menulis api/paths/{kode}.json dengan path yang sudah dibulatkan, dan memperbarui lat/lng pada berkas detail maupun list (tools/generate_boundaries.go:228-252). Di akhir ia hanya menghitung ulang total_paths di api/stats.json (tools/generate_boundaries.go:307-311, tools/generate_boundaries.go:665-705). Rincian bentuk geometri dan cakupan wilayah ada di Polygon dan Boundary.

4. add-has-path: menempelkan has_path

buildHasPathSet membaca api/paths/, mengambil nama berkas tanpa .json sebagai kode, dan mengembalikan set kosong bila direktori itu belum ada (tools/add_has_path.go:110-127). Setiap target lalu dibaca, has_path diisi hasPathSet[id], meta.updated_at disegarkan, meta.generated_at dihapus, dan berkas ditulis ulang secara compact (tools/add_has_path.go:143-176). Kegagalan pada satu berkas tidak menghentikan proses: ia hanya dicatat sebagai warning: patch %s: %v, dan hasil akhirnya dilaporkan sebagai [has_path] patched %d/%d files (tools/add_has_path.go:94-108).

5. generate-missings: daftar wilayah yang kurang

Wilayah masuk daftar bila !has_path || !has_latlng (tools/generate_missings.go:109-116). Hasilnya api/missings.json berisi data, meta, dan summary, dengan summary memuat total_missing, total_missing_path, total_missing_latlng, total_missing_both, serta by_level untuk province, regency, district, dan village (tools/generate_missings.go:151-175). Arti setiap medan diuraikan di Kelengkapan Data dan missings.json.

6. update-stats: hitung endpoint dan ukuran

stats.json wajib sudah ada; bila tidak, perintah berhenti dengan stats file not found %s: %w (tools/update_stats.go:51-55). filepath.WalkDir menyusuri -out, melewati direktori berawalan titik, dan hanya menghitung berkas berakhiran .json sebagai endpoint sambil menjumlahkan ukuran logis dan ukuran disk tiap berkas (tools/update_stats.go:62-98). Lima kunci ditulis ke data: total_endpoints, total_filesize, total_filesize_human, total_disk_usage, total_disk_usage_human, dan meta.updated_at diperbarui (tools/update_stats.go:121-130).

Karena menulis stats.json sendiri mengubah ukuran berkas, fungsi ini mengukur ulang berkas itu dan mengoreksi total_filesize serta total_disk_usage dengan selisih ukuran lama dan baru, lalu menulis ulang stats.json sekali lagi (tools/update_stats.go:136-157).

Jebakan urutan

⚠️ Menjalankan update-stats lebih dulu membuat angka total_endpoints dan ukuran tidak mencakup berkas yang lahir sesudahnya, sehingga stats.json harus jadi langkah terakhir.

⚠️ Struktur genShortItem dan genPlace memang punya medan HasPath, tetapi fase A dan fase B tidak pernah mengisinya, sehingga menjalankan generate-static-api setelah add-has-path mengembalikan has_path ke false di berkas yang ditulis ulang (tools/generate_static_api.go:100-124).

⚠️ generate-boundaries -skip-download memakai zip apa pun yang ada di .cache; zip kosong atau usang membuat paths yang dihasilkan tidak sesuai dengan data/ saat itu (tools/generate_boundaries.go:98-124).