package versioning import ( "net/http" "github.com/kataras/iris/v12/context" "github.com/kataras/iris/v12/core/router" ) type ( vroute struct { method string path string versions Map } // Group is a group of version-based routes. // One version per one or more routes. Group struct { version string extraMethods []string routes []vroute deprecation DeprecationOptions } ) // 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, } } // Deprecated marks this group and all its versioned routes // as deprecated versions of that endpoint. // 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. 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 } // 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 route already exists. if r.method == method && r.path == path { return } } g.routes = append(g.routes, vroute{ method: method, path: path, versions: Map{g.version: 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. // // `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) { 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(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(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(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(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(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(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(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(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(http.MethodTrace, 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) } // 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 { total[i].versions[g.version] = r.versions[g.version] continue inner } } total = append(total, r) } } for _, vr := range total { if notFoundHandler != nil { vr.versions[NotFound] = notFoundHandler } route := r.Handle(vr.method, vr.path, NewMatcher(vr.versions)) actualRoutes = append(actualRoutes, route) } return }