From b22a18da6b69978cce0106584346608d759265f7 Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Sat, 10 Nov 2018 03:49:32 +0200 Subject: [PATCH 01/12] initialize support for versioning as requested, per route -- not finished yet Former-commit-id: ade66610125f06a0b5ce3e90bcafe349f216a616 --- versioning/deprecation.go | 36 ++++++++++++++ versioning/deprecation_test.go | 33 +++++++++++++ versioning/version.go | 72 ++++++++++++++++++++++++++++ versioning/version_test.go | 48 +++++++++++++++++++ versioning/versioning.go | 87 ++++++++++++++++++++++++++++++++++ versioning/versioning_test.go | 49 +++++++++++++++++++ 6 files changed, 325 insertions(+) create mode 100644 versioning/deprecation.go create mode 100644 versioning/deprecation_test.go create mode 100644 versioning/version.go create mode 100644 versioning/version_test.go create mode 100644 versioning/versioning.go create mode 100644 versioning/versioning_test.go diff --git a/versioning/deprecation.go b/versioning/deprecation.go new file mode 100644 index 00000000..afccc5f9 --- /dev/null +++ b/versioning/deprecation.go @@ -0,0 +1,36 @@ +package versioning + +import ( + "time" + + "github.com/kataras/iris/context" +) + +type DeprecationOptions struct { + WarnMessage string + DeprecationDate time.Time + DeprecationInfo string +} + +var DefaultDeprecationOptions = DeprecationOptions{ + WarnMessage: "WARNING! You are using a deprecated version of this API.", +} + +func Deprecated(handler context.Handler, options DeprecationOptions) context.Handler { + if options.WarnMessage == "" { + options.WarnMessage = DefaultDeprecationOptions.WarnMessage + } + + return func(ctx context.Context) { + handler(ctx) + ctx.Header("X-API-Warn", options.WarnMessage) + + if !options.DeprecationDate.IsZero() { + ctx.Header("X-API-Deprecation-Date", context.FormatTime(ctx, options.DeprecationDate)) + } + + if options.DeprecationInfo != "" { + ctx.Header("X-API-Deprecation-Info", options.DeprecationInfo) + } + } +} diff --git a/versioning/deprecation_test.go b/versioning/deprecation_test.go new file mode 100644 index 00000000..4cdcf83e --- /dev/null +++ b/versioning/deprecation_test.go @@ -0,0 +1,33 @@ +package versioning_test + +import ( + "testing" + "time" + + "github.com/kataras/iris" + "github.com/kataras/iris/httptest" + "github.com/kataras/iris/versioning" +) + +func TestDeprecated(t *testing.T) { + app := iris.New() + + writeVesion := func(ctx iris.Context) { + ctx.WriteString(versioning.GetVersion(ctx)) + } + + opts := versioning.DeprecationOptions{ + WarnMessage: "deprecated, see ", + DeprecationDate: time.Now().UTC(), + DeprecationInfo: "a bigger version is available, see for more information", + } + app.Get("/", versioning.Deprecated(writeVesion, opts)) + + e := httptest.New(t, app) + + ex := e.GET("/").WithHeader(versioning.AcceptVersionHeaderKey, "1.0").Expect() + ex.Status(iris.StatusOK).Body().Equal("1.0") + ex.Header("X-API-Warn").Equal(opts.WarnMessage) + expectedDateStr := opts.DeprecationDate.Format(app.ConfigurationReadOnly().GetTimeFormat()) + ex.Header("X-API-Deprecation-Date").Equal(expectedDateStr) +} diff --git a/versioning/version.go b/versioning/version.go new file mode 100644 index 00000000..1536ea2d --- /dev/null +++ b/versioning/version.go @@ -0,0 +1,72 @@ +package versioning + +import ( + "strings" + + "github.com/kataras/iris/context" +) + +const ( + AcceptVersionHeaderKey = "Accept-Version" + AcceptHeaderKey = "Accept" + AcceptHeaderVersionValue = "version" + + Key = "iris.api.version" // for use inside the ctx.Values(), not visible by the user. + NotFound = Key + ".notfound" +) + +var NotFoundHandler = func(ctx context.Context) { + // 303 is an option too, + // end-dev has the chance to change that behavior by using the NotFound in the map: + // + // https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html + /* + 10.5.2 501 Not Implemented + + The server does not support the functionality required to fulfill the request. + This is the appropriate response when the server does not + recognize the request method and is not capable of supporting it for any resource. + */ + ctx.WriteString("version not found") + ctx.StatusCode(501) +} + +func GetVersion(ctx context.Context) string { + // firstly by context store, if manually set-ed by a middleware. + if version := ctx.Values().GetString(Key); version != "" { + return version + } + + // secondly by the "Accept-Version" header. + if version := ctx.GetHeader(AcceptVersionHeaderKey); version != "" { + return version + } + + // thirdly by the "Accept" header which is like"...; version=1.0" + acceptValue := ctx.GetHeader(AcceptHeaderKey) + if acceptValue != "" { + if idx := strings.Index(acceptValue, AcceptHeaderVersionValue); idx != -1 { + rem := acceptValue[idx:] + startVersion := strings.Index(rem, "=") + if startVersion == -1 || len(rem) < startVersion+1 { + return NotFound + } + + rem = rem[startVersion+1:] + + end := strings.Index(rem, " ") + if end == -1 { + end = strings.Index(rem, ";") + } + if end == -1 { + end = len(rem) + } + + if version := rem[:end]; version != "" { + return version + } + } + } + + return NotFound +} diff --git a/versioning/version_test.go b/versioning/version_test.go new file mode 100644 index 00000000..059fb310 --- /dev/null +++ b/versioning/version_test.go @@ -0,0 +1,48 @@ +package versioning_test + +import ( + "testing" + + "github.com/kataras/iris" + "github.com/kataras/iris/httptest" + "github.com/kataras/iris/versioning" +) + +func TestGetVersion(t *testing.T) { + app := iris.New() + + writeVesion := func(ctx iris.Context) { + ctx.WriteString(versioning.GetVersion(ctx)) + } + + app.Get("/", writeVesion) + app.Get("/manual", func(ctx iris.Context) { + ctx.Values().Set(versioning.Key, "11.0.5") + ctx.Next() + }, writeVesion) + + e := httptest.New(t, app) + + e.GET("/").WithHeader(versioning.AcceptVersionHeaderKey, "1.0").Expect(). + Status(iris.StatusOK).Body().Equal("1.0") + e.GET("/").WithHeader(versioning.AcceptHeaderKey, "application/vnd.api+json; version=2.1").Expect(). + Status(iris.StatusOK).Body().Equal("2.1") + e.GET("/").WithHeader(versioning.AcceptHeaderKey, "application/vnd.api+json; version=2.1 ;other=dsa").Expect(). + Status(iris.StatusOK).Body().Equal("2.1") + e.GET("/").WithHeader(versioning.AcceptHeaderKey, "version=2.1").Expect(). + Status(iris.StatusOK).Body().Equal("2.1") + e.GET("/").WithHeader(versioning.AcceptHeaderKey, "version=1").Expect(). + Status(iris.StatusOK).Body().Equal("1") + + // unknown versions. + e.GET("/").WithHeader(versioning.AcceptVersionHeaderKey, "").Expect(). + Status(iris.StatusOK).Body().Equal(versioning.NotFound) + e.GET("/").WithHeader(versioning.AcceptHeaderKey, "application/vnd.api+json; version=").Expect(). + Status(iris.StatusOK).Body().Equal(versioning.NotFound) + e.GET("/").WithHeader(versioning.AcceptHeaderKey, "application/vnd.api+json; version= ;other=dsa").Expect(). + Status(iris.StatusOK).Body().Equal(versioning.NotFound) + e.GET("/").WithHeader(versioning.AcceptHeaderKey, "version=").Expect(). + Status(iris.StatusOK).Body().Equal(versioning.NotFound) + + e.GET("/manual").Expect().Status(iris.StatusOK).Body().Equal("11.0.5") +} diff --git a/versioning/versioning.go b/versioning/versioning.go new file mode 100644 index 00000000..8aaec792 --- /dev/null +++ b/versioning/versioning.go @@ -0,0 +1,87 @@ +package versioning + +import ( + "github.com/kataras/iris/context" + + "github.com/hashicorp/go-version" +) + +type Map map[string]context.Handler + +func Handler(versions Map) context.Handler { + constraintsHandlers, notFoundHandler := buildConstraints(versions) + + return func(ctx context.Context) { + versionString := GetVersion(ctx) + if versionString == NotFound { + notFoundHandler(ctx) + return + } + + version, err := version.NewVersion(versionString) + if err != nil { + notFoundHandler(ctx) + return + } + + for _, ch := range constraintsHandlers { + if ch.constraints.Check(version) { + ctx.Header("X-API-Version", version.String()) + ch.handler(ctx) + return + } + } + + notFoundHandler(ctx) + } +} + +type constraintsHandler struct { + constraints version.Constraints + handler context.Handler +} + +func buildConstraints(versionsHandler Map) (constraintsHandlers []*constraintsHandler, notfoundHandler context.Handler) { + for v, h := range versionsHandler { + if v == NotFound { + notfoundHandler = h + continue + } + + constraints, err := version.NewConstraint(v) + if err != nil { + panic(err) + } + + constraintsHandlers = append(constraintsHandlers, &constraintsHandler{ + constraints: constraints, + handler: h, + }) + } + + if notfoundHandler == nil { + notfoundHandler = NotFoundHandler + } + + // no sort, the end-dev should declare + // all version constraint, i.e < 4.0 may be catch 1.0 if not something like + // >= 3.0, < 4.0. + // I can make it ordered but I do NOT like the final API of it: + /* + app.Get("/api/user", Handler( // accepts an array, ordered, see last elem. + V("1.0", vHandler("v1 here")), + V("2.0", vHandler("v2 here")), + V("< 4.0", vHandler("v3.x here")), + )) + instead we have: + + app.Get("/api/user", Handler(Map{ // accepts a map, unordered, see last elem. + "1.0": Deprecated(vHandler("v1 here")), + "2.0": vHandler("v2 here"), + ">= 3.0, < 4.0": vHandler("v3.x here"), + VersionUnknown: customHandlerForNotMatchingVersion, + })) + */ + + return +} diff --git a/versioning/versioning_test.go b/versioning/versioning_test.go new file mode 100644 index 00000000..40dc41a3 --- /dev/null +++ b/versioning/versioning_test.go @@ -0,0 +1,49 @@ +package versioning_test + +import ( + "testing" + + "github.com/kataras/iris" + "github.com/kataras/iris/httptest" + "github.com/kataras/iris/versioning" +) + +func notFoundHandler(ctx iris.Context) { + ctx.NotFound() +} + +const ( + v10Response = "v1.0 handler" + v2Response = "v2.x handler" +) + +func sendHandler(contents string) iris.Handler { + return func(ctx iris.Context) { + ctx.WriteString(contents) + } +} + +func TestHandler(t *testing.T) { + app := iris.New() + + userAPI := app.Party("/api/user") + userAPI.Get("/", versioning.Handler(versioning.Map{ + "1.0": sendHandler(v10Response), + ">= 2, < 3": sendHandler(v2Response), + versioning.NotFound: notFoundHandler, + })) + + e := httptest.New(t, app) + + e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "1").Expect(). + Status(iris.StatusOK).Body().Equal(v10Response) + e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "2.0").Expect(). + Status(iris.StatusOK).Body().Equal(v2Response) + e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "2.1").Expect(). + Status(iris.StatusOK).Body().Equal(v2Response) + e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "2.9.9").Expect(). + Status(iris.StatusOK).Body().Equal(v2Response) + + e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "3.0").Expect(). + Status(iris.StatusNotFound).Body().Equal("Not Found") +} From 7608873e703b794013c8ce60138e952bfe9372e4 Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Sat, 10 Nov 2018 23:29:24 +0200 Subject: [PATCH 02/12] versioning API: initialize support for grouping Former-commit-id: 36cf8cd79801e8556f3c7b560f3bd759d9770d67 --- _examples/routing/dynamic-path/main.go | 79 +++++++---- versioning/deprecation.go | 4 + versioning/group.go | 175 +++++++++++++++++++++++++ versioning/version.go | 3 +- versioning/versioning.go | 9 +- versioning/versioning_test.go | 72 +++++++++- 6 files changed, 314 insertions(+), 28 deletions(-) create mode 100644 versioning/group.go diff --git a/_examples/routing/dynamic-path/main.go b/_examples/routing/dynamic-path/main.go index d94b0c18..6e9fd0bb 100644 --- a/_examples/routing/dynamic-path/main.go +++ b/_examples/routing/dynamic-path/main.go @@ -28,30 +28,65 @@ func main() { // otherwise it pre-compiles the regexp and adds the necessary middleware(s). // // Standard macro types for parameters: - // +------------------------+ - // | {param:string} | - // +------------------------+ + // +------------------------+ + // | {param:string} | + // +------------------------+ // string type - // anything + // anything (single path segmnent) // - // +-------------------------------+ - // | {param:int} or {param:int} | - // +-------------------------------+ + // +-------------------------------+ + // | {param:int} | + // +-------------------------------+ // int type - // both positive and negative numbers, any number of digits (ctx.Params().GetInt will limit the digits based on the host arch) + // -9223372036854775808 to 9223372036854775807 (x64) or -2147483648 to 2147483647 (x32), depends on the host arch // - // +-------------------------------+ - // | {param:int64} or {param:long} | - // +-------------------------------+ + // +------------------------+ + // | {param:int8} | + // +------------------------+ + // int8 type + // -128 to 127 + // + // +------------------------+ + // | {param:int16} | + // +------------------------+ + // int16 type + // -32768 to 32767 + // + // +------------------------+ + // | {param:int32} | + // +------------------------+ + // int32 type + // -2147483648 to 2147483647 + // + // +------------------------+ + // | {param:int64} | + // +------------------------+ // int64 type // -9223372036854775808 to 9223372036854775807 // // +------------------------+ + // | {param:uint} | + // +------------------------+ + // uint type + // 0 to 18446744073709551615 (x64) or 0 to 4294967295 (x32) + // + // +------------------------+ // | {param:uint8} | // +------------------------+ // uint8 type // 0 to 255 // + // +------------------------+ + // | {param:uint16} | + // +------------------------+ + // uint16 type + // 0 to 65535 + // + // +------------------------+ + // | {param:uint32} | + // +------------------------+ + // uint32 type + // 0 to 4294967295 // // +------------------------+ // | {param:uint64} | @@ -66,15 +101,15 @@ func main() { // only "1" or "t" or "T" or "TRUE" or "true" or "True" // or "0" or "f" or "F" or "FALSE" or "false" or "False" // - // +------------------------+ - // | {param:alphabetical} | - // +------------------------+ + // +------------------------+ + // | {param:alphabetical} | + // +------------------------+ // alphabetical/letter type // letters only (upper or lowercase) // - // +------------------------+ - // | {param:file} | - // +------------------------+ + // +------------------------+ + // | {param:file} | + // +------------------------+ // file type // letters (upper or lowercase) // numbers (0-9) @@ -83,12 +118,12 @@ func main() { // point (.) // no spaces ! or other character // - // +------------------------+ - // | {param:path} | - // +------------------------+ + // +------------------------+ + // | {param:path} | + // +------------------------+ // path type - // anything, should be the last part, more than one path segment, - // i.e: /path1/path2/path3 , ctx.Params().Get("param") == "/path1/path2/path3" + // anything, should be the last part, can be more than one path segment, + // i.e: "/test/*param" and request: "/test/path1/path2/path3" , ctx.Params().Get("param") == "path1/path2/path3" // // if type is missing then parameter's type is defaulted to string, so // {param} == {param:string}. diff --git a/versioning/deprecation.go b/versioning/deprecation.go index afccc5f9..3d8b5363 100644 --- a/versioning/deprecation.go +++ b/versioning/deprecation.go @@ -12,6 +12,10 @@ type DeprecationOptions struct { DeprecationInfo string } +func (opts DeprecationOptions) ShouldHandle() bool { + return opts.WarnMessage != "" || !opts.DeprecationDate.IsZero() || opts.DeprecationInfo != "" +} + var DefaultDeprecationOptions = DeprecationOptions{ WarnMessage: "WARNING! You are using a deprecated version of this API.", } diff --git a/versioning/group.go b/versioning/group.go new file mode 100644 index 00000000..2d52c15c --- /dev/null +++ b/versioning/group.go @@ -0,0 +1,175 @@ +package versioning + +import ( + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/router" +) + +func RegisterGroups(r router.Party, groups ...*Group) []*router.Route { + return Concat(groups...).For(r) +} + +type ( + vroute struct { + method string + path string + versions Map + } + + Group struct { + version string + routes []vroute + + deprecation DeprecationOptions + } +) + +func NewGroup(version string) *Group { + return &Group{ + version: version, + } +} + +// Deprecated marks this group and all its versioned routes +// as deprecated versions of that endpoint. +// It can be called in the end just before `Concat` or `RegisterGroups` +// or first by `NewGroup(...).Deprecated(...)`. It returns itself. +func (g *Group) Deprecated(options DeprecationOptions) *Group { + // if `Deprecated` is called in the end. + for _, r := range g.routes { + r.versions[g.version] = Deprecated(r.versions[g.version], options) + } + + // store the options if called before registering any versioned routes. + g.deprecation = options + + return g +} + +// Handle registers a versioned route to the group. +// +// See `Concat` and `RegisterGroups` for more. +func (g *Group) Handle(method string, registeredPath string, handler context.Handler) { + if g.deprecation.ShouldHandle() { // if `Deprecated` called first. + handler = Deprecated(handler, g.deprecation) + } + + g.routes = append(g.routes, vroute{ + method: method, + path: registeredPath, + versions: Map{g.version: handler}, + }) +} + +// None registers an "offline" versioned route +// see `context#ExecRoute(routeName)` and routing examples. +func (g *Group) None(path string, handler context.Handler) { + g.Handle(router.MethodNone, path, handler) +} + +// Get registers a versioned route for the Get http method. +func (g *Group) Get(path string, handler context.Handler) { + g.Handle("GET", path, handler) +} + +// Post registers a versioned route for the Post http method. +func (g *Group) Post(path string, handler context.Handler) { + g.Handle("POST", path, handler) +} + +// Put registers a versioned route for the Put http method +func (g *Group) Put(path string, handler context.Handler) { + g.Handle("PUT", path, handler) +} + +// Delete registers a versioned route for the Delete http method. +func (g *Group) Delete(path string, handler context.Handler) { + g.Handle("DELETE", path, handler) +} + +// Connect registers a versioned route for the Connect http method. +func (g *Group) Connect(path string, handler context.Handler) { + g.Handle("CONNECT", path, handler) +} + +// Head registers a versioned route for the Head http method. +func (g *Group) Head(path string, handler context.Handler) { + g.Handle("HEAD", path, handler) +} + +// Options registers a versioned route for the Options http method. +func (g *Group) Options(path string, handler context.Handler) { + g.Handle("OPTIONS", path, handler) +} + +// Patch registers a versioned route for the Patch http method. +func (g *Group) Patch(path string, handler context.Handler) { + g.Handle("PATCH", path, handler) +} + +// Trace registers a versioned route for the Trace http method. +func (g *Group) Trace(path string, handler context.Handler) { + g.Handle("TRACE", path, handler) +} + +// Any registers a versioned route for ALL of the http methods +// (Get,Post,Put,Head,Patch,Options,Connect,Delete). +func (g *Group) Any(registeredPath string, handler context.Handler) { + g.Get(registeredPath, handler) + g.Post(registeredPath, handler) + g.Put(registeredPath, handler) + g.Delete(registeredPath, handler) + g.Connect(registeredPath, handler) + g.Head(registeredPath, handler) + g.Options(registeredPath, handler) + g.Patch(registeredPath, handler) + g.Trace(registeredPath, handler) +} + +type Groups struct { + routes []vroute + + notFoundHandler context.Handler +} + +func Concat(groups ...*Group) *Groups { + var total []vroute + + for _, g := range groups { + inner: + for _, r := range g.routes { + for i, tr := range total { + if tr.method == r.method && tr.path == r.path { + for k, v := range r.versions { + total[i].versions[k] = v + } + continue inner + } + } + } + + total = append(total, g.routes...) + } + + return &Groups{total, NotFoundHandler} +} + +func (g *Groups) NotFound(handler context.Handler) *Groups { + g.notFoundHandler = handler + return g +} + +func (g *Groups) For(r router.Party) (totalRoutesRegistered []*router.Route) { + for _, vr := range g.routes { + if g.notFoundHandler != nil { + vr.versions[NotFound] = g.notFoundHandler + } + + // fmt.Printf("Method: %s | Path: %s | Versions: %#+v\n", vr.method, vr.path, vr.versions) + route := r.Handle(vr.method, vr.path, + NewMatcher(vr.versions)) + totalRoutesRegistered = append(totalRoutesRegistered, route) + } + + return +} diff --git a/versioning/version.go b/versioning/version.go index 1536ea2d..3de1f865 100644 --- a/versioning/version.go +++ b/versioning/version.go @@ -27,8 +27,9 @@ var NotFoundHandler = func(ctx context.Context) { This is the appropriate response when the server does not recognize the request method and is not capable of supporting it for any resource. */ - ctx.WriteString("version not found") + ctx.StatusCode(501) + ctx.WriteString("version not found") } func GetVersion(ctx context.Context) string { diff --git a/versioning/versioning.go b/versioning/versioning.go index 8aaec792..90d976c0 100644 --- a/versioning/versioning.go +++ b/versioning/versioning.go @@ -8,7 +8,7 @@ import ( type Map map[string]context.Handler -func Handler(versions Map) context.Handler { +func NewMatcher(versions Map) context.Handler { constraintsHandlers, notFoundHandler := buildConstraints(versions) return func(ctx context.Context) { @@ -32,6 +32,9 @@ func Handler(versions Map) context.Handler { } } + // pass the not matched version so the not found handler can have knowedge about it. + // ctx.Values().Set(Key, versionString) + // or let a manual cal of GetVersion(ctx) do that instead. notFoundHandler(ctx) } } @@ -68,14 +71,14 @@ func buildConstraints(versionsHandler Map) (constraintsHandlers []*constraintsHa // >= 3.0, < 4.0. // I can make it ordered but I do NOT like the final API of it: /* - app.Get("/api/user", Handler( // accepts an array, ordered, see last elem. + app.Get("/api/user", NewMatcher( // accepts an array, ordered, see last elem. V("1.0", vHandler("v1 here")), V("2.0", vHandler("v2 here")), V("< 4.0", vHandler("v3.x here")), )) instead we have: - app.Get("/api/user", Handler(Map{ // accepts a map, unordered, see last elem. + app.Get("/api/user", NewMatcher(Map{ // accepts a map, unordered, see last elem. "1.0": Deprecated(vHandler("v1 here")), "2.0": vHandler("v2 here"), ">= 3.0, < 4.0": vHandler("v3.x here"), diff --git a/versioning/versioning_test.go b/versioning/versioning_test.go index 40dc41a3..06a205d2 100644 --- a/versioning/versioning_test.go +++ b/versioning/versioning_test.go @@ -23,11 +23,11 @@ func sendHandler(contents string) iris.Handler { } } -func TestHandler(t *testing.T) { +func TestNewMatcher(t *testing.T) { app := iris.New() userAPI := app.Party("/api/user") - userAPI.Get("/", versioning.Handler(versioning.Map{ + userAPI.Get("/", versioning.NewMatcher(versioning.Map{ "1.0": sendHandler(v10Response), ">= 2, < 3": sendHandler(v2Response), versioning.NotFound: notFoundHandler, @@ -47,3 +47,71 @@ func TestHandler(t *testing.T) { e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "3.0").Expect(). Status(iris.StatusNotFound).Body().Equal("Not Found") } + +func TestNewGroup(t *testing.T) { + app := iris.New() + // userAPI := app.Party("/api/user") + + // userAPIV10 := versioning.NewGroup("1.0", userAPI) + // userAPIV10.Get("/", sendHandler(v10Response)) + // userAPIV2 := versioning.NewGroup(">= 2, < 3", userAPI) + // userAPIV2.Get("/", sendHandler(v2Response)) + + // --- + + // userAPI := app.Party("/api/user") + // userVAPI := versioning.NewGroup(userAPI) + // userAPIV10 := userVAPI.Version("1.0") + // userAPIV10.Get("/", sendHandler(v10Response)) + + // userAPIV10 := userVAPI.Version("2.0") + // userAPIV10.Get("/", sendHandler(v10Response)) + // userVAPI.NotFound(...) + // userVAPI.Build() + + // -- + + userAPI := app.Party("/api/user") + // [... static serving, middlewares and etc goes here]. + + userAPIV10 := versioning.NewGroup("1.0").Deprecated(versioning.DefaultDeprecationOptions) + userAPIV10.Get("/", sendHandler(v10Response)) + + userAPIV2 := versioning.NewGroup(">= 2, < 3") + userAPIV2.Get("/", sendHandler(v2Response)) + userAPIV2.Post("/", sendHandler(v2Response)) + userAPIV2.Put("/other", sendHandler(v2Response)) + + // versioning.Concat(userAPIV10, userAPIV2) + // NotFound(func(ctx iris.Context) { + // ctx.StatusCode(iris.StatusNotFound) + // ctx.Writef("unknown version %s", versioning.GetVersion(ctx)) + // }). + // For(userAPI) + // This is legal too: + // For(app.PartyFunc("/api/user", func(r iris.Party) { + // // [... static serving, middlewares and etc goes here]. + // })) + + versioning.RegisterGroups(userAPI, userAPIV10, userAPIV2) + + e := httptest.New(t, app) + + ex := e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "1").Expect() + ex.Status(iris.StatusOK).Body().Equal(v10Response) + ex.Header("X-API-Warn").Equal(versioning.DefaultDeprecationOptions.WarnMessage) + + e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "2.0").Expect(). + Status(iris.StatusOK).Body().Equal(v2Response) + e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "2.1").Expect(). + Status(iris.StatusOK).Body().Equal(v2Response) + e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "2.9.9").Expect(). + Status(iris.StatusOK).Body().Equal(v2Response) + e.POST("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "2").Expect(). + Status(iris.StatusOK).Body().Equal(v2Response) + e.PUT("/api/user/other").WithHeader(versioning.AcceptVersionHeaderKey, "2.9").Expect(). + Status(iris.StatusOK).Body().Equal(v2Response) + + e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "3.0").Expect(). + Status(iris.StatusNotImplemented).Body().Equal("version not found") +} From d766bf9f657f93997beb048cc28bda027c2f776d Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Sat, 10 Nov 2018 23:46:01 +0200 Subject: [PATCH 03/12] fix https://github.com/kataras/iris/pull/1128 for https://github.com/kataras/iris/issues/1132 Former-commit-id: fd3ef788ff4696e8a7f3d0d2cf11be4532abdf3f --- Gopkg.lock | 6 +++--- Gopkg.toml | 2 +- _examples/routing/dynamic-path/main.go | 2 +- context/context.go | 2 +- doc.go | 2 +- go.mod | 2 +- go.sum | 4 ++-- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 64107208..82bb6db8 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -152,10 +152,10 @@ version = "v1.0.0" [[projects]] - name = "gopkg.in/russross/blackfriday.v2" + name = "github.com/russross/blackfriday" packages = ["."] - revision = "cadec560ec52d93835bf2f15bd794700d3a2473b" - version = "v2.0.0" + revision = "d3b5b032dc8e8927d31a5071b56e14c89f045135" + version = "v2.0.1" [[projects]] branch = "master" diff --git a/Gopkg.toml b/Gopkg.toml index b03d8693..0951bc11 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -52,7 +52,7 @@ [[constraint]] branch = "v2" - name = "gopkg.in/russross/blackfriday.v2" + name = "github.com/russross/blackfriday" [[constraint]] branch = "master" diff --git a/_examples/routing/dynamic-path/main.go b/_examples/routing/dynamic-path/main.go index 6e9fd0bb..d2399839 100644 --- a/_examples/routing/dynamic-path/main.go +++ b/_examples/routing/dynamic-path/main.go @@ -123,7 +123,7 @@ func main() { // +------------------------+ // path type // anything, should be the last part, can be more than one path segment, - // i.e: "/test/*param" and request: "/test/path1/path2/path3" , ctx.Params().Get("param") == "path1/path2/path3" + // i.e: "/test/{param:path}" and request: "/test/path1/path2/path3" , ctx.Params().Get("param") == "path1/path2/path3" // // if type is missing then parameter's type is defaulted to string, so // {param} == {param:string}. diff --git a/context/context.go b/context/context.go index 7ea78229..70bdcab0 100644 --- a/context/context.go +++ b/context/context.go @@ -29,7 +29,7 @@ import ( formbinder "github.com/iris-contrib/formBinder" "github.com/json-iterator/go" "github.com/microcosm-cc/bluemonday" - "gopkg.in/russross/blackfriday.v2" + "github.com/russross/blackfriday" "gopkg.in/yaml.v2" ) diff --git a/doc.go b/doc.go index 9c6fe387..161b159b 100644 --- a/doc.go +++ b/doc.go @@ -800,7 +800,7 @@ Standard macro types for parameters: +------------------------+ path type anything, should be the last part, can be more than one path segment, - i.e: "/test/*param" and request: "/test/path1/path2/path3" , ctx.Params().Get("param") == "path1/path2/path3" + i.e: "/test/{param:path}" and request: "/test/path1/path2/path3" , ctx.Params().Get("param") == "path1/path2/path3" if type is missing then parameter's type is defaulted to string, so {param} == {param:string}. diff --git a/go.mod b/go.mod index 5d0d1a48..df915b02 100644 --- a/go.mod +++ b/go.mod @@ -44,6 +44,7 @@ require ( github.com/onsi/gomega v1.4.2 // indirect github.com/pkg/errors v0.8.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/russross/blackfriday v2.0.0+incompatible github.com/ryanuber/columnize v2.1.0+incompatible github.com/sergi/go-diff v1.0.0 // indirect github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95 // indirect @@ -63,6 +64,5 @@ require ( golang.org/x/sys v0.0.0-20180928133829-e4b3c5e90611 // indirect gopkg.in/ini.v1 v1.38.3 // indirect gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce // indirect - gopkg.in/russross/blackfriday.v2 v2.0.1+incompatible gopkg.in/yaml.v2 v2.2.1 ) diff --git a/go.sum b/go.sum index e09465a2..28ff171d 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,7 @@ github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7 h1:PqzgE6kAMi github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Joker/hpp v0.0.0-20180418125244-6893e659854a/go.mod h1:MzD2WMdSxvbHw5fM/OXOFily/lipJWRc9C1px0Mt0ZE= github.com/Joker/jade v1.0.0 h1:lOCEPvTAtWfLpSZYMOv/g44MGQFAolbKh2khHHGu0Kc= github.com/Joker/jade v1.0.0/go.mod h1:efZIdO0py/LtcJRSa/j2WEklMSAw84WV0zZVMxNToB8= github.com/Shopify/goreferrer v0.0.0-20180807163728-b9777dc9f9cc h1:zZYkIbeMNcH1lhztdVxy4+Ykk8NoMhqUfSigsrT/x7Y= @@ -92,6 +93,7 @@ github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/ryanuber/columnize v2.1.0+incompatible h1:j1Wcmh8OrK4Q7GXY+V7SVSY8nUWQxHW5TkBe7YUl+2s= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= @@ -143,8 +145,6 @@ gopkg.in/ini.v1 v1.38.3 h1:ourkRZgR6qjJYoec9lYhX4+nuN1tEbV34dQEQ3IRk9U= gopkg.in/ini.v1 v1.38.3/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce h1:xcEWjVhvbDy+nHP67nPDDpbYrY+ILlfndk4bRioVHaU= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= -gopkg.in/russross/blackfriday.v2 v2.0.0+incompatible h1:l1Mna0cVh8WlpyB8uFtc2c+5cdvrI5CDyuwTgIChojI= -gopkg.in/russross/blackfriday.v2 v2.0.0+incompatible/go.mod h1:6sSBNz/GtOm/pJTuh5UmBK2ZHfmnxGbl2NZg1UliSOI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= From 3b2f19eda57c5fef51d6b5e1c82b46cb1814d39a Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Sun, 11 Nov 2018 00:41:55 +0200 Subject: [PATCH 04/12] move the blackfriday lib to the iris-contrib for the stability to avoid issues like: https://github.com/kataras/iris/issues/1132 Former-commit-id: 5540abd73f3b917e89a73486e5983605546b9008 --- Gopkg.lock | 6 +++--- Gopkg.toml | 6 +++--- context/context.go | 2 +- go.mod | 4 ++-- go.sum | 10 +++++----- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 82bb6db8..4331c0c3 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -152,10 +152,10 @@ version = "v1.0.0" [[projects]] - name = "github.com/russross/blackfriday" + branch = "master" + name = "github.com/iris-contrib/blackfriday" packages = ["."] - revision = "d3b5b032dc8e8927d31a5071b56e14c89f045135" - version = "v2.0.1" + revision = "fd09ab28f1e13c6be7fef337a9ec91dc8bd4c4d9" [[projects]] branch = "master" diff --git a/Gopkg.toml b/Gopkg.toml index 0951bc11..c757d7a7 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -51,12 +51,12 @@ name = "github.com/microcosm-cc/bluemonday" [[constraint]] - branch = "v2" - name = "github.com/russross/blackfriday" + branch = "master" + name = "github.com/ryanuber/columnize" [[constraint]] branch = "master" - name = "github.com/ryanuber/columnize" + name = "github.com/iris-contrib/blackfriday" [[constraint]] branch = "v2" diff --git a/context/context.go b/context/context.go index 70bdcab0..d1e8e4de 100644 --- a/context/context.go +++ b/context/context.go @@ -26,10 +26,10 @@ import ( "github.com/Shopify/goreferrer" "github.com/fatih/structs" + "github.com/iris-contrib/blackfriday" formbinder "github.com/iris-contrib/formBinder" "github.com/json-iterator/go" "github.com/microcosm-cc/bluemonday" - "github.com/russross/blackfriday" "gopkg.in/yaml.v2" ) diff --git a/go.mod b/go.mod index df915b02..3af6c02e 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,9 @@ require ( github.com/google/go-querystring v1.0.0 // indirect github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c // indirect github.com/gorilla/websocket v1.4.0 + github.com/hashicorp/go-version v1.0.0 github.com/imkira/go-interpol v1.1.0 // indirect + github.com/iris-contrib/blackfriday v0.0.0-20181110223730-fd09ab28f1e1 github.com/iris-contrib/formBinder v0.0.0-20171010160137-ad9fb86c356f github.com/iris-contrib/go.uuid v2.0.0+incompatible github.com/iris-contrib/httpexpect v0.0.0-20180314041918-ebe99fcebbce @@ -44,10 +46,8 @@ require ( github.com/onsi/gomega v1.4.2 // indirect github.com/pkg/errors v0.8.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/russross/blackfriday v2.0.0+incompatible github.com/ryanuber/columnize v2.1.0+incompatible github.com/sergi/go-diff v1.0.0 // indirect - github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95 // indirect github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d // indirect github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a // indirect github.com/stretchr/testify v1.2.2 // indirect diff --git a/go.sum b/go.sum index 28ff171d..76fb0f0c 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,7 @@ github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7 h1:PqzgE6kAMi github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Joker/hpp v0.0.0-20180418125244-6893e659854a h1:PiDAizhfJbwZMISZ1Itx1ZTFeOFCml89Ofmz3V8rhoU= github.com/Joker/hpp v0.0.0-20180418125244-6893e659854a/go.mod h1:MzD2WMdSxvbHw5fM/OXOFily/lipJWRc9C1px0Mt0ZE= github.com/Joker/jade v1.0.0 h1:lOCEPvTAtWfLpSZYMOv/g44MGQFAolbKh2khHHGu0Kc= github.com/Joker/jade v1.0.0/go.mod h1:efZIdO0py/LtcJRSa/j2WEklMSAw84WV0zZVMxNToB8= @@ -41,10 +42,14 @@ github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c h1:16eHWuMGvCjSf github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/hashicorp/go-version v1.0.0 h1:21MVWPKDphxa7ineQQTrCU5brh7OuVVAzGOCnnCPtE8= +github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk= github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= +github.com/iris-contrib/blackfriday v0.0.0-20181110223730-fd09ab28f1e1 h1:U6CcvSHk8+jgsB/9Co9gCp63HOdtK3TZo1rRuZu4uTg= +github.com/iris-contrib/blackfriday v0.0.0-20181110223730-fd09ab28f1e1/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= github.com/iris-contrib/formBinder v0.0.0-20171010160137-ad9fb86c356f h1:WgD6cqCSncBgkftw34mSPlMKU5JgoruAomW/SJtRrGU= github.com/iris-contrib/formBinder v0.0.0-20171010160137-ad9fb86c356f/go.mod h1:i8kTYUOEstd/S8TG0ChTXQdf4ermA/e8vJX0+QruD9w= github.com/iris-contrib/go.uuid v2.0.0+incompatible h1:XZubAYg61/JwnJNbZilGjf3b3pB80+OQg2qf6c8BfWE= @@ -93,13 +98,10 @@ github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/ryanuber/columnize v2.1.0+incompatible h1:j1Wcmh8OrK4Q7GXY+V7SVSY8nUWQxHW5TkBe7YUl+2s= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95 h1:/vdW8Cb7EXrkqWGufVMES1OH2sU9gKVb2n9/1y5NMBY= -github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a h1:JSvGDIbmil4Ui/dDdFBExb7/cmkNjyX5F97oglmvCDo= @@ -125,8 +127,6 @@ github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZ golang.org/x/crypto v0.0.0-20180927165925-5295e8364332 h1:hvQVdF6P9DX4OiKA5tpehlG6JsgzmyQiThG7q5Bn3UQ= golang.org/x/crypto v0.0.0-20180927165925-5295e8364332/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3 h1:dgd4x4kJt7G4k4m93AYLzM8Ni6h2qLTfh9n9vXJT3/0= -golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181102091132-c10e9556a7bc h1:ZMCWScCvS2fUVFw8LOpxyUUW5qiviqr4Dg5NdjLeiLU= golang.org/x/net v0.0.0-20181102091132-c10e9556a7bc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= From 3a77da586fb8db844f37e9b33d84d45b548c5930 Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Sun, 11 Nov 2018 00:57:26 +0200 Subject: [PATCH 05/12] blackfriday move to iris-contrib, tested Former-commit-id: b9a0b9288f06a190500d8583af2503fcd67efe12 --- Gopkg.lock | 2 +- HISTORY.md | 2 +- go.mod | 7 ++++--- go.sum | 15 +++++++++++---- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 4331c0c3..112390bc 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -155,7 +155,7 @@ branch = "master" name = "github.com/iris-contrib/blackfriday" packages = ["."] - revision = "fd09ab28f1e13c6be7fef337a9ec91dc8bd4c4d9" + revision = "48b3da6a6f3865c7eb1eba96d74cf0a16f63faca" [[projects]] branch = "master" diff --git a/HISTORY.md b/HISTORY.md index 6a21f38c..7d1bb7cf 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -119,7 +119,7 @@ For the craziest of us, click [here](https://github.com/kataras/iris/compare/v10 ## Routing -I wrote a [new router implementation](https://github.com/kataras/muxie#philosophy) for our Iris internal(low-level) routing mechanism, it is good to know that this was the second time we have updated the router internals without a single breaking change after the v6, thanks to the very well-writen and designed-first code we have for the high-level path syntax component called [macro interpreter](macro/interpreter). +I wrote a [new router implementation](https://github.com/kataras/muxie#philosophy) for our Iris internal(low-level) routing mechanism, it is good to know that this was the second time we have updated the router internals without a single breaking change after the v6, thanks to the very well-written and designed-first code we have for the high-level path syntax component called [macro interpreter](macro/interpreter). The new router supports things like **closest wildcard resolution**. diff --git a/go.mod b/go.mod index 3af6c02e..93068c7e 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/aymerick/raymond v2.0.2+incompatible github.com/boltdb/bolt v1.3.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/hashicorp/go-version v1.0.0 github.com/dgraph-io/badger v1.5.4 github.com/dgryski/go-farm v0.0.0-20180109070241-2de33835d102 // indirect github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 @@ -20,9 +21,8 @@ require ( github.com/google/go-querystring v1.0.0 // indirect github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c // indirect github.com/gorilla/websocket v1.4.0 - github.com/hashicorp/go-version v1.0.0 github.com/imkira/go-interpol v1.1.0 // indirect - github.com/iris-contrib/blackfriday v0.0.0-20181110223730-fd09ab28f1e1 + github.com/iris-contrib/blackfriday v2.0.0+incompatible github.com/iris-contrib/formBinder v0.0.0-20171010160137-ad9fb86c356f github.com/iris-contrib/go.uuid v2.0.0+incompatible github.com/iris-contrib/httpexpect v0.0.0-20180314041918-ebe99fcebbce @@ -35,7 +35,7 @@ require ( github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect github.com/kataras/golog v0.0.0-20180321173939-03be10146386 github.com/kataras/pio v0.0.0-20180511174041-a9733b5b6b83 // indirect - github.com/klauspost/compress v1.4.0 + github.com/klauspost/compress v1.4.1 github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e // indirect github.com/mattn/go-colorable v0.0.9 // indirect github.com/mattn/go-isatty v0.0.4 // indirect @@ -48,6 +48,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/ryanuber/columnize v2.1.0+incompatible github.com/sergi/go-diff v1.0.0 // indirect + github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95 // indirect github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d // indirect github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a // indirect github.com/stretchr/testify v1.2.2 // indirect diff --git a/go.sum b/go.sum index 76fb0f0c..cbdc0919 100644 --- a/go.sum +++ b/go.sum @@ -42,14 +42,12 @@ github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c h1:16eHWuMGvCjSf github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/hashicorp/go-version v1.0.0 h1:21MVWPKDphxa7ineQQTrCU5brh7OuVVAzGOCnnCPtE8= -github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk= github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= -github.com/iris-contrib/blackfriday v0.0.0-20181110223730-fd09ab28f1e1 h1:U6CcvSHk8+jgsB/9Co9gCp63HOdtK3TZo1rRuZu4uTg= -github.com/iris-contrib/blackfriday v0.0.0-20181110223730-fd09ab28f1e1/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= +github.com/iris-contrib/blackfriday v2.0.0+incompatible h1:o5sHQHHm0ToHUlAJSTjW9UWicjJSDDauOOQ2AHuIVp4= +github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= github.com/iris-contrib/formBinder v0.0.0-20171010160137-ad9fb86c356f h1:WgD6cqCSncBgkftw34mSPlMKU5JgoruAomW/SJtRrGU= github.com/iris-contrib/formBinder v0.0.0-20171010160137-ad9fb86c356f/go.mod h1:i8kTYUOEstd/S8TG0ChTXQdf4ermA/e8vJX0+QruD9w= github.com/iris-contrib/go.uuid v2.0.0+incompatible h1:XZubAYg61/JwnJNbZilGjf3b3pB80+OQg2qf6c8BfWE= @@ -76,6 +74,8 @@ github.com/kataras/pio v0.0.0-20180511174041-a9733b5b6b83 h1:NoJ+fI58ptwrPc1blX1 github.com/kataras/pio v0.0.0-20180511174041-a9733b5b6b83/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0= github.com/klauspost/compress v1.4.0 h1:8nsMz3tWa9SWWPL60G1V6CUsf4lLjWLTNEtibhe8gh8= github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.4.1 h1:8VMb5+0wMgdBykOV96DwNwKFQ+WTI4pzYURP99CcB9E= +github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e h1:+lIPJOWl+jSiJOc70QXJ07+2eg2Jy2EC7Mi11BWujeM= github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= @@ -102,6 +102,8 @@ github.com/ryanuber/columnize v2.1.0+incompatible h1:j1Wcmh8OrK4Q7GXY+V7SVSY8nUW github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95 h1:/vdW8Cb7EXrkqWGufVMES1OH2sU9gKVb2n9/1y5NMBY= +github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a h1:JSvGDIbmil4Ui/dDdFBExb7/cmkNjyX5F97oglmvCDo= @@ -127,9 +129,12 @@ github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZ golang.org/x/crypto v0.0.0-20180927165925-5295e8364332 h1:hvQVdF6P9DX4OiKA5tpehlG6JsgzmyQiThG7q5Bn3UQ= golang.org/x/crypto v0.0.0-20180927165925-5295e8364332/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3 h1:dgd4x4kJt7G4k4m93AYLzM8Ni6h2qLTfh9n9vXJT3/0= +golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181102091132-c10e9556a7bc h1:ZMCWScCvS2fUVFw8LOpxyUUW5qiviqr4Dg5NdjLeiLU= golang.org/x/net v0.0.0-20181102091132-c10e9556a7bc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181108082009-03003ca0c849 h1:FSqE2GGG7wzsYUsWiQ8MZrvEd1EOyU3NCF0AW3Wtltg= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -149,3 +154,5 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +github.com/hashicorp/go-version v1.0.0 h1:21MVWPKDphxa7ineQQTrCU5brh7OuVVAzGOCnnCPtE8= +github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= \ No newline at end of file From fc9e5b3c05450211dad50c62b0df8d8bba863dfe Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Sun, 11 Nov 2018 01:33:14 +0200 Subject: [PATCH 06/12] resolve master conflicts for vendor Former-commit-id: c47d392d32b704f230ae4b2e2610ea35c4e399f6 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 93068c7e..723b0774 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,6 @@ require ( github.com/aymerick/raymond v2.0.2+incompatible github.com/boltdb/bolt v1.3.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/hashicorp/go-version v1.0.0 github.com/dgraph-io/badger v1.5.4 github.com/dgryski/go-farm v0.0.0-20180109070241-2de33835d102 // indirect github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 @@ -21,6 +20,7 @@ require ( github.com/google/go-querystring v1.0.0 // indirect github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c // indirect github.com/gorilla/websocket v1.4.0 + github.com/hashicorp/go-version v1.0.0 github.com/imkira/go-interpol v1.1.0 // indirect github.com/iris-contrib/blackfriday v2.0.0+incompatible github.com/iris-contrib/formBinder v0.0.0-20171010160137-ad9fb86c356f diff --git a/go.sum b/go.sum index cbdc0919..fa58ee46 100644 --- a/go.sum +++ b/go.sum @@ -42,6 +42,8 @@ github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c h1:16eHWuMGvCjSf github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/hashicorp/go-version v1.0.0 h1:21MVWPKDphxa7ineQQTrCU5brh7OuVVAzGOCnnCPtE8= +github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk= @@ -154,5 +156,3 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -github.com/hashicorp/go-version v1.0.0 h1:21MVWPKDphxa7ineQQTrCU5brh7OuVVAzGOCnnCPtE8= -github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= \ No newline at end of file From 70610af6fd6c2015c0b1656c059d01da4acb7c0c Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Sun, 11 Nov 2018 02:18:19 +0200 Subject: [PATCH 07/12] add versioning/README.md Former-commit-id: 7ea92fadc982038533675996704b6bf89e149aae --- versioning/README.md | 86 +++++++++++++++++++++++++++++++++++ versioning/versioning.go | 24 ++++++++-- versioning/versioning_test.go | 44 ++++++++++-------- 3 files changed, 131 insertions(+), 23 deletions(-) create mode 100644 versioning/README.md diff --git a/versioning/README.md b/versioning/README.md new file mode 100644 index 00000000..3e854d95 --- /dev/null +++ b/versioning/README.md @@ -0,0 +1,86 @@ +# Versioning + +The [versioning](https://github.com/kataras/iris/tree/master/versioning) package provides [semver](https://semver.org/) versioning for your APIs. It implements all the suggestions written at [api-guidelines](https://github.com/byrondover/api-guidelines/blob/master/Guidelines.md#versioning) and more. + + +The version comparison is done by the [go-version](https://github.com/hashicorp/go-version) package. It supports matching over patterns like `">= 1.0, < 3"` and etc. + +## Features + +- per route version matching, a normal iris handler with "switch" cases via Map for version => handler +- per group versioned routes and deprecation API +- version matching like ">= 1.0, < 2.0" or just "2.0.1" and etc. +- version not found handler (can be customized by simply adding the versioning.NotFound: customNotMatchVersionHandler on the Map) +- version is retrieved from the "Accept" and "Accept-Version" headers (can be customized via middleware) +- respond with "X-API-Version" header, if version found. +- deprecation options with customizable "X-API-Warn", "X-API-Deprecation-Date", "X-API-Deprecation-Info" headers via `Deprecated` wrapper. + +## Get version + +Current request version is retrieved by `versioning.GetVersion(ctx)`. + +By default the `GetVersion` will try to read from: +- `Accept` header, i.e `Accept: "application/json; version=1.0"` +- `Accept-Version` header, i.e `Accept-Version: "1.0"` + +You can also set a custom version for a handler via a middleware by using the context's store values. +For example: +```go +func(ctx iris.Context) { + ctx.Values().Set(versioning.Key, ctx.URLParamDefault("version", "1.0")) + ctx.Next() +} +``` + +## Match version to handler + +The `versioning.NewMatcher(versioning.Map) iris.Handler` creates a single handler which decides what handler need to be executed based on the requested version. + +```go +app := iris.New() + +// middleware for all versions. +myMiddleware := func(ctx iris.Context) { + // [...] + ctx.Next() +} + +myCustomNotVersionFound := func(ctx iris.Context) { + ctx.StatusCode(404) + ctx.Writef("%s version not found", versioning.GetVersion(ctx)) +} + +userAPI := app.Party("/api/user") +userAPI.Get("/", myMiddleware, versioning.NewMatcher(versioning.Map{ + "1.0": sendHandler(v10Response), + ">= 2, < 3": sendHandler(v2Response), + versioning.NotFound: myCustomNotVersionFound, +})) +``` + +## Grouping versioned routes + +Impl & tests done, example not. **TODO** + +## Compare version manually from inside your handlers + +```go +// reports if the "version" is matching to the "is". +// the "is" can be a constraint like ">= 1, < 3". +If(version string, is string) bool +``` + +```go +// same as `If` but expects a Context to read the requested version. +Match(ctx iris.Context, expectedVersion string) bool +``` + +```go +app.Get("/api/user", func(ctx iris.Context) { + if versioning.Match(ctx, ">= 2.2.3") { + // [logic for >= 2.2.3 version of your handler goes here] + return + } +}) +``` + diff --git a/versioning/versioning.go b/versioning/versioning.go index 90d976c0..5e2b8d84 100644 --- a/versioning/versioning.go +++ b/versioning/versioning.go @@ -6,6 +6,24 @@ import ( "github.com/hashicorp/go-version" ) +func If(v string, is string) bool { + ver, err := version.NewVersion(v) + if err != nil { + return false + } + + constraints, err := version.NewConstraint(is) + if err != nil { + return false + } + + return constraints.Check(ver) +} + +func Match(ctx context.Context, expectedVersion string) bool { + return If(GetVersion(ctx), expectedVersion) +} + type Map map[string]context.Handler func NewMatcher(versions Map) context.Handler { @@ -18,15 +36,15 @@ func NewMatcher(versions Map) context.Handler { return } - version, err := version.NewVersion(versionString) + ver, err := version.NewVersion(versionString) if err != nil { notFoundHandler(ctx) return } for _, ch := range constraintsHandlers { - if ch.constraints.Check(version) { - ctx.Header("X-API-Version", version.String()) + if ch.constraints.Check(ver) { + ctx.Header("X-API-Version", ver.String()) ch.handler(ctx) return } diff --git a/versioning/versioning_test.go b/versioning/versioning_test.go index 06a205d2..ea31b344 100644 --- a/versioning/versioning_test.go +++ b/versioning/versioning_test.go @@ -23,6 +23,14 @@ func sendHandler(contents string) iris.Handler { } } +func TestIf(t *testing.T) { + if expected, got := true, versioning.If("1.0", ">=1"); expected != got { + t.Fatalf("expected %s to be %s", "1.0", ">= 1") + } + if expected, got := true, versioning.If("1.2.3", "> 1.2"); expected != got { + t.Fatalf("expected %s to be %s", "1.2.3", "> 1.2") + } +} func TestNewMatcher(t *testing.T) { app := iris.New() @@ -33,6 +41,17 @@ func TestNewMatcher(t *testing.T) { versioning.NotFound: notFoundHandler, })) + // middleware as usual. + myMiddleware := func(ctx iris.Context) { + ctx.Header("X-Custom", "something") + ctx.Next() + } + myVersions := versioning.Map{ + "1.0": sendHandler(v10Response), + } + + userAPI.Get("/with_middleware", myMiddleware, versioning.NewMatcher(myVersions)) + e := httptest.New(t, app) e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "1").Expect(). @@ -44,32 +63,17 @@ func TestNewMatcher(t *testing.T) { e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "2.9.9").Expect(). Status(iris.StatusOK).Body().Equal(v2Response) + // middleware as usual. + ex := e.GET("/api/user/with_middleware").WithHeader(versioning.AcceptVersionHeaderKey, "1.0").Expect() + ex.Status(iris.StatusOK).Body().Equal(v10Response) + ex.Header("X-Custom").Equal("something") + e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "3.0").Expect(). Status(iris.StatusNotFound).Body().Equal("Not Found") } func TestNewGroup(t *testing.T) { app := iris.New() - // userAPI := app.Party("/api/user") - - // userAPIV10 := versioning.NewGroup("1.0", userAPI) - // userAPIV10.Get("/", sendHandler(v10Response)) - // userAPIV2 := versioning.NewGroup(">= 2, < 3", userAPI) - // userAPIV2.Get("/", sendHandler(v2Response)) - - // --- - - // userAPI := app.Party("/api/user") - // userVAPI := versioning.NewGroup(userAPI) - // userAPIV10 := userVAPI.Version("1.0") - // userAPIV10.Get("/", sendHandler(v10Response)) - - // userAPIV10 := userVAPI.Version("2.0") - // userAPIV10.Get("/", sendHandler(v10Response)) - // userVAPI.NotFound(...) - // userVAPI.Build() - - // -- userAPI := app.Party("/api/user") // [... static serving, middlewares and etc goes here]. From 6886fd98c80931db8bb249379a91ec06c81495ed Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Sun, 11 Nov 2018 17:27:31 +0200 Subject: [PATCH 08/12] complete the versioning/README.md and add `AllowMethods` like the normal Party, version-specific middlewares are not needed because the end-developer should declare a middleware with manual matching of version using versioning.Match(ctx, version) bool instead Former-commit-id: 4f4c23dd7c043d5ab735070ae4d59ea84e3af2e0 --- versioning/README.md | 70 ++++++++++++++++++++++++++++++++++- versioning/group.go | 38 ++++++++++++++----- versioning/versioning_test.go | 2 +- 3 files changed, 98 insertions(+), 12 deletions(-) diff --git a/versioning/README.md b/versioning/README.md index 3e854d95..e26fe07b 100644 --- a/versioning/README.md +++ b/versioning/README.md @@ -58,9 +58,75 @@ userAPI.Get("/", myMiddleware, versioning.NewMatcher(versioning.Map{ })) ``` -## Grouping versioned routes +### Deprecation -Impl & tests done, example not. **TODO** +Using the `versioning.Deprecated(handler iris.Handler, options versioning.DeprecationOptions) iris.Handler` function you can mark a specific handler version as deprecated. + + +```go +v10Handler := versioning.Deprecated(sendHandler(v10Response), versioning.DeprecationOptions{ + // if empty defaults to: "WARNING! You are using a deprecated version of this API." + WarnMessage string + DeprecationDate time.Time + DeprecationInfo string +}) + +userAPI.Get("/", versioning.NewMatcher(versioning.Map{ + "1.0": v10Handler, + // [...] +})) +``` + +This will make the handler to send these headers to the client: + +- `"X-API-Warn": options.WarnMessage` +- `"X-API-Deprecation-Date": context.FormatTime(ctx, options.DeprecationDate))` +- `"X-API-Deprecation-Info": options.DeprecationInfo` + +> versioning.DefaultDeprecationOptions can be passed instead if you don't care about Date and Info. + +## Grouping routes by version + +Grouping routes by version is possible as well. + +Using the `versioning.NewGroup(version string) *versioning.Group` function you can create a group to register your versioned routes. +The `versioning.RegisterGroups(r iris.Party, groups ...*versioning.Group)` must be called in the end in order to register the routes to a specific `Party`. + +```go +app := iris.New() + +userAPI := app.Party("/api/user") +// [... static serving, middlewares and etc goes here]. + +userAPIV10 := versioning.NewGroup("1.0") +userAPIV10.Get("/", sendHandler(v10Response)) + +userAPIV2 := versioning.NewGroup(">= 2, < 3") +userAPIV2.Get("/", sendHandler(v2Response)) +userAPIV2.Post("/", sendHandler(v2Response)) +userAPIV2.Put("/other", sendHandler(v2Response)) + +versioning.RegisterGroups(userAPI, userAPIV10, userAPIV2) +``` + +### Deprecation for Group + +Just call the `Deprecated(versioning.DeprecationOptions)` on the group you want to notify your API consumers that this specific version is deprecated. + +```go +userAPIV10 := versioning.NewGroup("1.0").Deprecated(versioning.DefaultDeprecationOptions) +``` + +### Version not found for Groups + +In order to register a custom version not found handler you have to use the `versioning.Concat` first, which gives you the API to add a version not found handler. + +```go +versioning.Concat(userAPIV10, userAPIV2).NotFound(func(ctx iris.Context) { + ctx.StatusCode(iris.StatusNotFound) + ctx.Writef("unknown version %s", versioning.GetVersion(ctx)) +}).For(userAPI) +``` ## Compare version manually from inside your handlers diff --git a/versioning/group.go b/versioning/group.go index 2d52c15c..77a23666 100644 --- a/versioning/group.go +++ b/versioning/group.go @@ -17,8 +17,9 @@ type ( } Group struct { - version string - routes []vroute + version string + extraMethods []string + routes []vroute deprecation DeprecationOptions } @@ -46,21 +47,40 @@ func (g *Group) Deprecated(options DeprecationOptions) *Group { return g } -// Handle registers a versioned route to the group. -// -// See `Concat` and `RegisterGroups` for more. -func (g *Group) Handle(method string, registeredPath string, handler context.Handler) { - if g.deprecation.ShouldHandle() { // if `Deprecated` called first. - handler = Deprecated(handler, g.deprecation) +func (g *Group) AllowMethods(methods ...string) *Group { + g.extraMethods = append(g.extraMethods, methods...) + return g +} + +func (g *Group) addVRoute(method, path string, handler context.Handler) { + for _, r := range g.routes { // check if already exists. + if r.method == method && r.path == path { + return + } } g.routes = append(g.routes, vroute{ method: method, - path: registeredPath, + path: path, versions: Map{g.version: handler}, }) } +// Handle registers a versioned route to the group. +// +// See `Concat` and `RegisterGroups` for more. +func (g *Group) Handle(method string, path string, handler context.Handler) { + if g.deprecation.ShouldHandle() { // if `Deprecated` called first. + handler = Deprecated(handler, g.deprecation) + } + + methods := append(g.extraMethods, method) + + for _, method := range methods { + g.addVRoute(method, path, handler) + } +} + // None registers an "offline" versioned route // see `context#ExecRoute(routeName)` and routing examples. func (g *Group) None(path string, handler context.Handler) { diff --git a/versioning/versioning_test.go b/versioning/versioning_test.go index ea31b344..84e27472 100644 --- a/versioning/versioning_test.go +++ b/versioning/versioning_test.go @@ -86,7 +86,7 @@ func TestNewGroup(t *testing.T) { userAPIV2.Post("/", sendHandler(v2Response)) userAPIV2.Put("/other", sendHandler(v2Response)) - // versioning.Concat(userAPIV10, userAPIV2) + // versioning.Concat(userAPIV10, userAPIV2). // NotFound(func(ctx iris.Context) { // ctx.StatusCode(iris.StatusNotFound) // ctx.Writef("unknown version %s", versioning.GetVersion(ctx)) From c74196c6d71dd6afaaeda7e230916e0bd51a555e Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Sun, 18 Nov 2018 02:41:24 +0200 Subject: [PATCH 09/12] finalize the API Former-commit-id: e680a9fc517c02eca66f83e42519c9820122fae8 --- versioning/README.md | 17 ++------ versioning/deprecation.go | 10 +++++ versioning/group.go | 80 +++++++++++++++-------------------- versioning/version.go | 28 ++++++++++-- versioning/versioning.go | 12 ++++++ versioning/versioning_test.go | 41 ++++++++++++------ 6 files changed, 114 insertions(+), 74 deletions(-) diff --git a/versioning/README.md b/versioning/README.md index e26fe07b..07af97ba 100644 --- a/versioning/README.md +++ b/versioning/README.md @@ -90,7 +90,7 @@ This will make the handler to send these headers to the client: Grouping routes by version is possible as well. Using the `versioning.NewGroup(version string) *versioning.Group` function you can create a group to register your versioned routes. -The `versioning.RegisterGroups(r iris.Party, groups ...*versioning.Group)` must be called in the end in order to register the routes to a specific `Party`. +The `versioning.RegisterGroups(r iris.Party, versionNotFoundHandler iris.Handler, groups ...*versioning.Group)` must be called in the end in order to register the routes to a specific `Party`. ```go app := iris.New() @@ -106,9 +106,11 @@ userAPIV2.Get("/", sendHandler(v2Response)) userAPIV2.Post("/", sendHandler(v2Response)) userAPIV2.Put("/other", sendHandler(v2Response)) -versioning.RegisterGroups(userAPI, userAPIV10, userAPIV2) +versioning.RegisterGroups(userAPI, versioning.NotFoundHandler, userAPIV10, userAPIV2) ``` +> A middleware can be registered to the actual `iris.Party` only, using the methods we learnt above, i.e by using the `versioning.Match` in order to detect what code/handler you want to be executed when "x" or no version is requested. + ### Deprecation for Group Just call the `Deprecated(versioning.DeprecationOptions)` on the group you want to notify your API consumers that this specific version is deprecated. @@ -117,17 +119,6 @@ Just call the `Deprecated(versioning.DeprecationOptions)` on the group you want userAPIV10 := versioning.NewGroup("1.0").Deprecated(versioning.DefaultDeprecationOptions) ``` -### Version not found for Groups - -In order to register a custom version not found handler you have to use the `versioning.Concat` first, which gives you the API to add a version not found handler. - -```go -versioning.Concat(userAPIV10, userAPIV2).NotFound(func(ctx iris.Context) { - ctx.StatusCode(iris.StatusNotFound) - ctx.Writef("unknown version %s", versioning.GetVersion(ctx)) -}).For(userAPI) -``` - ## Compare version manually from inside your handlers ```go diff --git a/versioning/deprecation.go b/versioning/deprecation.go index 3d8b5363..c7b8cd06 100644 --- a/versioning/deprecation.go +++ b/versioning/deprecation.go @@ -6,20 +6,30 @@ import ( "github.com/kataras/iris/context" ) +// DeprecationOptions describes the deprecation headers key-values. +// - "X-API-Warn": options.WarnMessage +// - "X-API-Deprecation-Date": context.FormatTime(ctx, options.DeprecationDate)) +// - "X-API-Deprecation-Info": options.DeprecationInfo type DeprecationOptions struct { WarnMessage string DeprecationDate time.Time DeprecationInfo string } +// ShouldHandle reports whether the deprecation headers should be present or no. func (opts DeprecationOptions) ShouldHandle() bool { return opts.WarnMessage != "" || !opts.DeprecationDate.IsZero() || opts.DeprecationInfo != "" } +// DefaultDeprecationOptions are the default deprecation options, +// it defaults the "X-API-Warn" header to a generic message. var DefaultDeprecationOptions = DeprecationOptions{ WarnMessage: "WARNING! You are using a deprecated version of this API.", } +// Deprecated marks a specific handler as a deprecated. +// Deprecated can be used to tell the clients that +// a newer version of that specific resource is available instead. func Deprecated(handler context.Handler, options DeprecationOptions) context.Handler { if options.WarnMessage == "" { options.WarnMessage = DefaultDeprecationOptions.WarnMessage diff --git a/versioning/group.go b/versioning/group.go index 77a23666..3ee4ef4c 100644 --- a/versioning/group.go +++ b/versioning/group.go @@ -1,14 +1,12 @@ package versioning import ( + "net/http" + "github.com/kataras/iris/context" "github.com/kataras/iris/core/router" ) -func RegisterGroups(r router.Party, groups ...*Group) []*router.Route { - return Concat(groups...).For(r) -} - type ( vroute struct { method string @@ -16,6 +14,8 @@ type ( versions Map } + // Group is a group of version-based routes. + // One version per one or more routes. Group struct { version string extraMethods []string @@ -25,6 +25,9 @@ type ( } ) +// NewGroup returns a ptr to Group based on the given "version". +// +// See `Handle` and `RegisterGroups` for more. func NewGroup(version string) *Group { return &Group{ version: version, @@ -33,7 +36,7 @@ func NewGroup(version string) *Group { // Deprecated marks this group and all its versioned routes // as deprecated versions of that endpoint. -// It can be called in the end just before `Concat` or `RegisterGroups` +// It can be called in the end just before `RegisterGroups` // or first by `NewGroup(...).Deprecated(...)`. It returns itself. func (g *Group) Deprecated(options DeprecationOptions) *Group { // if `Deprecated` is called in the end. @@ -47,13 +50,16 @@ func (g *Group) Deprecated(options DeprecationOptions) *Group { return g } +// AllowMethods can be called before `Handle/Get/Post...` +// to tell the underline router that all routes should be registered +// to these "methods" as well. func (g *Group) AllowMethods(methods ...string) *Group { g.extraMethods = append(g.extraMethods, methods...) return g } func (g *Group) addVRoute(method, path string, handler context.Handler) { - for _, r := range g.routes { // check if already exists. + for _, r := range g.routes { // check if route already exists. if r.method == method && r.path == path { return } @@ -67,8 +73,10 @@ func (g *Group) addVRoute(method, path string, handler context.Handler) { } // Handle registers a versioned route to the group. +// A call of `RegisterGroups` is necessary in order to register the actual routes +// when the group is complete. // -// See `Concat` and `RegisterGroups` for more. +// `RegisterGroups` for more. func (g *Group) Handle(method string, path string, handler context.Handler) { if g.deprecation.ShouldHandle() { // if `Deprecated` called first. handler = Deprecated(handler, g.deprecation) @@ -89,47 +97,47 @@ func (g *Group) None(path string, handler context.Handler) { // Get registers a versioned route for the Get http method. func (g *Group) Get(path string, handler context.Handler) { - g.Handle("GET", path, handler) + g.Handle(http.MethodGet, path, handler) } // Post registers a versioned route for the Post http method. func (g *Group) Post(path string, handler context.Handler) { - g.Handle("POST", path, handler) + g.Handle(http.MethodPost, path, handler) } // Put registers a versioned route for the Put http method func (g *Group) Put(path string, handler context.Handler) { - g.Handle("PUT", path, handler) + g.Handle(http.MethodPut, path, handler) } // Delete registers a versioned route for the Delete http method. func (g *Group) Delete(path string, handler context.Handler) { - g.Handle("DELETE", path, handler) + g.Handle(http.MethodDelete, path, handler) } // Connect registers a versioned route for the Connect http method. func (g *Group) Connect(path string, handler context.Handler) { - g.Handle("CONNECT", path, handler) + g.Handle(http.MethodConnect, path, handler) } // Head registers a versioned route for the Head http method. func (g *Group) Head(path string, handler context.Handler) { - g.Handle("HEAD", path, handler) + g.Handle(http.MethodHead, path, handler) } // Options registers a versioned route for the Options http method. func (g *Group) Options(path string, handler context.Handler) { - g.Handle("OPTIONS", path, handler) + g.Handle(http.MethodOptions, path, handler) } // Patch registers a versioned route for the Patch http method. func (g *Group) Patch(path string, handler context.Handler) { - g.Handle("PATCH", path, handler) + g.Handle(http.MethodPatch, path, handler) } // Trace registers a versioned route for the Trace http method. func (g *Group) Trace(path string, handler context.Handler) { - g.Handle("TRACE", path, handler) + g.Handle(http.MethodTrace, path, handler) } // Any registers a versioned route for ALL of the http methods @@ -146,49 +154,31 @@ func (g *Group) Any(registeredPath string, handler context.Handler) { g.Trace(registeredPath, handler) } -type Groups struct { - routes []vroute - - notFoundHandler context.Handler -} - -func Concat(groups ...*Group) *Groups { +// RegisterGroups registers one or more groups to an `iris.Party` or to the root router. +// See `NewGroup` and `NotFoundHandler` too. +func RegisterGroups(r router.Party, notFoundHandler context.Handler, groups ...*Group) (actualRoutes []*router.Route) { var total []vroute - for _, g := range groups { inner: for _, r := range g.routes { for i, tr := range total { if tr.method == r.method && tr.path == r.path { - for k, v := range r.versions { - total[i].versions[k] = v - } + total[i].versions[g.version] = r.versions[g.version] continue inner } } - } - total = append(total, g.routes...) + total = append(total, r) + } } - return &Groups{total, NotFoundHandler} -} - -func (g *Groups) NotFound(handler context.Handler) *Groups { - g.notFoundHandler = handler - return g -} - -func (g *Groups) For(r router.Party) (totalRoutesRegistered []*router.Route) { - for _, vr := range g.routes { - if g.notFoundHandler != nil { - vr.versions[NotFound] = g.notFoundHandler + for _, vr := range total { + if notFoundHandler != nil { + vr.versions[NotFound] = notFoundHandler } - // fmt.Printf("Method: %s | Path: %s | Versions: %#+v\n", vr.method, vr.path, vr.versions) - route := r.Handle(vr.method, vr.path, - NewMatcher(vr.versions)) - totalRoutesRegistered = append(totalRoutesRegistered, route) + route := r.Handle(vr.method, vr.path, NewMatcher(vr.versions)) + actualRoutes = append(actualRoutes, route) } return diff --git a/versioning/version.go b/versioning/version.go index 3de1f865..75d814ba 100644 --- a/versioning/version.go +++ b/versioning/version.go @@ -7,14 +7,28 @@ import ( ) const ( - AcceptVersionHeaderKey = "Accept-Version" - AcceptHeaderKey = "Accept" + // AcceptVersionHeaderKey is the header key of "Accept-Version". + AcceptVersionHeaderKey = "Accept-Version" + // AcceptHeaderKey is the header key of "Accept". + AcceptHeaderKey = "Accept" + // AcceptHeaderVersionValue is the Accept's header value search term the requested version. AcceptHeaderVersionValue = "version" - Key = "iris.api.version" // for use inside the ctx.Values(), not visible by the user. + // Key is the context key of the version, can be used to manually modify the "requested" version. + // Example of how you can change the default behavior to extract a requested version (which is by headers) + // from a "version" url parameter instead: + // func(ctx iris.Context) { // &version=1 + // ctx.Values().Set(versioning.Key, ctx.URLParamDefault("version", "1")) + // ctx.Next() + // } + Key = "iris.api.version" // for use inside the ctx.Values(), not visible by the user. + // NotFound is the key that can be used inside a `Map` or inside `ctx.Values().Set(versioning.Key, versioning.NotFound)` + // to tell that a version wasn't found, therefore the not found handler should handle the request instead. NotFound = Key + ".notfound" ) +// NotFoundHandler is the default version not found handler that +// is executed from `NewMatcher` when no version is registered as available to dispatch a resource. var NotFoundHandler = func(ctx context.Context) { // 303 is an option too, // end-dev has the chance to change that behavior by using the NotFound in the map: @@ -32,6 +46,14 @@ var NotFoundHandler = func(ctx context.Context) { ctx.WriteString("version not found") } +// GetVersion returns the current request version. +// +// By default the `GetVersion` will try to read from: +// - "Accept" header, i.e Accept: "application/json; version=1.0" +// - "Accept-Version" header, i.e Accept-Version: "1.0" +// +// However, the end developer can also set a custom version for a handler via a middleware by using the context's store key +// for versions (see `Key` for further details on that). func GetVersion(ctx context.Context) string { // firstly by context store, if manually set-ed by a middleware. if version := ctx.Values().GetString(Key); version != "" { diff --git a/versioning/versioning.go b/versioning/versioning.go index 5e2b8d84..ca280d1e 100644 --- a/versioning/versioning.go +++ b/versioning/versioning.go @@ -6,6 +6,8 @@ import ( "github.com/hashicorp/go-version" ) +// If reports whether the "version" is matching to the "is". +// the "is" can be a constraint like ">= 1, < 3". func If(v string, is string) bool { ver, err := version.NewVersion(v) if err != nil { @@ -20,12 +22,22 @@ func If(v string, is string) bool { return constraints.Check(ver) } +// Match acts exactly the same as `If` does but instead it accepts +// a Context, so it can be called by a handler to determinate the requested version. func Match(ctx context.Context, expectedVersion string) bool { return If(GetVersion(ctx), expectedVersion) } +// Map is a map of versions targets to a handlers, +// a handler per version or constraint, the key can be something like ">1, <=2" or just "1". type Map map[string]context.Handler +// NewMatcher creates a single handler which decides what handler +// should be executed based on the requested version. +// +// Use the `NewGroup` if you want to add many routes under a specific version. +// +// See `Map` and `NewGroup` too. func NewMatcher(versions Map) context.Handler { constraintsHandlers, notFoundHandler := buildConstraints(versions) diff --git a/versioning/versioning_test.go b/versioning/versioning_test.go index 84e27472..4f7d5600 100644 --- a/versioning/versioning_test.go +++ b/versioning/versioning_test.go @@ -79,25 +79,40 @@ func TestNewGroup(t *testing.T) { // [... static serving, middlewares and etc goes here]. userAPIV10 := versioning.NewGroup("1.0").Deprecated(versioning.DefaultDeprecationOptions) - userAPIV10.Get("/", sendHandler(v10Response)) + // V10middlewareResponse := "m1" + // userAPIV10.Use(func(ctx iris.Context) { + // println("exec userAPIV10.Use - midl1") + // sendHandler(V10middlewareResponse)(ctx) + // ctx.Next() + // }) + // userAPIV10.Use(func(ctx iris.Context) { + // println("exec userAPIV10.Use - midl2") + // sendHandler(V10middlewareResponse + "midl2")(ctx) + // ctx.Next() + // }) + // userAPIV10.Use(func(ctx iris.Context) { + // println("exec userAPIV10.Use - midl3") + // ctx.Next() + // }) + userAPIV10.Get("/", sendHandler(v10Response)) userAPIV2 := versioning.NewGroup(">= 2, < 3") + // V2middlewareResponse := "m2" + // userAPIV2.Use(func(ctx iris.Context) { + // println("exec userAPIV2.Use - midl1") + // sendHandler(V2middlewareResponse)(ctx) + // ctx.Next() + // }) + // userAPIV2.Use(func(ctx iris.Context) { + // println("exec userAPIV2.Use - midl2") + // ctx.Next() + // }) + userAPIV2.Get("/", sendHandler(v2Response)) userAPIV2.Post("/", sendHandler(v2Response)) userAPIV2.Put("/other", sendHandler(v2Response)) - // versioning.Concat(userAPIV10, userAPIV2). - // NotFound(func(ctx iris.Context) { - // ctx.StatusCode(iris.StatusNotFound) - // ctx.Writef("unknown version %s", versioning.GetVersion(ctx)) - // }). - // For(userAPI) - // This is legal too: - // For(app.PartyFunc("/api/user", func(r iris.Party) { - // // [... static serving, middlewares and etc goes here]. - // })) - - versioning.RegisterGroups(userAPI, userAPIV10, userAPIV2) + versioning.RegisterGroups(userAPI, versioning.NotFoundHandler, userAPIV10, userAPIV2) e := httptest.New(t, app) From b8addac888042ac966b592af79f51ae97d7277d6 Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Sun, 18 Nov 2018 03:06:59 +0200 Subject: [PATCH 10/12] add versioning example Former-commit-id: 54c903135d1c2ba1838b47fa419433dbb010c317 --- _examples/README.md | 5 +++ _examples/versioning/README.md | 1 + _examples/versioning/main.go | 73 ++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 _examples/versioning/README.md create mode 100644 _examples/versioning/main.go diff --git a/_examples/README.md b/_examples/README.md index fd0f4f7c..38a3faa2 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -158,6 +158,11 @@ Navigate through examples for a better understanding. * [per-route](routing/writing-a-middleware/per-route/main.go) * [globally](routing/writing-a-middleware/globally/main.go) +### Versioning + +- [How it works](https://github.com/kataras/iris/blob/master/versioning/README.md) +- [Example](versioning/main.go) + ### hero - [Basic](hero/basic/main.go) diff --git a/_examples/versioning/README.md b/_examples/versioning/README.md new file mode 100644 index 00000000..d45f42d3 --- /dev/null +++ b/_examples/versioning/README.md @@ -0,0 +1 @@ +Head over to the [kataras/iris/versioning/README.md](https://github.com/kataras/iris/blob/master/versioning/README.md) instead. \ No newline at end of file diff --git a/_examples/versioning/main.go b/_examples/versioning/main.go new file mode 100644 index 00000000..8516b190 --- /dev/null +++ b/_examples/versioning/main.go @@ -0,0 +1,73 @@ +package main + +import ( + "github.com/kataras/iris" + "github.com/kataras/iris/versioning" +) + +func main() { + app := iris.New() + + examplePerRoute(app) + examplePerParty(app) + + // Read the README.md before any action. + app.Run(iris.Addr(":8080")) +} + +// How to test: +// Open Postman +// GET: localhost:8080/api/cats +// Headers[1] = Accept-Version: "1" and repeat with +// Headers[1] = Accept-Version: "2.5" +// or even "Accept": "application/json; version=2.5" +func examplePerRoute(app *iris.Application) { + app.Get("/api/cats", versioning.NewMatcher(versioning.Map{ + "1": catsVersionExactly1Handler, + ">= 2, < 3": catsV2Handler, + versioning.NotFound: versioning.NotFoundHandler, + })) +} + +// How to test: +// Open Postman +// GET: localhost:8080/api/users +// Headers[1] = Accept-Version: "1.9.9" and repeat with +// Headers[1] = Accept-Version: "2.5" +// +// POST: localhost:8080/api/users/new +// Headers[1] = Accept-Version: "1.8.3" +// +// POST: localhost:8080/api/users +// Headers[1] = Accept-Version: "2" +func examplePerParty(app *iris.Application) { + usersAPI := app.Party("/api/users") + + // version 1. + usersAPIV1 := versioning.NewGroup(">= 1, < 2") + usersAPIV1.Get("/", func(ctx iris.Context) { + ctx.Writef("v1 resource: /api/users handler") + }) + usersAPIV1.Post("/new", func(ctx iris.Context) { + ctx.Writef("v1 resource: /api/users/new post handler") + }) + + // version 2. + usersAPIV2 := versioning.NewGroup(">= 2, < 3") + usersAPIV2.Get("/", func(ctx iris.Context) { + ctx.Writef("v2 resource: /api/users handler") + }) + usersAPIV2.Post("/", func(ctx iris.Context) { + ctx.Writef("v2 resource: /api/users post handler") + }) + + versioning.RegisterGroups(usersAPI, versioning.NotFoundHandler, usersAPIV1, usersAPIV2) +} + +func catsVersionExactly1Handler(ctx iris.Context) { + ctx.Writef("v1 exactly resource: /api/cats handler") +} + +func catsV2Handler(ctx iris.Context) { + ctx.Writef("v2 resource: /api/cats handler") +} From 18b236a1ee2911ddbe6fd9645eeb30a179b0b7ee Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Sun, 18 Nov 2018 03:09:17 +0200 Subject: [PATCH 11/12] add AUTHORS note to the new versioning package Former-commit-id: f14579297e6662ae88f2fd33cce613b9c262ec13 --- versioning/AUTHORS | 1 + versioning/LICENSE | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 versioning/AUTHORS create mode 100644 versioning/LICENSE diff --git a/versioning/AUTHORS b/versioning/AUTHORS new file mode 100644 index 00000000..848245bb --- /dev/null +++ b/versioning/AUTHORS @@ -0,0 +1 @@ +Gerasimos Maropoulos diff --git a/versioning/LICENSE b/versioning/LICENSE new file mode 100644 index 00000000..469fb44d --- /dev/null +++ b/versioning/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2018 Gerasimos Maropoulos. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Iris nor the names of its +contributor, Gerasimos Maropoulos, may be used to endorse or promote products +derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file From c0f48385a83ee8e7f4d30baa3b795a07c740bf0b Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Sun, 18 Nov 2018 03:26:48 +0200 Subject: [PATCH 12/12] Publish version 11.1.0. Read HISTORY.md for details Former-commit-id: 5bc97f3093655d08e07cfa0a27decda98b6d300f --- Gopkg.lock | 6 ++ Gopkg.toml | 4 ++ HISTORY.md | 149 +++++++++++++++++++++++++++++++++++++++++++ HISTORY_GR.md | 4 ++ HISTORY_ID.md | 4 ++ HISTORY_ZH.md | 4 ++ README.md | 8 ++- README_GR.md | 4 +- README_ID.md | 4 +- README_JPN.md | 4 +- README_PT_BR.md | 4 +- README_RU.md | 4 +- README_ZH.md | 4 +- VERSION | 2 +- doc.go | 2 +- iris.go | 2 +- versioning/README.md | 3 +- 17 files changed, 193 insertions(+), 19 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 112390bc..09d3684c 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -289,6 +289,12 @@ packages = ["internal","redis"] revision = "2cd21d9966bf7ff9ae091419744f0b3fb0fecace" +[[projects]] + branch = "master" + name = "github.com/hashicorp/go-version" + packages = ["."] + revision = "b5a281d3160aa11950a6182bd9a9dc2cb1e02d50" + [solve-meta] analyzer-name = "dep" analyzer-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index c757d7a7..899be2c4 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -89,3 +89,7 @@ [[constraint]] branch = "master" name = "github.com/gomodule/redigo" + +[[constraint]] + name = "github.com/hashicorp/go-version" + version = "1.0.0" \ No newline at end of file diff --git a/HISTORY.md b/HISTORY.md index 7d1bb7cf..ef56dacc 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -17,6 +17,155 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene **How to upgrade**: Open your command-line and execute this command: `go get -u github.com/kataras/iris` or let the automatic updater do that for you. +# Su, 18 November 2018 | v11.1.0 + +PR: https://github.com/kataras/iris/pull/1130 + +This release contains a new feature for versioning your Iris APIs. The initial motivation and feature request came by https://github.com/kataras/iris/issues/1129. + +The [versioning](https://github.com/kataras/iris/tree/master/versioning) package provides [semver](https://semver.org/) versioning for your APIs. It implements all the suggestions written at [api-guidelines](https://github.com/byrondover/api-guidelines/blob/master/Guidelines.md#versioning) and more. + + +The version comparison is done by the [go-version](https://github.com/hashicorp/go-version) package. It supports matching over patterns like `">= 1.0, < 3"` and etc. + +## Features + +- per route version matching, a normal iris handler with "switch" cases via Map for version => handler +- per group versioned routes and deprecation API +- version matching like ">= 1.0, < 2.0" or just "2.0.1" and etc. +- version not found handler (can be customized by simply adding the versioning.NotFound: customNotMatchVersionHandler on the Map) +- version is retrieved from the "Accept" and "Accept-Version" headers (can be customized via middleware) +- respond with "X-API-Version" header, if version found. +- deprecation options with customizable "X-API-Warn", "X-API-Deprecation-Date", "X-API-Deprecation-Info" headers via `Deprecated` wrapper. + +## Get version + +Current request version is retrieved by `versioning.GetVersion(ctx)`. + +By default the `GetVersion` will try to read from: +- `Accept` header, i.e `Accept: "application/json; version=1.0"` +- `Accept-Version` header, i.e `Accept-Version: "1.0"` + +You can also set a custom version for a handler via a middleware by using the context's store values. +For example: +```go +func(ctx iris.Context) { + ctx.Values().Set(versioning.Key, ctx.URLParamDefault("version", "1.0")) + ctx.Next() +} +``` + +## Match version to handler + +The `versioning.NewMatcher(versioning.Map) iris.Handler` creates a single handler which decides what handler need to be executed based on the requested version. + +```go +app := iris.New() + +// middleware for all versions. +myMiddleware := func(ctx iris.Context) { + // [...] + ctx.Next() +} + +myCustomNotVersionFound := func(ctx iris.Context) { + ctx.StatusCode(404) + ctx.Writef("%s version not found", versioning.GetVersion(ctx)) +} + +userAPI := app.Party("/api/user") +userAPI.Get("/", myMiddleware, versioning.NewMatcher(versioning.Map{ + "1.0": sendHandler(v10Response), + ">= 2, < 3": sendHandler(v2Response), + versioning.NotFound: myCustomNotVersionFound, +})) +``` + +### Deprecation + +Using the `versioning.Deprecated(handler iris.Handler, options versioning.DeprecationOptions) iris.Handler` function you can mark a specific handler version as deprecated. + + +```go +v10Handler := versioning.Deprecated(sendHandler(v10Response), versioning.DeprecationOptions{ + // if empty defaults to: "WARNING! You are using a deprecated version of this API." + WarnMessage string + DeprecationDate time.Time + DeprecationInfo string +}) + +userAPI.Get("/", versioning.NewMatcher(versioning.Map{ + "1.0": v10Handler, + // [...] +})) +``` + +This will make the handler to send these headers to the client: + +- `"X-API-Warn": options.WarnMessage` +- `"X-API-Deprecation-Date": context.FormatTime(ctx, options.DeprecationDate))` +- `"X-API-Deprecation-Info": options.DeprecationInfo` + +> versioning.DefaultDeprecationOptions can be passed instead if you don't care about Date and Info. + +## Grouping routes by version + +Grouping routes by version is possible as well. + +Using the `versioning.NewGroup(version string) *versioning.Group` function you can create a group to register your versioned routes. +The `versioning.RegisterGroups(r iris.Party, versionNotFoundHandler iris.Handler, groups ...*versioning.Group)` must be called in the end in order to register the routes to a specific `Party`. + +```go +app := iris.New() + +userAPI := app.Party("/api/user") +// [... static serving, middlewares and etc goes here]. + +userAPIV10 := versioning.NewGroup("1.0") +userAPIV10.Get("/", sendHandler(v10Response)) + +userAPIV2 := versioning.NewGroup(">= 2, < 3") +userAPIV2.Get("/", sendHandler(v2Response)) +userAPIV2.Post("/", sendHandler(v2Response)) +userAPIV2.Put("/other", sendHandler(v2Response)) + +versioning.RegisterGroups(userAPI, versioning.NotFoundHandler, userAPIV10, userAPIV2) +``` + +> A middleware can be registered to the actual `iris.Party` only, using the methods we learnt above, i.e by using the `versioning.Match` in order to detect what code/handler you want to be executed when "x" or no version is requested. + +### Deprecation for Group + +Just call the `Deprecated(versioning.DeprecationOptions)` on the group you want to notify your API consumers that this specific version is deprecated. + +```go +userAPIV10 := versioning.NewGroup("1.0").Deprecated(versioning.DefaultDeprecationOptions) +``` + +## Compare version manually from inside your handlers + +```go +// reports if the "version" is matching to the "is". +// the "is" can be a constraint like ">= 1, < 3". +If(version string, is string) bool +``` + +```go +// same as `If` but expects a Context to read the requested version. +Match(ctx iris.Context, expectedVersion string) bool +``` + +```go +app.Get("/api/user", func(ctx iris.Context) { + if versioning.Match(ctx, ">= 2.2.3") { + // [logic for >= 2.2.3 version of your handler goes here] + return + } +}) +``` + +Example can be found [here](_examples/versioning/main.go). + # Fr, 09 November 2018 | v11.0.4 Add `Configuration.DisablePathCorrectionRedirection` - `iris.WithoutPathCorrectionRedirection` to support diff --git a/HISTORY_GR.md b/HISTORY_GR.md index c16bbf7b..021ee5ad 100644 --- a/HISTORY_GR.md +++ b/HISTORY_GR.md @@ -17,6 +17,10 @@ **Πώς να αναβαθμίσετε**: Ανοίξτε την γραμμή εντολών σας και εκτελέστε αυτήν την εντολή: `go get -u github.com/kataras/iris` ή αφήστε το αυτόματο updater να το κάνει αυτό για σας. +# Su, 18 November 2018 | v11.1.0 + +Πατήστε [εδώ](https://github.com/kataras/iris/blob/master/HISTORY.md#su-18-november-2018--v1110) για να διαβάσετε στα αγγλικά για το νέο "versioning" feature. + # Fr, 09 November 2018 | v11.0.4 Πατήστε [εδώ](https://github.com/kataras/iris/blob/master/HISTORY.md#fr-09-november-2018--v1104) για να διαβάσετε στα αγγλικά τις αλλαγές που φέρνει το τελευταίο patch για την έκδοση 11. diff --git a/HISTORY_ID.md b/HISTORY_ID.md index ed48b3a2..414dc195 100644 --- a/HISTORY_ID.md +++ b/HISTORY_ID.md @@ -17,6 +17,10 @@ Developers tidak diwajibkan untuk melakukan upgrade apabila mereka tidak membutu **Cara Upgrade**: Bukan command-line anda dan eksekuis perintah ini: `go get -u github.com/kataras/iris` atau biarkan updater otomatis melakukannya untuk anda. +# Su, 18 November 2018 | v11.1.0 + +This history entry is not translated yet to the Indonesian language yet, please refer to the english version of the [HISTORY entry](https://github.com/kataras/iris/blob/master/HISTORY.md#su-18-november-2018--v1110) instead. + # Fr, 09 November 2018 | v11.0.4 This history entry is not translated yet to the Indonesian language yet, please refer to the english version of the [HISTORY entry](https://github.com/kataras/iris/blob/master/HISTORY.md#fr-09-november-2018--v1104) instead. diff --git a/HISTORY_ZH.md b/HISTORY_ZH.md index 6ad12ef6..4b5af7d1 100644 --- a/HISTORY_ZH.md +++ b/HISTORY_ZH.md @@ -17,6 +17,10 @@ **如何升级**: 打开命令行执行以下命令: `go get -u github.com/kataras/iris` 或者等待自动更新。 +# Su, 18 November 2018 | v11.1.0 + +This history entry is not translated yet to the Chinese language yet, please refer to the english version of the [HISTORY entry](https://github.com/kataras/iris/blob/master/HISTORY.md#su-18-november-2018--v1110) instead. + # Fr, 09 November 2018 | v11.0.4 This history entry is not translated yet to the Chinese language yet, please refer to the english version of the [HISTORY entry](https://github.com/kataras/iris/blob/master/HISTORY.md#fr-09-november-2018--v1104) instead. diff --git a/README.md b/README.md index db03b61f..9c29b829 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,12 @@ +# ⚡️ Update: community-driven version 11.1.0 + +Click [here](HISTORY.md#su-18-november-2018--v1110) to read about the versioning API that the most recent version of Iris brings to you. + # Iris Web Framework -[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris) [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/routing%20by-example-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.0-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases) +[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris) [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/routing%20by-example-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.1-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases) Iris is a fast, simple yet fully featured and very efficient web framework for Go. @@ -1013,7 +1017,7 @@ Iris, unlike others, is 100% compatible with the standards and that's why the ma ## Support -- [HISTORY](HISTORY.md#fr-09-november-2018--v1104) file is your best friend, it contains information about the latest features and changes +- [HISTORY](HISTORY.md#su-18-november-2018--v1110) file is your best friend, it contains information about the latest features and changes - Did you happen to find a bug? Post it at [github issues](https://github.com/kataras/iris/issues) - Do you have any questions or need to speak with someone experienced to solve a problem at real-time? Join us to the [community chat](https://chat.iris-go.com) - Complete our form-based user experience report by clicking [here](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link) diff --git a/README_GR.md b/README_GR.md index f824e03a..d5c5321a 100644 --- a/README_GR.md +++ b/README_GR.md @@ -2,7 +2,7 @@ -[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris) [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.0-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases) +[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris) [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.1-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases) Το Iris είναι ένα γρήγορο, απλό αλλά και πλήρως λειτουργικό και πολύ αποδοτικό web framework για τη Go. @@ -108,7 +108,7 @@ _Η τελευταία ενημέρωση έγινε την [Τρίτη, 21 Νο ## Υποστήριξη -- To [HISTORY](HISTORY_GR.md#fr-09-november-2018--v1104) αρχείο είναι ο καλύτερος σας φίλος, περιέχει πληροφορίες σχετικά με τις τελευταίες λειτουργίες(features) και αλλαγές +- To [HISTORY](HISTORY_GR.md#su-18-november-2018--v1110) αρχείο είναι ο καλύτερος σας φίλος, περιέχει πληροφορίες σχετικά με τις τελευταίες λειτουργίες(features) και αλλαγές - Μήπως τυχαίνει να βρήκατε κάποιο bug; Δημοσιεύστε το στα [github issues](https://github.com/kataras/iris/issues) - Έχετε οποιεσδήποτε ερωτήσεις ή πρέπει να μιλήσετε με κάποιον έμπειρο για την επίλυση ενός προβλήματος σε πραγματικό χρόνο; Ελάτε μαζί μας στην [συνομιλία κοινότητας](https://chat.iris-go.com) - Συμπληρώστε την αναφορά εμπειρίας χρήστη κάνοντας κλικ [εδώ](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link) diff --git a/README_ID.md b/README_ID.md index 085c421c..30a7075b 100644 --- a/README_ID.md +++ b/README_ID.md @@ -2,7 +2,7 @@ -[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris) [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.0-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases) +[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris) [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.1-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases) Iris adalah web framework yang cepat, sederhana namun berfitur lengkap dan sangat efisien untuk Go. @@ -106,7 +106,7 @@ _Diperbarui pada: [Tuesday, 21 November 2017](_benchmarks/README_UNIX.md)_ ## Dukungan -- File [HISTORY](HISTORY_ID.md#fr-09-november-2018--v1104) adalah sahabat anda, file tersebut memiliki informasi terkait fitur dan perubahan terbaru +- File [HISTORY](HISTORY_ID.md#su-18-november-2018--v1110) adalah sahabat anda, file tersebut memiliki informasi terkait fitur dan perubahan terbaru - Apakah anda menemukan bug? Laporkan itu melalui [github issues](https://github.com/kataras/iris/issues) - Apakah anda memiliki pertanyaan atau butuh untuk bicara kepada seseorang yang sudah berpengalaman untuk menyelesaikan masalah secara langsung? Gabung bersama kami di [community chat](https://chat.iris-go.com) - Lengkapi laporan user-experience berbasis formulir kami dengan tekan [disini](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link) diff --git a/README_JPN.md b/README_JPN.md index 151a904f..c46c95d4 100644 --- a/README_JPN.md +++ b/README_JPN.md @@ -2,7 +2,7 @@ -[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris) [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.0-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases) +[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris) [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.1-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases) Irisはシンプルで高速、それにも関わらず充実した機能を有する効率的なGo言語のウェブフレームワークです。 @@ -106,7 +106,7 @@ _Updated at: [Tuesday, 21 November 2017](_benchmarks/README_UNIX.md)_ ## 支援 -- [HISTORY](HISTORY.md#fr-09-november-2018--v1104)ファイルはあなたの友人です。このファイルには、機能に関する最新の情報や変更点が記載されています。 +- [HISTORY](HISTORY.md#su-18-november-2018--v1110)ファイルはあなたの友人です。このファイルには、機能に関する最新の情報や変更点が記載されています。 - バグを発見しましたか?[github issues](https://github.com/kataras/iris/issues)に投稿をお願い致します。 - 質問がありますか?または問題を即時に解決するため、熟練者に相談する必要がありますか?[community chat](https://chat.iris-go.com)に参加しましょう。 - [here](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link)をクリックしてユーザーとしての体験を報告しましょう。 diff --git a/README_PT_BR.md b/README_PT_BR.md index e2068d8a..4b637fea 100644 --- a/README_PT_BR.md +++ b/README_PT_BR.md @@ -2,7 +2,7 @@ -[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris) [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.0-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases) +[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris) [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.1-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases) Iris é um framework rápido, simples porém completo e muito eficiente para a linguagem Go. @@ -106,7 +106,7 @@ _Atualizado em : [Terça, 21 de Novembro de 2017](_benchmarks/README_UNIX.md)_ ## Apoie -- [HISTORY](HISTORY.md#fr-09-november-2018--v1104) o arquivo HISTORY é o seu melhor amigo, ele contém informações sobre as últimas features e mudanças. +- [HISTORY](HISTORY.md#su-18-november-2018--v1110) o arquivo HISTORY é o seu melhor amigo, ele contém informações sobre as últimas features e mudanças. - Econtrou algum bug ? Poste-o nas [issues](https://github.com/kataras/iris/issues) - Possui alguma dúvida ou gostaria de falar com alguém experiente para resolver seu problema em tempo real ? Junte-se ao [chat da nossa comunidade](https://chat.iris-go.com). - Complete nosso formulário de experiência do usuário clicando [aqui](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link) diff --git a/README_RU.md b/README_RU.md index f791bb72..4a8e9287 100644 --- a/README_RU.md +++ b/README_RU.md @@ -2,7 +2,7 @@ -[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris) [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.0-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases) +[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris) [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.1-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases) Iris - это быстрая, простая, но полнофункциональная и очень эффективная веб-платформа для Go. @@ -106,7 +106,7 @@ _Обновлено: [Вторник, 21 ноября 2017 г.](_benchmarks/READ ## Поддержка -- Файл [HISTORY](HISTORY.md#fr-09-november-2018--v1104) - ваш лучший друг, он содержит информацию о последних особенностях и всех изменениях +- Файл [HISTORY](HISTORY.md#su-18-november-2018--v1110) - ваш лучший друг, он содержит информацию о последних особенностях и всех изменениях - Вы случайно обнаружили ошибку? Опубликуйте ее на [Github вопросы](https://github.com/kataras/iris/issues) - У Вас есть какие-либо вопросы или Вам нужно поговорить с кем-то, кто бы смог решить Вашу проблему в режиме реального времени? Присоединяйтесь к нам в [чате сообщества](https://chat.iris-go.com) - Заполните наш отчет о пользовательском опыте на основе формы, нажав [здесь](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link) diff --git a/README_ZH.md b/README_ZH.md index b8140b84..4d8be6f8 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -2,7 +2,7 @@ -[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris) [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.0-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases) +[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris) [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.1-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases) Iris 是一款超快、简洁高效的 Go 语言 Web开发框架。 @@ -102,7 +102,7 @@ _更新于: [2017年11月21日星期二](_benchmarks/README_UNIX.md)_ ## 支持 -- [更新记录](HISTORY_ZH.md#fr-09-november-2018--v1104) 是您最好的朋友,它包含有关最新功能和更改的信息 +- [更新记录](HISTORY_ZH.md#su-18-november-2018--v1110) 是您最好的朋友,它包含有关最新功能和更改的信息 - 你碰巧找到了一个错误? 请提交 [github issues](https://github.com/kataras/iris/issues) - 您是否有任何疑问或需要与有经验的人士交谈以实时解决问题? [加入我们的聊天](https://chat.iris-go.com) - [点击这里完成我们基于表单的用户体验报告](https://docs.google.com/forms/d/e/1FAIpQLSdCxZXPANg_xHWil4kVAdhmh7EBBHQZ_4_xSZVDL-oCC_z5pA/viewform?usp=sf_link) diff --git a/VERSION b/VERSION index 6c5393d1..59226bc2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -11.0.4:https://github.com/kataras/iris/blob/master/HISTORY.md#fr-09-november-2018--v1104 \ No newline at end of file +11.1.0:https://github.com/kataras/iris/blob/master/HISTORY.md#su-18-november-2018--v1110 \ No newline at end of file diff --git a/doc.go b/doc.go index 161b159b..81aa5854 100644 --- a/doc.go +++ b/doc.go @@ -35,7 +35,7 @@ Source code and other details for the project are available at GitHub: Current Version -11.0.4 +11.1.0 Installation diff --git a/iris.go b/iris.go index e785d016..0459d360 100644 --- a/iris.go +++ b/iris.go @@ -33,7 +33,7 @@ import ( var ( // Version is the current version number of the Iris Web Framework. - Version = "11.0.4" + Version = "11.1.0" ) // HTTP status codes as registered with IANA. diff --git a/versioning/README.md b/versioning/README.md index 07af97ba..01a629ad 100644 --- a/versioning/README.md +++ b/versioning/README.md @@ -139,5 +139,4 @@ app.Get("/api/user", func(ctx iris.Context) { return } }) -``` - +``` \ No newline at end of file