Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redesign the annotation syntax #18

Closed
RussellLuo opened this issue Oct 31, 2021 · 0 comments · Fixed by #19
Closed

Redesign the annotation syntax #18

RussellLuo opened this issue Oct 31, 2021 · 0 comments · Fixed by #19

Comments

@RussellLuo
Copy link
Owner

RussellLuo commented Oct 31, 2021

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant