Skip to content

Commit

Permalink
Path repair
Browse files Browse the repository at this point in the history
  • Loading branch information
coca committed May 9, 2023
1 parent a161aa7 commit 28b3742
Show file tree
Hide file tree
Showing 18 changed files with 227 additions and 163 deletions.
10 changes: 5 additions & 5 deletions README-zh_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Crud 是一个帮助编写增查改删(CRUD)服务的 golang 模块。有了
0. 安装:

```sh
go get -u github.com/cdfmlr/crud
go get -u github.com/tqrj/crud
```

1. 然后你只需要定义模型,并在 orm 和 router 中注册它们:
Expand All @@ -26,8 +26,8 @@ go get -u github.com/cdfmlr/crud
package main

import (
"github.com/cdfmlr/crud/orm"
"github.com/cdfmlr/crud/router"
"github.com/tqrj/crud/orm"
"github.com/tqrj/crud/router"
)

type Todo struct {
Expand Down Expand Up @@ -155,11 +155,11 @@ DELETE /projects/:ProjectID/todos/:TodoID # delete an associated relationship

**文档**:

- [go doc](https://pkg.go.dev/github.com/cdfmlr/crud)
- [go doc](https://pkg.go.dev/github.com/tqrj/crud)

**实例**:

- [sshman](https://github.com/cdfmlr/sshman) 是一个更真实的例子,说明crud如何帮助你快速、轻松地建立一个真实世界的 CRUD REST API 项目。请务必看一下。
- [sshman](https://github.com/tqrj/sshman) 是一个更真实的例子,说明crud如何帮助你快速、轻松地建立一个真实世界的 CRUD REST API 项目。请务必看一下。

## 它是如何工作的

Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

English | [机翻中文](README-zh_CN.md)

[![Go Reference Badge](https://pkg.go.dev/badge/github.com/cdfmlr/crud.svg)](https://pkg.go.dev/github.com/cdfmlr/crud) [![Go Report Badge](https://goreportcard.com/badge/github.com/cdfmlr/crud)](https://goreportcard.com/report/github.com/cdfmlr/crud)
[![Go Reference Badge](https://pkg.go.dev/badge/github.com/tqrj/crud.svg)](https://pkg.go.dev/github.com/tqrj/crud) [![Go Report Badge](https://goreportcard.com/badge/github.com/tqrj/crud)](https://goreportcard.com/report/github.com/tqrj/crud)

Crud is a golang package that helps writing CRUD servers.
With this package, all you need is models,
Expand All @@ -30,8 +30,8 @@ go get -u github.com/tqrj/crud
package main

import (
"github.com/cdfmlr/crud/orm"
"github.com/cdfmlr/crud/router"
"github.com/tqrj/crud/orm"
"github.com/tqrj/crud/router"
)

type Todo struct {
Expand Down Expand Up @@ -180,11 +180,11 @@ to build your own CRUD API services:

**Documents**:

- [go doc](https://pkg.go.dev/github.com/cdfmlr/crud)
- [go doc](https://pkg.go.dev/github.com/tqrj/crud)

**Examples**:

- [sshman](https://github.com/cdfmlr/sshman) is a more real world example of how
- [sshman](https://github.com/tqrj/sshman) is a more real world example of how
crud can help you build a CRUD REST API project fast and easily. Please check
it out.

Expand Down
29 changes: 16 additions & 13 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/cdfmlr/crud/log"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
"github.com/tqrj/crud/log"
"reflect"
"strings"
)

var logger = log.ZoneLogger("crud/config")

// Init reads configure, write values into given configModel
// - FromFile: read config from file
// - FromEnv: read config from environment variables
// - WatchFileChange: watch config file and reload config when changed
// - FromFile: read config from file
// - FromEnv: read config from environment variables
// - WatchFileChange: watch config file and reload config when changed
func Init(configModel any, options ...Option) error {
for _, option := range options {
err := option(configModel)
Expand Down Expand Up @@ -46,8 +46,9 @@ func FromFile(path string) Option {
}

// WatchFileChange works with FromFile:
// var config MyConfig
// config.Init(&config, FromFile(path), WatchFileChange(hook))
//
// var config MyConfig
// config.Init(&config, FromFile(path), WatchFileChange(hook))
//
// WatchFileChange watch current viper config file,
// and reload config when changed.
Expand All @@ -69,13 +70,15 @@ func WatchFileChange(hook func(oldConfig any, newConfig any)) Option {

// FromEnv reads config from environment variables, and unmarshal to config
// prefix is the prefix of environment variables:
// type MyConfig struct {
// Foo struct {
// Bar string
// }
// }
// config := MyConfig{}
// config.Init(&config, FromEnv("MYAPP"))
//
// type MyConfig struct {
// Foo struct {
// Bar string
// }
// }
// config := MyConfig{}
// config.Init(&config, FromEnv("MYAPP"))
//
// will read `config.Foo.Bar` from env `MYAPP_FOO_BAR`.
func FromEnv(prefix string) Option {
return func(config any) error {
Expand Down
11 changes: 9 additions & 2 deletions controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,19 @@
// is actually a gin.HandlerFunc:
//
// - GET /models => GetListHandler[Model]: to retrieve a model list
//
// - GET /models/:id => GetByIDHandler[Model]: to retrieve a model by id
//
// - POST /models => CreateHandler[Model] : to create a new model
//
// - PUT /models/:id => UpdateHandler[Model] : to update an existing model
//
// - DELETE /models/:id => DeleteHandler[Model] : to delete an existing model
//
// - GET /models/:id/field => GetFieldHandler[Model] : to retrieve a field (nested model) of a model
//
// - POST /models/:id/field => CreateNestedHandler[Model] : to create a nested model (association)
//
// - DELETE /models/:id/field => DeleteNestedHandler[Model] : to delete an association record
//
// The controller are all generic functions, which is available in Go 1.18 and
Expand All @@ -21,12 +27,13 @@
// go have no way to infer them.
//
// Notice that there is not a UpdateNestedHandler, because:
// PUT /models/:id/field/:id == PUT /field/:id
//
// PUT /models/:id/field/:id == PUT /field/:id
//
// [Gin]: https://github.com/gin-gonic/gin
// [Go generics tutorial]: https://go.dev/doc/tutorial/generics
package controller

import "github.com/cdfmlr/crud/log"
import "github.com/tqrj/crud/log"

var logger = log.ZoneLogger("crud/controller")
35 changes: 20 additions & 15 deletions controller/create.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
package controller

import (
"github.com/cdfmlr/crud/orm"
"github.com/cdfmlr/crud/service"
"github.com/gin-gonic/gin"
"github.com/tqrj/crud/orm"
"github.com/tqrj/crud/service"
"reflect"
)

// CreateHandler handles
// POST /T
//
// POST /T
//
// creates a new model T, responds with the created model T if successful.
//
// Request body:
// - {...} // fields of the model T
// - {...} // fields of the model T
//
// Response:
// - 200 OK: { T: {...} }
// - 400 Bad Request: { error: "request band failed" }
// - 422 Unprocessable Entity: { error: "create process failed" }
// - 200 OK: { T: {...} }
// - 400 Bad Request: { error: "request band failed" }
// - 422 Unprocessable Entity: { error: "create process failed" }
func CreateHandler[T any]() gin.HandlerFunc {
return func(c *gin.Context) {
var model T
Expand All @@ -40,20 +42,23 @@ func CreateHandler[T any]() gin.HandlerFunc {
}

// CreateNestedHandler handles
// POST /P/:parentIDRouteParam/T
//
// POST /P/:parentIDRouteParam/T
//
// where:
// - P is the parent model, T is the child model
// - parentIDRouteParam is the route param name of the parent model P
// - field is the field name of the child model T in the parent model P
// - P is the parent model, T is the child model
// - parentIDRouteParam is the route param name of the parent model P
// - field is the field name of the child model T in the parent model P
//
// responds with the updated parent model P
//
// Request body:
// - {...} // fields of the child model T
// - {...} // fields of the child model T
//
// Response:
// - 200 OK: { P: {...} }
// - 400 Bad Request: { error: "request band failed" }
// - 422 Unprocessable Entity: { error: "create process failed" }
// - 200 OK: { P: {...} }
// - 400 Bad Request: { error: "request band failed" }
// - 422 Unprocessable Entity: { error: "create process failed" }
func CreateNestedHandler[P orm.Model, T orm.Model](parentIDRouteParam string, field string) gin.HandlerFunc {
return func(c *gin.Context) {
parentID := c.Param(parentIDRouteParam)
Expand Down
32 changes: 18 additions & 14 deletions controller/delete.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
package controller

import (
"github.com/cdfmlr/crud/orm"
"github.com/cdfmlr/crud/service"
"github.com/gin-gonic/gin"
"github.com/tqrj/crud/orm"
"github.com/tqrj/crud/service"
)

// DeleteHandler handles
// DELETE /T/:idParam
//
// DELETE /T/:idParam
//
// Deletes the model T with the given id.
//
// Request body: none
//
// Response:
// - 200 OK: { deleted: true }
// - 400 Bad Request: { error: "missing id" }
// - 422 Unprocessable Entity: { error: "delete process failed" }
// - 200 OK: { deleted: true }
// - 400 Bad Request: { error: "missing id" }
// - 422 Unprocessable Entity: { error: "delete process failed" }
func DeleteHandler[T orm.Model](idParam string) gin.HandlerFunc {
return func(c *gin.Context) {
id := c.Param(idParam)
Expand All @@ -39,19 +41,21 @@ func DeleteHandler[T orm.Model](idParam string) gin.HandlerFunc {
}

// DeleteNestedHandler handles
// DELETE /P/:parentIdParam/T/:childIdParam
//
// DELETE /P/:parentIdParam/T/:childIdParam
//
// where:
// - P is the parent model, T is the child model
// - parentIdParam is the route param name of the parent model P
// - childIdParam is the route param name of the child model T in the parent model P
// - field is the field name of the child model T in the parent model P
// - P is the parent model, T is the child model
// - parentIdParam is the route param name of the parent model P
// - childIdParam is the route param name of the child model T in the parent model P
// - field is the field name of the child model T in the parent model P
//
// Request body: none
//
// Response:
// - 200 OK: { deleted: true }
// - 400 Bad Request: { error: "missing id" }
// - 422 Unprocessable Entity: { error: "delete process failed" }
// - 200 OK: { deleted: true }
// - 400 Bad Request: { error: "missing id" }
// - 422 Unprocessable Entity: { error: "delete process failed" }
func DeleteNestedHandler[P orm.Model, T orm.Model](parentIdParam string, field string, childIdParam string) gin.HandlerFunc {
return func(c *gin.Context) {
parentId := c.Param(parentIdParam)
Expand Down
53 changes: 31 additions & 22 deletions controller/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@ package controller

import (
"context"
"github.com/cdfmlr/crud/orm"
"github.com/cdfmlr/crud/service"
"github.com/gin-gonic/gin"
"github.com/tqrj/crud/orm"
"github.com/tqrj/crud/service"
"reflect"
)

// GetRequestOptions is the query options (?opt=val) for GET requests:
//
// limit=10&offset=4& # pagination
// order_by=id&desc=true& # ordering
// filter_by=name&filter_value=John& # filtering
// total=true& # return total count (all available records under the filter, ignoring pagination)
// preload=Product&preload=Product.Manufacturer # preloading: loads nested models as well
// limit=10&offset=4& # pagination
// order_by=id&desc=true& # ordering
// filter_by=name&filter_value=John& # filtering
// total=true& # return total count (all available records under the filter, ignoring pagination)
// preload=Product&preload=Product.Manufacturer # preloading: loads nested models as well
//
// It is used in GetListHandler, GetByIDHandler and GetFieldHandler, to bind
// the query parameters in the GET request url.
Expand All @@ -30,16 +30,19 @@ type GetRequestOptions struct {
}

// GetListHandler handles
// GET /T
//
// GET /T
//
// It returns a list of models.
//
// QueryOptions (See GetRequestOptions for more details):
// limit, offset, order_by, desc, filter_by, filter_value, preload, total.
//
// limit, offset, order_by, desc, filter_by, filter_value, preload, total.
//
// Response:
// - 200 OK: { Ts: [{...}, ...] }
// - 400 Bad Request: { error: "request band failed" }
// - 422 Unprocessable Entity: { error: "get process failed" }
// - 200 OK: { Ts: [{...}, ...] }
// - 400 Bad Request: { error: "request band failed" }
// - 422 Unprocessable Entity: { error: "get process failed" }
func GetListHandler[T any]() gin.HandlerFunc {
return func(c *gin.Context) {
var request GetRequestOptions
Expand Down Expand Up @@ -77,14 +80,15 @@ func GetListHandler[T any]() gin.HandlerFunc {
}

// GetByIDHandler handles
// GET /T/:idParam
//
// GET /T/:idParam
//
// QueryOptions (See GetRequestOptions for more details): preload
//
// Response:
// - 200 OK: { T: {...} }
// - 400 Bad Request: { error: "request band failed" }
// - 422 Unprocessable Entity: { error: "get process failed" }
// - 200 OK: { T: {...} }
// - 400 Bad Request: { error: "request band failed" }
// - 422 Unprocessable Entity: { error: "get process failed" }
func GetByIDHandler[T orm.Model](idParam string) gin.HandlerFunc {
return func(c *gin.Context) {
var request GetRequestOptions
Expand All @@ -109,18 +113,23 @@ func GetByIDHandler[T orm.Model](idParam string) gin.HandlerFunc {
}

// GetFieldHandler handles
// GET /T/:idParam/field
//
// GET /T/:idParam/field
//
// QueryOptions (See GetRequestOptions for more details):
// limit, offset, order_by, desc, filter_by, filter_value, preload, total.
//
// limit, offset, order_by, desc, filter_by, filter_value, preload, total.
//
// Notice, all GetRequestOptions will be conditions for the field, for example:
// GET /user/123/order?preload=Product
//
// GET /user/123/order?preload=Product
//
// Preloads User.Order.Product instead of User.Product.
//
// Response:
// - 200 OK: { Fs: [{...}, ...] } // field models
// - 400 Bad Request: { error: "request band failed" }
// - 422 Unprocessable Entity: { error: "get process failed" }
// - 200 OK: { Fs: [{...}, ...] } // field models
// - 400 Bad Request: { error: "request band failed" }
// - 422 Unprocessable Entity: { error: "get process failed" }
func GetFieldHandler[T orm.Model](idParam string, field string) gin.HandlerFunc {
field = nameToField(field, *new(T))

Expand Down
Loading

0 comments on commit 28b3742

Please sign in to comment.