moving API to api package, swagger annotatoins

This commit is contained in:
Brad Rydzewski 2016-03-30 13:15:28 -07:00
parent e64ec7cf88
commit 0d04fa67e3
15 changed files with 249 additions and 159 deletions

2
.gitignore vendored
View file

@ -13,6 +13,8 @@ drone_*
.env
temp/
api/swagger/files/*
# vendored repositories that we don't actually need
# to vendor. so exclude them

1
api/build.go Normal file
View file

@ -0,0 +1 @@
package api

16
api/doc.go Normal file
View file

@ -0,0 +1,16 @@
// Package classification Drone API.
//
// Schemes: http, https
// BasePath: /api
// Version: 1.0.0
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// swagger:meta
package api
//go:generate swagger generate spec -o swagger/files/swagger.json

View file

@ -1,4 +1,4 @@
package controller
package api
import (
"net/http"
@ -8,8 +8,6 @@ import (
"github.com/drone/drone/model"
"github.com/drone/drone/router/middleware/context"
"github.com/drone/drone/router/middleware/session"
"github.com/drone/drone/shared/token"
"github.com/drone/drone/store"
)
@ -22,13 +20,6 @@ func GetNodes(c *gin.Context) {
}
}
func ShowNodes(c *gin.Context) {
user := session.User(c)
nodes, _ := store.GetNodeList(c)
token, _ := token.New(token.CsrfToken, user.Login).Sign(user.Hash)
c.HTML(http.StatusOK, "nodes.html", gin.H{"User": user, "Nodes": nodes, "Csrf": token})
}
func GetNode(c *gin.Context) {
}

View file

@ -1,4 +1,4 @@
package controller
package api
import (
"bytes"
@ -150,11 +150,11 @@ func PatchRepo(c *gin.Context) {
return
}
c.IndentedJSON(http.StatusOK, repo)
c.JSON(http.StatusOK, repo)
}
func GetRepo(c *gin.Context) {
c.IndentedJSON(http.StatusOK, session.Repo(c))
c.JSON(http.StatusOK, session.Repo(c))
}
func GetRepoKey(c *gin.Context) {

3
api/swagger/swagger.go Normal file
View file

@ -0,0 +1,3 @@
package swagger
//go:generate go-bindata -pkg swagger -o swagger_gen.go files/

113
api/user.go Normal file
View file

@ -0,0 +1,113 @@
package api
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/drone/drone/cache"
"github.com/drone/drone/model"
"github.com/drone/drone/router/middleware/session"
"github.com/drone/drone/shared/token"
"github.com/drone/drone/store"
)
// swagger:route GET /user user getUser
//
// Get the currently authenticated user.
//
// Responses:
// 200: user
//
func GetSelf(c *gin.Context) {
c.JSON(200, session.User(c))
}
// swagger:route GET /user/feed user getUserFeed
//
// Get the currently authenticated user's build feed.
//
// Responses:
// 200: feed
//
func GetFeed(c *gin.Context) {
repos, err := cache.GetRepos(c, session.User(c))
if err != nil {
c.String(500, "Error fetching repository list. %s", err)
return
}
feed, err := store.GetUserFeed(c, repos)
if err != nil {
c.String(500, "Error fetching feed. %s", err)
return
}
c.JSON(200, feed)
}
// swagger:route GET /user/repos user getUserRepos
//
// Get the currently authenticated user's active repository list.
//
// Responses:
// 200: repos
//
func GetRepos(c *gin.Context) {
repos, err := cache.GetRepos(c, session.User(c))
if err != nil {
c.String(500, "Error fetching repository list. %s", err)
return
}
repos_, err := store.GetRepoListOf(c, repos)
if err != nil {
c.String(500, "Error fetching repository list. %s", err)
return
}
c.JSON(http.StatusOK, repos_)
}
func GetRemoteRepos(c *gin.Context) {
repos, err := cache.GetRepos(c, session.User(c))
if err != nil {
c.String(500, "Error fetching repository list. %s", err)
return
}
c.JSON(http.StatusOK, repos)
}
func PostToken(c *gin.Context) {
user := session.User(c)
token := token.New(token.UserToken, user.Login)
tokenstr, err := token.Sign(user.Hash)
if err != nil {
c.AbortWithError(http.StatusInternalServerError, err)
return
}
c.String(http.StatusOK, tokenstr)
}
// swagger:response user
type userResp struct {
// in: body
Body model.User
}
// swagger:response users
type usersResp struct {
// in: body
Body []model.User
}
// swagger:response feed
type feedResp struct {
// in: body
Body []model.Feed
}
// swagger:response repos
type reposResp struct {
// in: body
Body []model.Repo
}

View file

@ -1,4 +1,4 @@
package controller
package api
import (
"net/http"
@ -6,33 +6,43 @@ import (
"github.com/gin-gonic/gin"
"github.com/drone/drone/model"
"github.com/drone/drone/router/middleware/session"
"github.com/drone/drone/shared/crypto"
"github.com/drone/drone/store"
)
// swagger:route GET /users user getUserList
//
// Get the list of all registered users.
//
// Responses:
// 200: user
//
func GetUsers(c *gin.Context) {
users, err := store.GetUserList(c)
if err != nil {
c.AbortWithStatus(http.StatusInternalServerError)
return
c.String(500, "Error getting user list. %s", err)
} else {
c.JSON(200, users)
}
c.IndentedJSON(http.StatusOK, users)
}
// swagger:route GET /users/{login} user getUserLogin
//
// Get the user with the matching login.
//
// Responses:
// 200: user
//
func GetUser(c *gin.Context) {
user, err := store.GetUserLogin(c, c.Param("login"))
if err != nil {
c.AbortWithStatus(http.StatusNotFound)
return
c.String(404, "Cannot find user. %s", err)
} else {
c.JSON(200, user)
}
c.IndentedJSON(http.StatusOK, user)
}
func PatchUser(c *gin.Context) {
me := session.User(c)
in := &model.User{}
err := c.Bind(in)
if err != nil {
@ -48,19 +58,13 @@ func PatchUser(c *gin.Context) {
user.Admin = in.Admin
user.Active = in.Active
// cannot update self
if me.ID == user.ID {
c.AbortWithStatus(http.StatusForbidden)
return
}
err = store.UpdateUser(c, user)
if err != nil {
c.AbortWithStatus(http.StatusConflict)
return
}
c.IndentedJSON(http.StatusOK, user)
c.JSON(http.StatusOK, user)
}
func PostUser(c *gin.Context) {
@ -85,29 +89,25 @@ func PostUser(c *gin.Context) {
return
}
c.IndentedJSON(http.StatusOK, user)
c.JSON(http.StatusOK, user)
}
// swagger:route DELETE /users/{login} user deleteUserLogin
//
// Delete the user with the matching login.
//
// Responses:
// 200: user
//
func DeleteUser(c *gin.Context) {
me := session.User(c)
user, err := store.GetUserLogin(c, c.Param("login"))
if err != nil {
c.AbortWithStatus(http.StatusNotFound)
c.String(404, "Cannot find user. %s", err)
return
}
// cannot delete self
if me.ID == user.ID {
c.AbortWithStatus(http.StatusForbidden)
return
if err = store.DeleteUser(c, user); err != nil {
c.String(500, "Error deleting user. %s", err)
} else {
c.String(200, "")
}
err = store.DeleteUser(c, user)
if err != nil {
c.AbortWithStatus(http.StatusInternalServerError)
return
}
c.Writer.WriteHeader(http.StatusNoContent)
}

View file

@ -231,10 +231,10 @@ func PostBuild(c *gin.Context) {
event := c.DefaultQuery("event", build.Event)
if event == model.EventPush ||
event == model.EventPull ||
event == model.EventTag ||
event == model.EventDeploy {
build.Event = event
event == model.EventPull ||
event == model.EventTag ||
event == model.EventDeploy {
build.Event = event
}
build.Deploy = c.DefaultQuery("deploy_to", build.Deploy)
}

View file

@ -200,3 +200,10 @@ func ShowBuild(c *gin.Context) {
"Csrf": csrf,
})
}
func ShowNodes(c *gin.Context) {
user := session.User(c)
nodes, _ := store.GetNodeList(c)
token, _ := token.New(token.CsrfToken, user.Login).Sign(user.Hash)
c.HTML(http.StatusOK, "nodes.html", gin.H{"User": user, "Nodes": nodes, "Csrf": token})
}

View file

@ -1,78 +0,0 @@
package controller
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/drone/drone/cache"
"github.com/drone/drone/router/middleware/session"
"github.com/drone/drone/shared/token"
"github.com/drone/drone/store"
)
func GetSelf(c *gin.Context) {
c.IndentedJSON(200, session.User(c))
}
func GetFeed(c *gin.Context) {
user := session.User(c)
// get the repository list from the cache
repos, err := cache.GetRepos(c, user)
if err != nil {
c.String(400, err.Error())
return
}
feed, err := store.GetUserFeed(c, repos)
if err != nil {
c.String(400, err.Error())
return
}
c.JSON(200, feed)
}
func GetRepos(c *gin.Context) {
user := session.User(c)
repos, err := cache.GetRepos(c, user)
if err != nil {
c.AbortWithStatus(http.StatusInternalServerError)
return
}
// for each repository in the remote system we get
// the intersection of those repostiories in Drone
repos_, err := store.GetRepoListOf(c, repos)
if err != nil {
c.AbortWithStatus(http.StatusInternalServerError)
return
}
c.IndentedJSON(http.StatusOK, repos_)
}
func GetRemoteRepos(c *gin.Context) {
user := session.User(c)
repos, err := cache.GetRepos(c, user)
if err != nil {
c.AbortWithStatus(http.StatusInternalServerError)
return
}
c.IndentedJSON(http.StatusOK, repos)
}
func PostToken(c *gin.Context) {
user := session.User(c)
token := token.New(token.UserToken, user.Login)
tokenstr, err := token.Sign(user.Hash)
if err != nil {
c.AbortWithError(http.StatusInternalServerError, err)
} else {
c.String(http.StatusOK, tokenstr)
}
}

View file

@ -1,5 +1,8 @@
package model
// Feed represents an item in the user's feed or timeline.
//
// swagger:model feed
type Feed struct {
Owner string `json:"owner" meddler:"repo_owner"`
Name string `json:"name" meddler:"repo_name"`

View file

@ -7,6 +7,9 @@ type RepoLite struct {
Avatar string `json:"avatar_url"`
}
// Repo represents a repository.
//
// swagger:model repo
type Repo struct {
ID int64 `json:"id" meddler:"repo_id,pk"`
UserID int64 `json:"-" meddler:"repo_user_id"`

View file

@ -1,14 +1,42 @@
package model
// User represents a registered user.
//
// swagger:model user
type User struct {
ID int64 `json:"id" meddler:"user_id,pk"`
Login string `json:"login" meddler:"user_login"`
Token string `json:"-" meddler:"user_token"`
Secret string `json:"-" meddler:"user_secret"`
Expiry int64 `json:"-" meddler:"user_expiry"`
Email string `json:"email" meddler:"user_email"`
// the id for this user.
//
// required: true
ID int64 `json:"id" meddler:"user_id,pk"`
// Login is the username for this user.
//
// required: true
Login string `json:"login" meddler:"user_login"`
// Token is the oauth2 token.
Token string `json:"-" meddler:"user_token"`
// Secret is the oauth2 token secret.
Secret string `json:"-" meddler:"user_secret"`
// Expiry is the token and secret expriation timestamp.
Expiry int64 `json:"-" meddler:"user_expiry"`
// Email is the email address for this user.
//
// required: true
Email string `json:"email" meddler:"user_email"`
// the avatar url for this user.
Avatar string `json:"avatar_url" meddler:"user_avatar"`
Active bool `json:"active," meddler:"user_active"`
Admin bool `json:"admin," meddler:"user_admin"`
Hash string `json:"-" meddler:"user_hash"`
// Activate indicates the user is active in the system.
Active bool `json:"active," meddler:"user_active"`
// Admin indicates the user is a system administrator.
Admin bool `json:"admin," meddler:"user_admin"`
// Hash is a unique token used to sign tokens.
Hash string `json:"-" meddler:"user_hash"`
}

View file

@ -6,6 +6,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/drone/drone/api"
"github.com/drone/drone/controller"
"github.com/drone/drone/router/middleware/header"
"github.com/drone/drone/router/middleware/location"
@ -60,34 +61,34 @@ func Load(middleware ...gin.HandlerFunc) http.Handler {
user := e.Group("/api/user")
{
user.Use(session.MustUser())
user.GET("", controller.GetSelf)
user.GET("/feed", controller.GetFeed)
user.GET("/repos", controller.GetRepos)
user.GET("/repos/remote", controller.GetRemoteRepos)
user.POST("/token", controller.PostToken)
user.GET("", api.GetSelf)
user.GET("/feed", api.GetFeed)
user.GET("/repos", api.GetRepos)
user.GET("/repos/remote", api.GetRemoteRepos)
user.POST("/token", api.PostToken)
}
users := e.Group("/api/users")
{
users.Use(session.MustAdmin())
users.GET("", controller.GetUsers)
users.POST("", controller.PostUser)
users.GET("/:login", controller.GetUser)
users.PATCH("/:login", controller.PatchUser)
users.DELETE("/:login", controller.DeleteUser)
users.GET("", api.GetUsers)
users.POST("", api.PostUser)
users.GET("/:login", api.GetUser)
users.PATCH("/:login", api.PatchUser)
users.DELETE("/:login", api.DeleteUser)
}
nodes := e.Group("/api/nodes")
{
nodes.Use(session.MustAdmin())
nodes.GET("", controller.GetNodes)
nodes.POST("", controller.PostNode)
nodes.DELETE("/:node", controller.DeleteNode)
nodes.GET("", api.GetNodes)
nodes.POST("", api.PostNode)
nodes.DELETE("/:node", api.DeleteNode)
}
repos := e.Group("/api/repos/:owner/:name")
{
repos.POST("", controller.PostRepo)
repos.POST("", api.PostRepo)
repo := repos.Group("")
{
@ -95,19 +96,19 @@ func Load(middleware ...gin.HandlerFunc) http.Handler {
repo.Use(session.SetPerm())
repo.Use(session.MustPull)
repo.GET("", controller.GetRepo)
repo.GET("/key", controller.GetRepoKey)
repo.POST("/key", controller.PostRepoKey)
repo.GET("", api.GetRepo)
repo.GET("/key", api.GetRepoKey)
repo.POST("/key", api.PostRepoKey)
repo.GET("/builds", controller.GetBuilds)
repo.GET("/builds/:number", controller.GetBuild)
repo.GET("/logs/:number/:job", controller.GetBuildLogs)
// requires authenticated user
repo.POST("/encrypt", session.MustUser(), controller.PostSecure)
repo.POST("/encrypt", session.MustUser(), api.PostSecure)
// requires push permissions
repo.PATCH("", session.MustPush, controller.PatchRepo)
repo.DELETE("", session.MustPush, controller.DeleteRepo)
repo.PATCH("", session.MustPush, api.PatchRepo)
repo.DELETE("", session.MustPush, api.DeleteRepo)
repo.POST("/builds/:number", session.MustPush, controller.PostBuild)
repo.DELETE("/builds/:number/:job", session.MustPush, controller.DeleteBuild)