Tham gia bản thử Hanzo và được giảm 50% mọi gói trả phí

Bộ định tuyến chính là sổ đăng ký

Hanzo Cloud không có tệp OpenAPI được đưa vào kho. Bảng tuyến đang sống là nguồn, được chiếu theo ba cách, và một phép thử song ánh làm hỏng bản dựng nếu hình chiếu nào trôi. 1,362 thao tác trên 963 đường dẫn, sinh theo từng tiến trình tại thời điểm yêu cầu.

Trong trang này

Không có openapi.yaml trong kho Hanzo Cloud. Không có sổ đăng ký thứ hai liệt kê điểm cuối, không có tệp chú thích, và không có bước sinh trong CI ghi đặc tả xuống đĩa. Muốn biết API là gì, hãy hỏi một tiến trình đang chạy, và nó trả lời bằng cách đọc chính bộ định tuyến của mình.

Bài này nói về vì sao cách sắp xếp ấy cuối cùng là cách duy nhất đứng vững, và về những phần của nó không chạy, hóa ra lại là nửa hữu ích hơn.

Một bảng, ba hình chiếu

serve.go dựng một bảng tuyến. Ba thứ rồi đọc nó, tất cả sau MountAll, để mỗi thứ thấy một bảng đủ:

  • /zap phát lại các trình xử lý /v1 trên vận chuyển ZAP.
  • Bảng điều khiển kết xuất chúng.
  • GET /v1/openapi.json mô tả chúng.

Không cái nào trong ba cái giữ một bản sao của bất cứ gì. Đó là toàn bộ ràng buộc thiết kế, và mọi thứ khác theo từ đó. Một hình chiếu giữ danh sách điểm cuối riêng sẽ là nguồn sự thật thứ hai, và nguồn sự thật thứ hai thì trôi, không phải thỉnh thoảng, mà luôn luôn, theo lịch do việc ai đó quên thường đến mức nào.

Hình chiếu mô tả là cái người ta hỏi, vậy nên: openapi.Live(app) gọi app.Fiber().GetRoutes(true), và mọi hàm khác trong openapi/ là hàm thuần của []Route trả về. Tài liệu không được dựng từ bộ định tuyến. Tài liệu chính là bộ định tuyến, ở một hình khác.

Vì sao đọc bộ định tuyến đang sống là nguồn toàn phần duy nhất

Lối khác hiển nhiên là quét mã nguồn. Nó không chạy, và lý do đáng nói cho chính xác, vì đó là lý do nhiều công cụ API âm thầm báo thiếu.

POST /v1/kms/auth/login được đăng ký như thế này:

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

Không grep nào tìm thấy đường dẫn ấy. Chuỗi /v1/kms/auth/login không tồn tại trong cơ sở mã. Nó chỉ tồn tại trong bộ định tuyến đã lắp, lúc chạy, sau khi khung nối tiền tố nhóm với lá.

Rồi nó tệ hơn, theo cách thực ra là đúng. Tập tuyến là một hàm của cấu hình triển khai, cfg.Enabled, cộng các cổng nội bộ như if kc != nil của KMS. Một triển khai không gắn quản trị thì không phục vụ tuyến quản trị. Nên đặc tả trung thực của triển khai ấy không quảng cáo chúng.

Đó là lý do tài liệu được sinh theo từng tiến trình, tại thời điểm yêu cầu, thay vì dựng một lần trong CI. Một đặc tả dựng trong CI mô tả một triển khai tối đa giả định, có thể không phải cái bạn đang nói chuyện. Một đặc tả đọc từ bộ định tuyến đang sống mô tả tiến trình đã trả lời bạn.

Phép thử khiến điều đó thành thật

Một kiến trúc nơi đặc tả không thể trôi là một tuyên bố, và tuyên bố cần một cổng. Cổng của chúng tôi là một song ánh, trong cmd/cloud/openapi_test.go:

func TestSpecDoesNotDriftFromLiveRouter(t *testing.T) {

Nó gắn mọi phân hệ trong apps.Wire(), sinh tài liệu, và khẳng định ánh xạ cả hai chiều: mọi tuyến đang sống xuất hiện như một thao tác, và mọi thao tác được một tuyến đang sống chống lưng. Không phải tập con ở chiều nào.

Chạy hôm nay:

bijection holds over 1362 operations / 963 paths / 138 products

Đây là phép thử duy nhất trong kho mà thất bại có nghĩa tài liệu đã công bố đang nói dối. Mọi thứ khác gãy làm sản phẩm sai; cái này làm hợp đồng sai, tệ hơn, vì người tích hợp đã xây dựa trên nó.

Số đếm dịch chuyển không phải vấn đề. Nó dịch chuyển mỗi khi thêm một phân hệ. Con số quan trọng là hiệu hai phía, và con số ấy là 0, nếu không bản dựng thất bại.

Trục sản phẩm là cơ học

Đoạn đường dẫn đầu tiên sau /v1/ là sản phẩm. openapi.Product đọc nó và gắn thẻ mỗi thao tác bằng nó, nhờ đó CLI dựng hanzo <product> <resource> <verb> mà không ai phải phán lệnh nên sống ở đâu.

Cố ý, đó không phải tên phân hệ. clients/billing cũng phục vụ /v1/finance/*, và URL là cái người gọi thấy, nên URL thắng. Đặt tên trục theo gói nội bộ sẽ biến CLI thành một hiện vật được tuyển, một thứ có ý kiến trong đó và ai đó phải giữ, thay vì một hình chiếu.

Bộ định tuyến không thể nói cho bạn điều gì

Đây là giới hạn, và chúng tôi không định đi vòng.

Phương thức, đường dẫn, tham số đường dẫn, và sản phẩm suy được từ bộ định tuyến. Lược đồ yêu cầu và phản hồi, tham số truy vấn và tiêu đề, mã trạng thái, cùng xác thực thì không. Bộ định tuyến giữ một func(*zip.Ctx) error. Kiểu yêu cầu là một biến cục bộ trong thân trình xử lý:

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

Go không thể phản chiếu từ một giá trị hàm vào thân nó. Không có cách sửa khéo ở đây, và thử làm trong bộ sinh nghĩa là đoán, đó là cách một đặc tả có được các mục tự tin mà sai.

Con đường duy nhất tới lược đồ là một thao tác có kiểu:

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

In và Out nằm trên phép đăng ký, nên lược đồ có sẵn mà không đọc tâm trí ai. Và vì chúng nằm trên phép đăng ký, cùng một mục cũng cho ra một công cụ MCP. zip/openapi.go và zip/mcp.go là hai người đọc của một sổ đăng ký. Đó là hình chiếu thứ tư, và nó đến mà không cần cơ chế thứ tư.

Việc chuyển là tăng dần theo cách dựng: GetRoutes() là tập cha của app.ops, nên đổi một trình xử lý thành thao tác có kiểu thêm lược đồ mà không đổi gì ở thượng nguồn.

Những tuyến không thể gán kiểu, và vì sao

clients/integrations được chuyển đến mức dây cho phép: 20 trong 41 tuyến là thao tác có kiểu. 21 tuyến còn lại ở dạng trình xử lý thô, và mỗi tuyến được liệt kê trong bản đồ rawRoutes tại ops_projection_test.go cùng lý do nó không thể chuyển:

  • 13 chuyển hướng trình duyệt. Callback OAuth và mỗi chân liên kết trả lời 302. Một thao tác có kiểu trả về một giá trị, không phải tiêu đề Location và một trạng thái trong dải 300.
  • 6 webhook. HMAC của Slack và GitHub, Ed25519 của Discord, JWT của Teams Bot Framework, tiêu đề token bí mật của Telegram. Mỗi cái xác thực một chữ ký trên byte thô của yêu cầu. Một thao tác có kiểu nhận In đã giải mã và không bao giờ thấy các byte mà chữ ký được tính trên đó, nên gán kiểu cho chúng sẽ làm hỏng xác thực.
  • 2 trình tạo 202 Accepted. cloud.Created cho chúng ta một đường nối 201. Không có đường nối 202. Hai tuyến cần một đường, nên hai tuyến chờ.

Chúng tôi nghĩ danh sách ấy là hiện vật quý nhất của cả hệ. Nó là một kiểm kê được đánh số, ghim bằng phép thử, đúng chỗ trừu tượng dừng lại, với một lý do mỗi mục mà người duyệt có thể tranh luận. Kiểu hỏng mà nó ngăn là một tuyến lặng lẽ trượt về trình xử lý thô rồi biến khỏi tài liệu, bộ công cụ MCP, và CLI cùng lúc, vô hình, vì không gì đang khẳng định nó từng ở đó.

Một kiến trúc không phải những phần khái quát được. Nó là những phần khái quát được, cộng một bản kê trung thực và được cưỡng chế về những phần không.

Cái này mua được gì

Người gọi tích hợp với Hanzo Cloud đọc một tài liệu do chính tiến trình đang trả lời yêu cầu của họ sinh ra, trong triển khai họ thực sự đang nói chuyện, vài mili giây trước. Một tác tử gọi qua MCP tới đúng trình xử lý, với cùng phép kiểm thuê bao, như người gọi REST. Một cuộc gọi /mcp ẩn danh nhận đúng 403 mà REST đưa, vì đó là cùng một đường mã và một phép thử ghim nó.

Không điều nào trong đó đòi ai phải nhớ sinh lại thứ gì.

Dạng tổng quát: nếu hai hiện vật phải khớp nhau, đừng sinh cái này từ cái kia rồi hy vọng. Hãy biến một cái thành một cái nhìn của cái kia, và viết phép thử thất bại khi chúng không phải vậy.

Bình luận

Đăng nhập bằng Hanzo ID để bình luận.

Đang tải bình luận…

Đọc tiếp