RussellLuo/kun

Redesign the annotation syntax

Closed this issue · 0 comments

Motivation

  1. Follow the Go conventions for comment directives (and this).
  2. Provide a unified annotation syntax for both comments and struct tags.
  3. Make it possible to apply Argument aggregation in a single-line annotation.
  4. Improve the comment syntax for manipulating HTTP request/response body fields.

Proposed annotation syntax

HTTP

  1. Define the HTTP operation

    • Directive: //kok:op

    • Arguments: <method> <pattern>

      • ...
    • Examples:

      type Service interface {
          //kok:op DELETE /users/{id}
          DeleteUser(ctx context.Context, id int) (err error)
      }
      
      // HTTP request:
      // $ http DELETE /users/101
  2. Define the HTTP request parameters

    • Directive: //kok:param
    • Arguments: <argName> [<parameter> [; <parameter2> [; ...]]]
      • parameter: in=<in> name=<name> required=<required> type=<type> descr=<descr>
        • ...
    • Examples:
      • 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:"in=path"`  // name defaults to snake case `id`
            Name string `kok:"in=query"` // name defaults to snake case `name`
            Age  int    `kok:"in=header name=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   // equivalent to `kok:"in=query name=name"`
            Age     int      // equivalent to `kok:"in=query name=age"`
            Hobbies []string // equivalent to `kok:"in=query name=hobbies"`
        }
        
        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; in=request name=RemoteAddr
            Log(ctx context.Context, ip net.IP) (err error)
        }
        
        // The equivalent annotations =>
        // (using backslash `\` for line continuation)
        
        type Service interface {
            //kok:op POST /logs
            //kok:param ip in=header name=X-Forwarded-For; \
            //             in=request name=RemoteAddr
            Log(ctx context.Context, ip net.IP) (err error)
        }
        
        // HTTP request:
        // $ http POST /logs
  3. Define the HTTP request body

    • Directive: //kok:body
    • Arguments: <field> or <manipulation> [; <manipulation2> [; ...]]
      • manipulation: <argName> name=<name> type=<type> descr=<descr>
        • ...
    • Examples:
      • 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
            Age     int
            Hobbies []string `kok:"name=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
  4. Define the success HTTP response

    • Directive: //kok:success

    • Arguments: statusCode=<statusCode> body=<body> manip=`<manipulation> [; <manipulation2> [; ...]]`

      • manipulation: <argName> name=<name> type=<type> descr=<descr>
        • ...
    • Examples:

      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)
      }
  5. Define the OAS metadata

    • Directive: //kok:oas

    • Arguments: <property>=<value>

    • Examples:

      // This is the API documentation of User.
      //kok:oas docsPath=/api-docs
      //kok:oas title=User API
      //kok:oas version=1.0.0
      //kok:oas basePath=/v1
      //kok:oas tags=user
      type Service interface {
          //kok:op POST /users
          CreateUser(ctx context.Context, name string, age int) (err error)
      }
  6. Define the annotation alias

    • Directive: //kok:alias

    • Arguments: <name>=`<value>`

    • Examples:

      type Service interface {
          //kok:op POST /users
          //kok:param operatorID in=header name=Authorization required=true
          CreateUser(ctx context.Context, operatorID int) (err error)
      
          //kok:op DELETE /users/{id}
          //kok:param operatorID in=header name=Authorization required=true
          DeleteUser(ctx context.Context, id, operatorID int) (err error)
      }
      
      // The equivalent annotations =>
      
      //kok:alias opID=`operatorID in=header name=Authorization required=true`
      type Service interface {
          //kok:op POST /users
          //kok:param $opID
          CreateUser(ctx context.Context, operatorID int) (err error)
      
          //kok:op DELETE /users/{id}
          //kok:param $opID
          DeleteUser(ctx context.Context, id, operatorID int) (err error)
      }

gRPC

  • Directive: //kok:grpc
  • Arguments: request=<request> response=<response>
  • Examples:
    • Omitted:

      type Service interface {
          //kok:grpc
          CreateUser(ctx context.Context, name string, age int) (err error)
      }
      
      // gRPC request:
      // $ grpcurl -d '{"name": "tracey", "age": 1}' ... pb.Service/CreateUser
    • Specified:

      type User struct {
          Name string `json:"name"`
          Age  int    `json:"age"`
      }
      
      type Service interface {
          //kok:grpc request=user
          CreateUser(ctx context.Context, user User) (err error)
      }
      
      // gRPC request:
      // $ grpcurl -d '{"name": "tracey", "age": 1}' ... pb.Service/CreateUser