Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 14 additions & 15 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,28 @@ on:
push:

jobs:

build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.24'
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: "1.25"

- name: Build
run: make build v=1
- name: Build
run: make build v=1

test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.24'
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: "1.25"

- name: Test
run: make test
- name: Test
run: make test
3 changes: 3 additions & 0 deletions examples/group/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# group

create router groups using the amp mux.
20 changes: 20 additions & 0 deletions examples/group/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package main

import (
"log"

"github.com/joseph-beck/amp/pkg/amp"
)

func main() {
g := amp.Group("/group")
g.Get("/hello", func(ctx *amp.Ctx) error {
return ctx.Render(200, "hello world!")
})

a := amp.New()

a.Group(g)

log.Fatalln(a.ListenAndServe())
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/joseph-beck/amp

go 1.24.0
go 1.25

require github.com/stretchr/testify v1.9.0

Expand Down
2 changes: 1 addition & 1 deletion pkg/amp/ctx.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Github Repository: https://github.com/joseph-beck/amp
// GitHub Repository: https://github.com/joseph-beck/amp
// GoDocs: https://pkg.go.dev/github.com/joseph-beck/amp

// Package Amp is a web framework made using the Go 1.22 Mux.
Expand Down
161 changes: 161 additions & 0 deletions pkg/amp/group.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// GitHub Repository: https://github.com/joseph-beck/amp
// GoDocs: https://pkg.go.dev/github.com/joseph-beck/amp

// Package Amp is a web framework made using the Go 1.22 Mux.
// Please ensure you are using Go 1.22, minimum, when using Amp.
package amp

// Group struct, used to group routes with a common prefix and middleware.
type group struct {
// Prefix used for all routes in this group.
// For example "/api/v1"
prefix string

// All handlers used in the group.
// This internal struct stores the method, path, handler and any middleware for each handler.
handlers []handlerConfig

// All middlewares that are applied to this group.
// These are applied before the route specific middleware.
middleware []Handler
}

// Internal struct to store handler configuration within a group.
type handlerConfig struct {
// HTTP method of the handler
// e.g. GET, POST, PUT, DELETE, etc.
method string

// Path of the handler.
// This is without the group prefix.
path string

// The handler function itself.
handler Handler

// Any middleware specific to this route.
// These are applied after the group middleware.
middleware []Handler
}

// Create a new group with a given prefix and optional middleware.
// Uses a variadic variable here, can give one or many middlewares here.
// All routes in this group will use these middlewares.
//
// g := amp.Group("/group")
// g.Get("/hello", func(ctx *amp.Ctx) error {
// return ctx.Render(200, "hello world!")
// })
//
// a := amp.New()
//
// a.Group(g)
//
// Can now use the route /group/hello
func Group(prefix string, middleware ...Handler) group {
return group{
prefix: prefix,
handlers: make([]handlerConfig, 0),
middleware: middleware,
}
}

// Internal handler function, shortcut for generating a handlerConfig.
// Appends handlerConfig to the group.
func (g *group) handler(method string, path string, handler Handler, middleware ...Handler) {
g.handlers = append(g.handlers, handlerConfig{
method,
path,
handler,
middleware,
})
}

// Adds middleware to the group.
// Uses a variadic variable here, can give one or many middlewares here.
// All routes in this group will use these middlewares.
func (g *group) Use(middleware ...Handler) {
g.middleware = append(g.middleware, middleware...)
}

// Get the prefix of the group.
func (g group) Prefix() string {
return g.prefix
}

// Generic handler, this can be used for a variety of http methods unlike specified ones, like Get.
// All given middleware will only be applied to this route.
// Will likely have to use a switch case statement within the handler to specify method.
//
// func(ctx *amp.Ctx) error {
// switch ctx.Method() {
// case "GET":
// ...
// default:
// ...
// }
// }
//
// Generally recommended to use a specified method.
func (g *group) Handler(path string, handler Handler, middleware ...Handler) {
g.handler("HANDLER", path, handler, middleware...)
}

// Create a Get route with a given path, handler and optional middleware.
// All given middleware will only be applied to this route.
// Get requests should be used to retrieve data.
func (g *group) Get(path string, handler Handler, middleware ...Handler) {
g.handler("GET", path, handler, middleware...)
}

// Create a Post route with a given path, handler and optional middleware.
// All given middleware will only be applied to this route.
// Post methods should be used for posting data or changing state.
func (g *group) Post(path string, handler Handler, middleware ...Handler) {
g.handler("POST", path, handler, middleware...)
}

// Create a Put route with a given path, handler and optional middleware.
// All given middleware will only be applied to this route.
// Put methods should be used for posting data, changing state or updating state.
func (g *group) Put(path string, handler Handler, middleware ...Handler) {
g.handler("PUT", path, handler, middleware...)
}

// Create a Patch route with a given path, handler and optional middleware.
// All given middleware will only be applied to this route.
// Patch methods should be used for changing state or updating data.
func (g *group) Patch(path string, handler Handler, middleware ...Handler) {
g.handler("PATCH", path, handler, middleware...)
}

// Create a Delete route with a given path, handler and optional middleware.
// All given middleware will only be applied to this route.
// Delete methods should be used for deleting data or a piece of state.
func (g *group) Delete(path string, handler Handler, middleware ...Handler) {
g.handler("DELETE", path, handler, middleware...)
}

// Create a Head route with a given path, handler and optional middleware.
// All given middleware will only be applied to this route.
func (g *group) Head(path string, handler Handler, middleware ...Handler) {
g.handler("HEAD", path, handler, middleware...)
}

// Create an Options route with a given path, handler and optional middleware.
// All given middleware will only be applied to this route.
func (g *group) Options(path string, handler Handler, middleware ...Handler) {
g.handler("OPTIONS", path, handler, middleware...)
}

// Create a Connect route with a given path, handler and optional middleware.
// All given middleware will only be applied to this route.
func (g *group) Connect(path string, handler Handler, middleware ...Handler) {
g.handler("CONNECT", path, handler, middleware...)
}

// Create a Trace route with a given path, handler and optional middleware.
// All given middleware will only be applied to this route.
func (g *group) Trace(path string, handler Handler, middleware ...Handler) {
g.handler("TRACE", path, handler, middleware...)
}
149 changes: 149 additions & 0 deletions pkg/amp/group_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package amp

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestGroup(t *testing.T) {
{
g := Group("/test")

assert.Equal(t, "/test", g.prefix)
assert.Equal(t, 0, len(g.handlers))
assert.Equal(t, 0, len(g.middleware))
}

{
g := Group("/test", func(ctx *Ctx) error { return nil })

assert.Equal(t, "/test", g.prefix)
assert.Equal(t, 0, len(g.handlers))
assert.Equal(t, 1, len(g.middleware))
}
}

func TestGroupUse(t *testing.T) {
g := Group("/test")

g.Use(func(ctx *Ctx) error { return nil })

assert.Equal(t, 1, len(g.middleware))
}

func TestGroupPrefix(t *testing.T) {
g := Group("/test")

assert.Equal(t, "/test", g.Prefix())
}

func TestGroupHandler(t *testing.T) {
g := Group("/test")

g.Handler("/hello", func(ctx *Ctx) error { return nil })

assert.Equal(t, 1, len(g.handlers))
assert.Equal(t, "HANDLER", g.handlers[0].method)
assert.Equal(t, "/hello", g.handlers[0].path)
assert.Equal(t, 0, len(g.handlers[0].middleware))
}

func TestGroupGet(t *testing.T) {
g := Group("/test")

g.Get("/hello", func(ctx *Ctx) error { return nil })

assert.Equal(t, 1, len(g.handlers))
assert.Equal(t, "GET", g.handlers[0].method)
assert.Equal(t, "/hello", g.handlers[0].path)
assert.Equal(t, 0, len(g.handlers[0].middleware))
}

func TestGroupPost(t *testing.T) {
g := Group("/test")

g.Post("/hello", func(ctx *Ctx) error { return nil })

assert.Equal(t, 1, len(g.handlers))
assert.Equal(t, "POST", g.handlers[0].method)
assert.Equal(t, "/hello", g.handlers[0].path)
assert.Equal(t, 0, len(g.handlers[0].middleware))
}

func TestGroupPut(t *testing.T) {
g := Group("/test")

g.Put("/hello", func(ctx *Ctx) error { return nil })

assert.Equal(t, 1, len(g.handlers))
assert.Equal(t, "PUT", g.handlers[0].method)
assert.Equal(t, "/hello", g.handlers[0].path)
assert.Equal(t, 0, len(g.handlers[0].middleware))
}

func TestGroupPatch(t *testing.T) {
g := Group("/test")

g.Patch("/hello", func(ctx *Ctx) error { return nil })

assert.Equal(t, 1, len(g.handlers))
assert.Equal(t, "PATCH", g.handlers[0].method)
assert.Equal(t, "/hello", g.handlers[0].path)
assert.Equal(t, 0, len(g.handlers[0].middleware))
}

func TestGroupDelete(t *testing.T) {
g := Group("/test")

g.Delete("/hello", func(ctx *Ctx) error { return nil })

assert.Equal(t, 1, len(g.handlers))
assert.Equal(t, "DELETE", g.handlers[0].method)
assert.Equal(t, "/hello", g.handlers[0].path)
assert.Equal(t, 0, len(g.handlers[0].middleware))
}

func TestGroupHead(t *testing.T) {
g := Group("/test")

g.Head("/hello", func(ctx *Ctx) error { return nil })

assert.Equal(t, 1, len(g.handlers))
assert.Equal(t, "HEAD", g.handlers[0].method)
assert.Equal(t, "/hello", g.handlers[0].path)
assert.Equal(t, 0, len(g.handlers[0].middleware))
}

func TestGroupOptions(t *testing.T) {
g := Group("/test")

g.Options("/hello", func(ctx *Ctx) error { return nil })

assert.Equal(t, 1, len(g.handlers))
assert.Equal(t, "OPTIONS", g.handlers[0].method)
assert.Equal(t, "/hello", g.handlers[0].path)
assert.Equal(t, 0, len(g.handlers[0].middleware))
}

func TestGroupConnect(t *testing.T) {
g := Group("/test")

g.Connect("/hello", func(ctx *Ctx) error { return nil })

assert.Equal(t, 1, len(g.handlers))
assert.Equal(t, "CONNECT", g.handlers[0].method)
assert.Equal(t, "/hello", g.handlers[0].path)
assert.Equal(t, 0, len(g.handlers[0].middleware))
}

func TestGroupTrace(t *testing.T) {
g := Group("/test")

g.Trace("/hello", func(ctx *Ctx) error { return nil })

assert.Equal(t, 1, len(g.handlers))
assert.Equal(t, "TRACE", g.handlers[0].method)
assert.Equal(t, "/hello", g.handlers[0].path)
assert.Equal(t, 0, len(g.handlers[0].middleware))
}
Loading