Working on better path patterns

This commit is contained in:
Andrea Fazzi 2020-01-16 12:47:35 +01:00
parent 22965dc1f4
commit 8a4afd970e
5 changed files with 122 additions and 65 deletions

View file

@ -47,6 +47,11 @@ type Handlers struct {
Static func() http.Handler Static func() http.Handler
Recover func(next http.Handler) http.Handler Recover func(next http.Handler) http.Handler
PathPatterns []PathPattern
APIPathPatterns []PathPattern
RolePermissions map[string]map[string][]int
CookieStore *sessions.CookieStore CookieStore *sessions.CookieStore
JWTSigningKey []byte JWTSigningKey []byte
@ -89,27 +94,9 @@ func pluralizedModelName(s interface{}) string {
// Generate CRUD handlers for models // Generate CRUD handlers for models
func (h *Handlers) generateModelHandlers(r *mux.Router, model interface{}) { func (h *Handlers) generateModelHandlers(r *mux.Router, model interface{}) {
var (
patterns []PathPattern = []PathPattern{
PathPattern{"/%s", "", []string{"GET"}, PermissionReadAll},
PathPattern{"/%s/{id}", "", []string{"GET"}, PermissionRead},
PathPattern{"/%s/create/", "/%s/%d?format=html&tpl_layout=base&tpl_content=%s_show", []string{"GET", "POST"}, PermissionCreate},
PathPattern{"/%s/{id}/update", "/%s/%d?format=html&tpl_layout=base&tpl_content=%s_show", []string{"GET", "POST"}, PermissionUpdate},
PathPattern{"/%s/{id}/delete", "/%s?format=html&tpl_layout=base&tpl_content=%s", []string{"DELETE"}, PermissionDelete},
}
apiPatterns []PathPattern
)
// Generate API patterns prefixing "api" path
for _, p := range patterns {
apiPatterns = append(apiPatterns, PathPattern{"/api" + p.PathPattern, "", p.Methods, p.Permission})
}
// Install standard paths // Install standard paths
for _, pattern := range patterns { for _, pattern := range DefaultPathPatterns {
r.Handle( r.Handle(
pattern.Path( pattern.Path(
pluralizedModelName(model), pluralizedModelName(model),
@ -124,7 +111,7 @@ func (h *Handlers) generateModelHandlers(r *mux.Router, model interface{}) {
// Install API paths // Install API paths
for _, pattern := range apiPatterns { for _, pattern := range h.APIPathPatterns {
r.Handle(pattern.Path( r.Handle(pattern.Path(
pluralizedModelName(model), pluralizedModelName(model),
), ),
@ -138,11 +125,11 @@ func (h *Handlers) generateModelHandlers(r *mux.Router, model interface{}) {
// Set permissions for HTML patterns // Set permissions for HTML patterns
for role, modelPermissions := range RolePermissions { for role, modelPermissions := range h.RolePermissions {
for m, perm := range modelPermissions { for m, perm := range modelPermissions {
if m == modelName(model) { if m == modelName(model) {
for _, p := range perm { for _, p := range perm {
for _, pattern := range patterns { for _, pattern := range h.PathPatterns {
if pattern.Permission == p { if pattern.Permission == p {
if permissions[role] == nil { if permissions[role] == nil {
permissions[role] = make(map[string]bool) permissions[role] = make(map[string]bool)
@ -151,7 +138,7 @@ func (h *Handlers) generateModelHandlers(r *mux.Router, model interface{}) {
} }
} }
for _, pattern := range apiPatterns { for _, pattern := range DefaultAPIPathPatterns {
if pattern.Permission == p { if pattern.Permission == p {
if permissions[role] == nil { if permissions[role] == nil {
permissions[role] = make(map[string]bool) permissions[role] = make(map[string]bool)
@ -167,7 +154,15 @@ func (h *Handlers) generateModelHandlers(r *mux.Router, model interface{}) {
} }
func NewHandlers(config *config.ConfigT, renderer map[string]renderer.Renderer, db *orm.Database, models []interface{}) *Handlers { func NewHandlers(
config *config.ConfigT,
renderer map[string]renderer.Renderer,
db *orm.Database,
models []interface{},
permissions map[string]map[string][]int,
pathPatterns []PathPattern,
apiPathPatterns []PathPattern,
) *Handlers {
handlers := new(Handlers) handlers := new(Handlers)
handlers.Config = config handlers.Config = config
@ -183,6 +178,11 @@ func NewHandlers(config *config.ConfigT, renderer map[string]renderer.Renderer,
handlers.Home = DefaultHomeHandler handlers.Home = DefaultHomeHandler
handlers.GetToken = DefaultGetTokenHandler handlers.GetToken = DefaultGetTokenHandler
handlers.RolePermissions = permissions
handlers.PathPatterns = pathPatterns
handlers.APIPathPatterns = apiPathPatterns
handlers.JWTCookieMiddleware = jwtmiddleware.New(jwtmiddleware.Options{ handlers.JWTCookieMiddleware = jwtmiddleware.New(jwtmiddleware.Options{
ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) { ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
return []byte(config.Keys.JWTSigningKey), nil return []byte(config.Keys.JWTSigningKey), nil
@ -233,6 +233,12 @@ func NewHandlers(config *config.ConfigT, renderer map[string]renderer.Renderer,
return handlers return handlers
} }
func (h *Handlers) NewReadAllRequest(model interface{}) (*http.Request, error) {
name := inflection.Plural(orm.ModelName(model))
request, err := http.NewRequest("GET", "/contests?format=html&tpl_layout=base&tpl_content=contests", nil)
return request, err
}
func (h *Handlers) onError(w http.ResponseWriter, r *http.Request, err string) { func (h *Handlers) onError(w http.ResponseWriter, r *http.Request, err string) {
http.Redirect(w, r, "/login?tpl_layout=login&tpl_content=login", http.StatusTemporaryRedirect) http.Redirect(w, r, "/login?tpl_layout=login&tpl_content=login", http.StatusTemporaryRedirect)
} }

View file

@ -110,7 +110,7 @@ func (t *testSuite) BeforeAll() {
// Initialize the renderers // Initialize the renderers
htmlRenderer, err := renderer.NewHTMLRenderer("./testdata/templates/") htmlRenderer, err := renderer.NewHTMLRenderer("../templates/")
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -119,12 +119,38 @@ func (t *testSuite) BeforeAll() {
"html": htmlRenderer, "html": htmlRenderer,
} }
handlers = NewHandlers(conf, renderer, db, orm.Models) permissions := map[string]map[string][]int{
"administrator": map[string][]int{
"Contest": []int{PermissionCreate, PermissionRead, PermissionReadAll, PermissionUpdate, PermissionDelete},
"Participant": []int{PermissionCreate, PermissionRead, PermissionReadAll, PermissionUpdate, PermissionDelete},
"School": []int{PermissionCreate, PermissionRead, PermissionReadAll, PermissionUpdate, PermissionDelete},
"Question": []int{PermissionCreate, PermissionRead, PermissionReadAll, PermissionUpdate, PermissionDelete},
"Answer": []int{PermissionCreate, PermissionRead, PermissionReadAll, PermissionUpdate, PermissionDelete},
"Response": []int{PermissionCreate, PermissionRead, PermissionReadAll, PermissionUpdate, PermissionDelete},
},
"school": map[string][]int{
"Participant": []int{PermissionCreate, PermissionRead, PermissionReadAll, PermissionUpdate, PermissionDelete},
"School": []int{PermissionRead, PermissionUpdate},
},
"participant": map[string][]int{
"Participant": []int{PermissionRead},
"Response": []int{PermissionUpdate, PermissionRead},
},
"subscriber": map[string][]int{
"School": []int{PermissionCreate, PermissionRead},
},
}
handlers = NewHandlers(conf, renderer, db, orm.Models, permissions)
token = requestToken(db, handlers) token = requestToken(db, handlers)
} }
func (t *testSuite) TestReadAllContests() { func (t *testSuite) TestReadAllContests() {
req, err := http.NewRequest("GET", "/contests?format=html&tpl_layout=base&tpl_content=contests", nil) req, err := RequestReadAll(&Contest{})
// req, err := http.NewRequest("GET", "/contests?format=html&tpl_layout=base&tpl_content=contests", nil)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -146,7 +172,7 @@ func (t *testSuite) TestReadAllContests() {
t.Equal(http.StatusOK, rr.Code) t.Equal(http.StatusOK, rr.Code)
if !t.Failed() { if !t.Failed() {
doc, err := goquery.NewDocumentFromResponse(rr) doc, err := goquery.NewDocumentFromResponse(rr.Result())
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }

27
handlers/paths.go Normal file
View file

@ -0,0 +1,27 @@
package handlers
const (
PermissionCreate = iota
PermissionRead
PermissionReadAll
PermissionUpdate
PermissionDelete
)
var (
DefaultPathPatterns []PathPattern = []PathPattern{
PathPattern{"/%s", "", []string{"GET"}, PermissionReadAll},
PathPattern{"/%s/{id}", "", []string{"GET"}, PermissionRead},
PathPattern{"/%s/create/", "/%s/%d?format=html&tpl_layout=base&tpl_content=%s_show", []string{"GET", "POST"}, PermissionCreate},
PathPattern{"/%s/{id}/update", "/%s/%d?format=html&tpl_layout=base&tpl_content=%s_show", []string{"GET", "POST"}, PermissionUpdate},
PathPattern{"/%s/{id}/delete", "/%s?format=html&tpl_layout=base&tpl_content=%s", []string{"DELETE"}, PermissionDelete},
}
DefaultAPIPathPatterns []PathPattern = []PathPattern{
PathPattern{"/api/%s", "", []string{"GET"}, PermissionReadAll},
PathPattern{"/api/%s/{id}", "", []string{"GET"}, PermissionRead},
PathPattern{"/api/%s/create/", "", []string{"GET", "POST"}, PermissionCreate},
PathPattern{"/api/%s/{id}/update", "", []string{"GET", "POST"}, PermissionUpdate},
PathPattern{"/api/%s/{id}/delete", "", []string{"DELETE"}, PermissionDelete},
}
)

View file

@ -1,36 +0,0 @@
package handlers
const (
PermissionCreate = iota
PermissionRead
PermissionReadAll
PermissionUpdate
PermissionDelete
)
var (
RolePermissions map[string]map[string][]int = map[string]map[string][]int{
"administrator": map[string][]int{
"Contest": []int{PermissionCreate, PermissionRead, PermissionReadAll, PermissionUpdate, PermissionDelete},
"Participant": []int{PermissionCreate, PermissionRead, PermissionReadAll, PermissionUpdate, PermissionDelete},
"School": []int{PermissionCreate, PermissionRead, PermissionReadAll, PermissionUpdate, PermissionDelete},
"Question": []int{PermissionCreate, PermissionRead, PermissionReadAll, PermissionUpdate, PermissionDelete},
"Answer": []int{PermissionCreate, PermissionRead, PermissionReadAll, PermissionUpdate, PermissionDelete},
"Response": []int{PermissionCreate, PermissionRead, PermissionReadAll, PermissionUpdate, PermissionDelete},
},
"school": map[string][]int{
"Participant": []int{PermissionCreate, PermissionRead, PermissionReadAll, PermissionUpdate, PermissionDelete},
"School": []int{PermissionRead, PermissionUpdate},
},
"participant": map[string][]int{
"Participant": []int{PermissionRead},
"Response": []int{PermissionUpdate, PermissionRead},
},
"subscriber": map[string][]int{
"School": []int{PermissionCreate, PermissionRead},
},
}
)

36
main.go
View file

@ -81,7 +81,41 @@ func main() {
"html": htmlRenderer, "html": htmlRenderer,
"json": jsonRenderer, "json": jsonRenderer,
} }
if err := http.ListenAndServe(":3000", handlers.LoggingHandler(os.Stdout, oef_handlers.NewHandlers(conf, renderer, db, orm.Models).Router)); err != nil {
permissions := map[string]map[string][]int{
"administrator": map[string][]int{
"Contest": []int{oef_handlers.PermissionCreate, oef_handlers.PermissionRead, oef_handlers.PermissionReadAll, oef_handlers.PermissionUpdate, oef_handlers.PermissionDelete},
"Participant": []int{oef_handlers.PermissionCreate, oef_handlers.PermissionRead, oef_handlers.PermissionReadAll, oef_handlers.PermissionUpdate, oef_handlers.PermissionDelete},
"School": []int{oef_handlers.PermissionCreate, oef_handlers.PermissionRead, oef_handlers.PermissionReadAll, oef_handlers.PermissionUpdate, oef_handlers.PermissionDelete},
"Question": []int{oef_handlers.PermissionCreate, oef_handlers.PermissionRead, oef_handlers.PermissionReadAll, oef_handlers.PermissionUpdate, oef_handlers.PermissionDelete},
"Answer": []int{oef_handlers.PermissionCreate, oef_handlers.PermissionRead, oef_handlers.PermissionReadAll, oef_handlers.PermissionUpdate, oef_handlers.PermissionDelete},
"Response": []int{oef_handlers.PermissionCreate, oef_handlers.PermissionRead, oef_handlers.PermissionReadAll, oef_handlers.PermissionUpdate, oef_handlers.PermissionDelete},
},
"school": map[string][]int{
"Participant": []int{oef_handlers.PermissionCreate, oef_handlers.PermissionRead, oef_handlers.PermissionReadAll, oef_handlers.PermissionUpdate, oef_handlers.PermissionDelete},
"School": []int{oef_handlers.PermissionRead, oef_handlers.PermissionUpdate},
},
"participant": map[string][]int{
"Participant": []int{oef_handlers.PermissionRead},
"Response": []int{oef_handlers.PermissionUpdate, oef_handlers.PermissionRead},
},
"subscriber": map[string][]int{
"School": []int{oef_handlers.PermissionCreate, oef_handlers.PermissionRead},
},
}
if err := http.ListenAndServe(":3000", handlers.LoggingHandler(os.Stdout, oef_handlers.NewHandlers(
conf,
renderer,
db,
orm.Models,
permissions,
handlers.DefaultPathPatterns,
handlers.DefaultAPIPathPatterns,
).Router)); err != nil {
panic(err) panic(err)
} }