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

chore(*): best practice for api definition #5

Merged
merged 5 commits into from
Apr 28, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/caicloud/nirvana"
"github.com/caicloud/nirvana/log"
"github.com/caicloud/nirvana/plugins/reqlog"
"github.com/spf13/pflag"

"github.com/caicloud/nirvana-practice/pkg/apis"
Expand All @@ -30,10 +31,11 @@ func main() {
}

// initialize Server config
config := nirvana.NewDefaultConfig().Configure(nirvana.Port(httpPort))

// install APIs
apis.Install(config)
config := nirvana.NewDefaultConfig().Configure(
nirvana.Port(httpPort),
reqlog.Default(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enabling reqlog at base level might not be a good idea; best adding it at /api so that no log will be printed for /healthz and /metircs

Copy link
Member Author

@supereagle supereagle Apr 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe reqlog can only be applied to base level. If we want to add it to /api level, we need to write a log middleware by ourselves.

Please confirm @iawia002

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm standing with @lichuan0620, we don't need to log every request such as /healthz and /metrics, it's useless. We recommend logging only helpful request such as /api/*

Copy link
Member Author

@supereagle supereagle Apr 27, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have used the log middleware in go-common.

nirvana.Descriptor(apis.Descriptor()),
)

// create the server and server
server := nirvana.NewServer(config)
Expand Down
18 changes: 18 additions & 0 deletions nirvana.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# This file describes your project. It's used to generate api docs and
# clients. All fields in this file won't affect nirvana configurations.

project: nirvana-practice-server
description: This is a practice project which using Nirvana API framework
schemes:
- http
hosts:
- localhost:8080
contacts:
- name: Owl
email: [email protected]
description: Maintainer this project
versions:
- name: v1alpha1
description: The v1alpha1 version of this project
rules:
- prefix: /api/v1alpha1/
Comment on lines +15 to +18
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can add more versions if necessary.

22 changes: 22 additions & 0 deletions pkg/apis/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// +nirvana:api=descriptors:"Descriptor"

package apis

import (
v1alpha1 "github.com/caicloud/nirvana-practice/pkg/apis/v1alpha1/descriptors"

def "github.com/caicloud/nirvana/definition"
)

// Descriptor returns a combined descriptor for APIs of all versions.
func Descriptor() def.Descriptor {
return def.Descriptor{
Description: "APIs",
Path: "/api",
Consumes: []string{def.MIMEJSON},
Produces: []string{def.MIMEJSON},
Children: []def.Descriptor{
v1alpha1.Descriptor(),
},
}
}
38 changes: 0 additions & 38 deletions pkg/apis/install.go

This file was deleted.

26 changes: 26 additions & 0 deletions pkg/apis/v1alpha1/customers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package v1alpha1

import (
"time"

meta "github.com/caicloud/nirvana-practice/pkg/apis/meta/v1"
)

type Customer struct {
meta.Metadata `json:",inline"`
Spec *CustomerSpec `json:"spec,omitempty"`
Status *CustomerStatus `json:"status,omitempty"`
}

type CustomerSpec struct {
Sex string `json:"sex,omitempty"`
}

type CustomerStatus struct {
LatestLoginTimestamp *time.Time `json:"latestLoginTimestamp,omitempty"`
}

type CustomersList struct {
Total int `json:"total"`
Items []*Customer `json:"items,omitempty"`
}
84 changes: 84 additions & 0 deletions pkg/apis/v1alpha1/descriptors/customers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package descriptors

import (
"github.com/caicloud/nirvana/definition"
"github.com/caicloud/nirvana/operators/validator"

meta "github.com/caicloud/nirvana-practice/pkg/apis/meta/v1"
"github.com/caicloud/nirvana-practice/pkg/apis/v1alpha1/handlers"
)

func init() {
register(customers...)
}

var customers = []definition.Descriptor{
{
Path: "/customers",
Description: "Customer API",
Tags: []string{"customer"},
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add tags to classify APIs.

Definitions: []definition.Definition{listCustomers, createCustomer},
Children: []definition.Descriptor{
{
Path: "/{customer}",
Definitions: []definition.Definition{getCustomer, updateCustomer, deleteCustomer},
},
},
},
}

var listCustomers = definition.Definition{
Method: definition.List,
Description: "list customers",
Function: handlers.ListCustomers,
Parameters: []definition.Parameter{
{
Source: definition.Auto,
Name: "options",
Description: "generic list options",
Operators: []definition.Operator{validator.Struct(&meta.ListOptions{})},
},
},
Results: definition.DataErrorResults("listed customers"),
}

var createCustomer = definition.Definition{
Method: definition.Create,
Description: "create customer",
Function: handlers.CreateCustomer,
Parameters: []definition.Parameter{
definition.BodyParameterFor("JSON body to describe the new customer"),
},
Results: definition.DataErrorResults("customer"),
}

var getCustomer = definition.Definition{
Method: definition.Get,
Description: "get customer",
Function: handlers.GetCustomer,
Parameters: []definition.Parameter{
definition.PathParameterFor("customer", "name of the customer to get"),
},
Results: definition.DataErrorResults("customer"),
}

var updateCustomer = definition.Definition{
Method: definition.Update,
Description: "update customer",
Function: handlers.UpdateCustomer,
Parameters: []definition.Parameter{
definition.PathParameterFor("customer", "name of the customer to update"),
definition.BodyParameterFor("JSON body to describe the new customer"),
},
Results: definition.DataErrorResults("customer"),
}

var deleteCustomer = definition.Definition{
Method: definition.Delete,
Description: "delete customer",
Function: handlers.DeleteCustomer,
Parameters: []definition.Parameter{
definition.PathParameterFor("customer", "name of the customer to delete"),
},
Results: []definition.Result{definition.ErrorResult()},
}
22 changes: 22 additions & 0 deletions pkg/apis/v1alpha1/descriptors/install.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package descriptors

import (
"github.com/caicloud/nirvana/definition"
)

var descriptors []definition.Descriptor

func register(ds ...definition.Descriptor) {
descriptors = append(descriptors, ds...)
}

// Descriptor returns a combined descriptor for current version.
func Descriptor() definition.Descriptor {
return definition.Descriptor{
Description: "v1alpha1 API",
Path: "/v1alpha1",
Consumes: []string{definition.MIMEJSON},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unless not all APIs are JSON, those should be added only at /api level

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add comments to illustrate that other content types can be added if necessary.

Produces: []string{definition.MIMEJSON},
Children: descriptors,
}
}
106 changes: 69 additions & 37 deletions pkg/apis/v1alpha1/descriptors/products.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,45 +8,77 @@ import (
"github.com/caicloud/nirvana-practice/pkg/apis/v1alpha1/handlers"
)

// ProductDescriptor builds and returns a Descriptor for all Product APIs.
func ProductDescriptor() definition.Descriptor {
return definition.Descriptor{
Path: "/products",
Definitions: []definition.Definition{
{
Method: definition.List,
Function: handlers.ListProducts,
Parameters: []definition.Parameter{
{
Source: definition.Auto,
Name: "options",
Description: "generic list options",
Operators: []definition.Operator{validator.Struct(&meta.ListOptions{})},
},
},
Results: definition.DataErrorResults("listed products"),
Description: "list products",
},
// TODO
},
func init() {
register(products...)
}

var products = []definition.Descriptor{
{
Path: "/products",
Description: "Product API",
Tags: []string{"product"},
Definitions: []definition.Definition{listProducts, createProduct},
Children: []definition.Descriptor{
{
Path: "/{product}",
Definitions: []definition.Definition{
{
Method: definition.Get,
Function: handlers.GetProduct,
Parameters: []definition.Parameter{
definition.PathParameterFor("product", "name of the product to get"),
},
Results: definition.DataErrorResults("the get result (or error)"),
Description: "get product",
},
// TODO
},
Description: "single-target Product APIs",
Path: "/{product}",
Definitions: []definition.Definition{getProduct, updateProduct, deleteProduct},
},
},
Description: "all Product APIs",
}
},
}

var listProducts = definition.Definition{
Method: definition.List,
Description: "list products",
Function: handlers.ListProducts,
Parameters: []definition.Parameter{
{
Source: definition.Auto,
Name: "options",
Description: "generic list options",
Operators: []definition.Operator{validator.Struct(&meta.ListOptions{})},
},
},
Results: definition.DataErrorResults("listed products"),
}

var createProduct = definition.Definition{
Method: definition.Create,
Description: "create product",
Function: handlers.CreateProduct,
Parameters: []definition.Parameter{
definition.BodyParameterFor("JSON body to describe the new product"),
},
Results: definition.DataErrorResults("product"),
}

var getProduct = definition.Definition{
Method: definition.Get,
Description: "get product",
Function: handlers.GetProduct,
Parameters: []definition.Parameter{
definition.PathParameterFor("product", "name of the product to get"),
},
Results: definition.DataErrorResults("product"),
}

var updateProduct = definition.Definition{
Method: definition.Update,
Description: "update product",
Function: handlers.UpdateProduct,
Parameters: []definition.Parameter{
definition.PathParameterFor("product", "name of the product to update"),
definition.BodyParameterFor("JSON body to describe the new product"),
},
Results: definition.DataErrorResults("product"),
}

var deleteProduct = definition.Definition{
Method: definition.Delete,
Description: "delete product",
Function: handlers.DeleteProduct,
Parameters: []definition.Parameter{
definition.PathParameterFor("product", "name of the product to delete"),
},
Results: []definition.Result{definition.ErrorResult()},
}
Loading