The toolkit of Go kit.
- Just write code for your business logic, generate everything else.
- Implement the service once, consume it in various ways (in-process function call or RPC).
-
Code Generation Tool
- Endpoint
- Transport
- HTTP
- HTTP Server
- HTTP Test
- HTTP Client
- OAS-v2 Documentation
- gRPC
- HTTP
-
Useful Packages
- appx: Application framework for HTTP and CRON applications (a wrapper of appx).
- prometheus: Prometheus metrics utilities.
- trace: A thin wrapper of x/net/trace for Go kit.
- werror: Classified business errors.
$ go get -u github.com/RussellLuo/kok/cmd/kokgenUsage
$ kokgen -h
kokgen [flags] source-file interface-name
-fmt
whether to make code formatted (default true)
-out string
output directory (default ".")
-pkg string
package name (default will infer)
-test string
the YAML file that provides test-cases for HTTP (default "./http.test.yaml")
-trace
whether to enable tracingNOTE: The following code is located in helloworld.
-
Define the interface
type Service interface { SayHello(ctx context.Context, name string) (message string, err error) }
-
Implement the service
type Greeter struct{} func (g *Greeter) SayHello(ctx context.Context, name string) (string, error) { return "Hello " + name, nil }
-
Add HTTP annotations
type Service interface { // @kok(op): POST /messages SayHello(ctx context.Context, name string) (message string, err error) }
-
Generate the HTTP code
$ cd examples/helloworld $ kokgen ./service.go Service -
Consume the service
Run the HTTP server:
$ go run cmd/main.go 2020/09/15 18:06:22 transport=HTTP addr=:8080
Consume by HTTPie:
$ http POST :8080/messages name=Tracey HTTP/1.1 200 OK Content-Length: 27 Content-Type: application/json; charset=utf-8 Date: Tue, 15 Sep 2020 10:06:34 GMT { "message": "Hello Tracey" }
-
See the OAS documentation
(Click to show details)
$ http GET :8080/api HTTP/1.1 200 OK Content-Length: 848 Content-Type: text/plain; charset=utf-8 Date: Tue, 15 Sep 2020 10:08:24 GMT swagger: "2.0" info: title: "No Title" version: "0.0.0" description: "Service is used for saying hello." license: name: "MIT" host: "example.com" basePath: "/" schemes: - "https" consumes: - "application/json" produces: - "application/json" paths: /messages: post: description: "SayHello says hello to the given name." operationId: "SayHello" parameters: - name: body in: body schema: $ref: "#/definitions/SayHelloRequestBody" produces: - application/json; charset=utf-8 responses: 200: description: "" schema: $ref: "#/definitions/SayHelloResponse" definitions: SayHelloRequestBody: type: object properties: name: type: string SayHelloResponse: type: object properties: message: type: string
See more examples here.
Define the HTTP request operation
-
Key:
@kok(op) -
Value:
<method> <pattern>- method: The request method
- pattern: The request URL
- NOTE: All variables (snake-case or camel-case) in pattern will automatically be bound to their corresponding method arguments (matches by name), as path parameters, if the variables are not specified as path parameters explicitly by
@kok(param).
- NOTE: All variables (snake-case or camel-case) in pattern will automatically be bound to their corresponding method arguments (matches by name), as path parameters, if the variables are not specified as path parameters explicitly by
-
Example:
type Service interface { // @kok(op): DELETE /users/{id} DeleteUser(ctx context.Context, id int) (err error) } // HTTP request: // $ http DELETE /users/101
Define the HTTP request parameters
- Key:
@kok(param) - Value:
<argName> < in:<in>,name:<name>,required:<required>- argName: The name of the method argument.
- Argument aggregation: By specifying the same argName, multiple request parameters (each one is of basic type or repeated basic type) can be aggregated into one method argument (of any type).
- You do not need to repeat the argName, only the first one is required.
- Argument aggregation: By specifying the same argName, multiple request parameters (each one is of basic type or repeated basic type) can be aggregated into one method argument (of any type).
- in:
- path: The method argument is sourced from a path parameter.
- Optional: All variables (snake-case or camel-case) in pattern will automatically be bound to their corresponding method arguments (matches by name), as path parameters.
- query: The method argument is sourced from a query parameter.
- To receive values from a multi-valued query parameter, the method argument can be defined as a slice of basic type.
- header: The method argument is sourced from a header parameter.
- cookie: The method argument is sourced from a cookie parameter.
- Not supported yet.
- request: The method argument is sourced from a property of Go's http.Request.
- This is a special case, and only one property
RemoteAddris available now. - Note that parameters located in request have no relationship with OAS.
- This is a special case, and only one property
- path: The method argument is sourced from a path parameter.
- name: The name of the corresponding request parameter.
- Optional: Defaults to argName if not specified.
- descr: The OAS description of the corresponding request parameter.
- Optional: Defaults to
""if not specified.
- Optional: Defaults to
- required: Determines whether this parameter is mandatory.
- Optional: Defaults to
false, if not specified. - If the parameter location is path, this property will be set to
trueinternally, whether it's specified or not.
- Optional: Defaults to
- argName: The name of the method argument.
- Example:
-
Bind request parameters to simple arguments:
type Service interface { // @kok(op): PUT /users/{id} // @kok(param): name < in:header,name:X-User-Name UpdateUser(ctx context.Context, id int, name string) (err error) } // HTTP request: // $ http PUT /users/101 X-User-Name:tracey
-
Bind multiple request parameters to a struct according to tags:
type User struct { ID int `kok:"path.id"` Name string `kok:"query.name"` Age int `kok:"header.X-User-Age"` } type Service interface { // @kok(op): PUT /users/{id} // @kok(param): user UpdateUser(ctx context.Context, user User) (err error) } // HTTP request: // $ http PUT /users/101?name=tracey X-User-Age:1
-
Bind multiple query parameters to a struct with no tags:
type User struct { Name string Age int Hobbies []string } type Service interface { // @kok(op): POST /users // @kok(param): user CreateUser(ctx context.Context, user User) (err error) } // HTTP request: // $ http POST /users?Name=tracey&Age=1&Hobbies=music&Hobbies=sport
-
Argument aggregation:
type Service interface { // @kok(op): POST /logs // @kok(param): ip < in:header,name:X-Forwarded-For // @kok(param): ip < in:request,name:RemoteAddr Log(ctx context.Context, ip net.IP) (err error) } // The equivalent annotations. type Service interface { // @kok(op): POST /logs // @kok(param): ip < in:header,name:X-Forwarded-For // @kok(param): < in:request,name:RemoteAddr Log(ctx context.Context, ip net.IP) (err error) } // You must customize the decoding of `ip` later (conventionally in another file named `codec.go`). // See examples in the `Encoding and decoding` section. // HTTP request: // $ http POST /logs
-
Define the HTTP request body
- Key:
@kok(body) - Value:
<field>orbody:<field>,name:<argName>=<name>,descr:<argName>=<descr>- field: The name of the method argument whose value is mapped to the HTTP request body.
- Optional: When omitted, a struct containing all the arguments, which are not located in path/query/header, will automatically be mapped to the HTTP request body.
- The special name
-can be used, to define that there is no HTTP request body. As a result, every argument, which is not located in path/query/header, will automatically be mapped to one or more query parameters.
- argName: The name of the method argument to be manipulated.
- name: The name of the corresponding request parameter.
- Optional: Defaults to argName if not specified.
- descr: The OAS description of the corresponding request parameter.
- Optional: Defaults to
""if not specified.
- Optional: Defaults to
- field: The name of the method argument whose value is mapped to the HTTP request body.
- Example:
-
Omitted:
type Service interface { // @kok(op): POST /users CreateUser(ctx context.Context, name string, age int) (err error) } // HTTP request: // $ http POST /users name=tracey age=1
-
Specified as a normal argument:
type User struct { Name string `json:"name"` Age int `json:"age"` } type Service interface { // @kok(op): POST /users // @kok(body): user CreateUser(ctx context.Context, user User) (err error) } // HTTP request: // $ http POST /users name=tracey age=1
-
Specified as
-:type User struct { Name string `kok:"name"` Age int `kok:"age"` Hobbies []string `kok:"hobby"` } type Service interface { // @kok(op): POST /users // @kok(body): - CreateUser(ctx context.Context, user User) (err error) } // HTTP request: // $ http POST /users?name=tracey&age=1&hobby=music&hobby=sport
-
Define the success HTTP response
-
Key:
@kok(success) -
Value:
statusCode:<statusCode>,body:<body>- statusCode: The status code of the success HTTP response.
- Optional: Defaults to 200 if not specified.
- body: The name of the response field whose value is mapped to the HTTP response body.
- Optional: When omitted, a struct containing all the results (except error) will automatically be mapped to the HTTP response body.
- statusCode: The status code of the success HTTP response.
-
Example:
type User struct { Name string `json:"name"` Age int `json:"age"` } type Service interface { // @kok(op): POST /users // @kok(success): statusCode:201,body:user CreateUser(ctx context.Context) (user User, err error) }
See the HTTP Codec interface.
Also see here for examples.
See the OAS Schema interface.
Checkout the Godoc.