Gabung beta Hanzo dan dapatkan diskon 50% untuk setiap paket berbayar

Router adalah registri

Hanzo Cloud tidak punya berkas OpenAPI yang di-check-in. Tabel rute yang hidup adalah sumbernya, diproyeksikan dengan tiga cara, dan uji bijeksi menggagalkan build jika ada proyeksi yang menyimpang. 1,362 operasi pada 963 path, dihasilkan per proses pada saat permintaan.

Di halaman ini

Tidak ada openapi.yaml di repositori Hanzo Cloud. Tidak ada registri kedua yang mendaftar endpoint, tidak ada berkas anotasi, dan tidak ada langkah generator di CI yang menulis spesifikasi ke disk. Jika Anda ingin tahu apa API itu, tanyakan pada proses yang sedang berjalan, dan ia menjawab dengan membaca routernya sendiri.

Tulisan ini tentang mengapa pengaturan itulah yang akhirnya bertahan, dan tentang bagian-bagiannya yang tidak bekerja, yang ternyata merupakan setengah yang lebih berguna.

Satu tabel, tiga proyeksi

serve.go menyusun satu tabel rute. Tiga hal kemudian membacanya, semuanya setelah MountAll, supaya masing-masing melihat tabel yang lengkap:

  • /zap memutar ulang handler /v1 di atas transport ZAP.
  • Konsol merendernya.
  • GET /v1/openapi.json mendeskripsikannya.

Tidak satu pun dari ketiganya menyimpan salinan apa pun. Itulah seluruh kendala desain, dan segala yang lain mengikutinya. Proyeksi yang menyimpan daftar endpoint sendiri akan menjadi sumber kebenaran kedua, dan sumber kebenaran kedua menyimpang, bukan kadang-kadang, selalu, pada jadwal yang ditentukan oleh seberapa sering seseorang lupa.

Proyeksi deskripsi adalah yang ditanyakan orang, jadi: openapi.Live(app) memanggil app.Fiber().GetRoutes(true), dan setiap fungsi lain di openapi/ adalah fungsi murni dari []Route yang kembali. Dokumen tidak dibangun dari router. Dokumen itu adalah router, dalam bentuk lain.

Mengapa membaca router yang hidup adalah satu-satunya sumber yang utuh

Alternatif yang jelas adalah memindai sumber. Itu tidak berhasil, dan alasannya layak dinyatakan dengan tepat, karena itulah alasan banyak perkakas API diam-diam melaporkan kurang.

POST /v1/kms/auth/login didaftarkan seperti ini:

Group("/v1/kms/auth").Post("/login")

Tidak ada grep yang menemukan path itu. String /v1/kms/auth/login tidak ada di basis kode. Ia hanya ada di router yang sudah dirakit, saat runtime, setelah prefiks grup dan daun digabung oleh framework.

Lalu menjadi lebih buruk, dengan cara yang sebenarnya benar. Himpunan rute adalah fungsi dari konfigurasi deployment, cfg.Enabled, plus gerbang internal seperti if kc != nil milik KMS. Deployment yang tidak memasang admin tidak menyajikan rute admin. Jadi spesifikasi yang jujur untuk deployment itu tidak mengiklankannya.

Inilah mengapa dokumen dihasilkan per proses, pada saat permintaan, bukan dibangun sekali di CI. Spesifikasi yang dibangun di CI menggambarkan deployment maksimal yang hipotetis, yang mungkin bukan yang sedang Anda ajak bicara. Spesifikasi yang dibaca dari router yang hidup menggambarkan proses yang menjawab Anda.

Pengujian yang membuatnya benar

Arsitektur tempat spesifikasi tidak dapat menyimpang adalah klaim, dan klaim butuh gerbang. Gerbang kami adalah bijeksi, di cmd/cloud/openapi_test.go:

func TestSpecDoesNotDriftFromLiveRouter(t *testing.T) {

Ia memasang setiap subsistem di apps.Wire(), menghasilkan dokumen, dan menegaskan pemetaan di kedua arah: setiap rute hidup muncul sebagai operasi, dan setiap operasi ditopang rute hidup. Bukan subset di salah satu arah.

Jalankan hari ini:

bijection holds over 1362 operations / 963 paths / 138 products

Ini satu-satunya pengujian di repositori yang kegagalannya berarti dokumen yang diterbitkan berbohong. Segala yang lain pecah membuat produk salah; yang ini membuat kontrak salah, dan itu lebih buruk, karena integrator sudah membangun terhadapnya.

Jumlah yang bergerak bukan masalah. Ia bergerak setiap kali subsistem ditambahkan. Angka yang penting adalah selisih kedua sisi, dan angka itu 0 atau build gagal.

Sumbu produk bersifat mekanis

Segmen path pertama setelah /v1/ adalah produk. openapi.Product membacanya dan menandai setiap operasi dengannya, yang memungkinkan CLI menyusun hanzo <product> <resource> <verb> tanpa ada yang menilai di mana sebuah perintah seharusnya tinggal.

Ini sengaja bukan nama subsistem. clients/billing juga melayani /v1/finance/*, dan URL adalah yang dilihat pemanggil, jadi URL yang menang. Menamai sumbu menurut paket internal akan membuat CLI artefak yang dikurasi, benda yang berisi pendapat dan harus dipelihara seseorang, alih-alih sebuah proyeksi.

Apa yang tidak dapat dikatakan router

Inilah batasnya, dan kami tidak bermaksud mengakalinya.

Metode, path, parameter path, dan produk dapat diturunkan dari router. Skema permintaan dan respons, parameter kueri dan header, kode status, serta auth bukan. Router menyimpan sebuah func(*zip.Ctx) error. Tipe permintaan adalah variabel lokal di dalam tubuh handler:

var req secretPutRequest
json.Unmarshal(ctx.Body(), &req)

Go tidak dapat merefleksi dari nilai fungsi ke dalam tubuhnya. Tidak ada perbaikan cerdik di sini, dan mencobanya di generator berarti menebak, dan begitulah spesifikasi memperoleh entri yang yakin tetapi salah.

Satu-satunya jalan ke skema adalah operasi bertipe:

zip.Get[In, Out](...)

In dan Out ada pada pendaftaran, jadi skema tersedia tanpa membaca pikiran siapa pun. Dan karena keduanya ada pada pendaftaran, entri yang sama juga menghasilkan alat MCP. zip/openapi.go dan zip/mcp.go adalah dua pembaca dari satu registri. Itulah proyeksi keempat, dan ia datang tanpa mekanisme keempat.

Migrasi bersifat inkremental menurut konstruksi: GetRoutes() adalah superset dari app.ops, jadi mengubah handler menjadi operasi bertipe menambah skema tanpa mengubah apa pun di hulunya.

Rute yang tidak dapat diberi tipe, dan mengapa

clients/integrations dikonversi sejauh yang diizinkan kawat: 20 dari 41 rute adalah operasi bertipe. 21 lainnya tetap handler mentah, dan masing-masing tercantum di peta rawRoutes dalam ops_projection_test.go beserta alasan ia tidak dapat pindah:

  • 13 pengalihan peramban. Callback OAuth dan setiap kaki tautan menjawab 302. Operasi bertipe mengembalikan nilai, bukan header Location dan status di kisaran 300.
  • 6 webhook. HMAC Slack dan GitHub, Ed25519 Discord, JWT Teams Bot Framework, header token rahasia Telegram. Setiap satu mengautentikasi tanda tangan di atas byte permintaan mentah. Operasi bertipe menerima In yang sudah didekode dan tidak pernah melihat byte yang menjadi dasar tanda tangan, jadi memberi tipe pada ini akan merusak auth-nya.
  • 2 pembuat 202 Accepted. cloud.Created memberi kami sambungan 201. Tidak ada sambungan 202. Dua rute membutuhkannya, jadi dua rute menunggu.

Kami pikir daftar itu adalah artefak paling berharga di seluruh sistem. Ia adalah inventaris yang terenumerasi dan disematkan uji tentang tepat di mana abstraksi berhenti, dengan alasan per entri yang dapat diperdebatkan peninjau. Mode kegagalan yang dicegahnya adalah rute yang diam-diam kembali menjadi handler mentah dan lenyap dari dokumen, himpunan alat MCP, dan CLI sekaligus, tanpa terlihat, karena tidak ada yang menegaskan bahwa ia pernah ada di sana.

Arsitektur bukan bagian yang menggeneralisasi. Ia adalah bagian yang menggeneralisasi ditambah catatan yang jujur dan ditegakkan tentang bagian yang tidak.

Apa yang ini beli

Pemanggil yang berintegrasi dengan Hanzo Cloud membaca dokumen yang dihasilkan oleh proses yang menjawab permintaan mereka, di deployment yang benar-benar mereka ajak bicara, beberapa milidetik yang lalu. Agen yang memanggil lewat MCP mencapai handler yang sama, dengan pemeriksaan tenancy yang sama, seperti pemanggil REST. Panggilan /mcp anonim mendapat 403 yang sama dengan yang diberikan REST, karena itu jalur kode yang sama dan sebuah uji menyematkannya.

Tidak satu pun dari itu mengharuskan seseorang ingat untuk meregenerasi apa pun.

Bentuk umumnya: jika dua artefak harus sepakat, jangan hasilkan yang satu dari yang lain lalu berharap. Jadikan salah satunya pandangan dari yang lain, dan tulis uji yang gagal ketika keduanya tidak demikian.

Komentar

Masuk dengan Hanzo ID untuk berkomentar.

Memuat komentar…

Baca selengkapnya