diff --git a/ent/admin/handler.go b/ent/admin/handler.go index 666c347..6c09326 100644 --- a/ent/admin/handler.go +++ b/ent/admin/handler.go @@ -3,7 +3,9 @@ package admin import ( "fmt" + "net/url" "strconv" + "time" "entgo.io/ent/dialect/sql" "github.com/labstack/echo/v4" @@ -36,15 +38,14 @@ func (h *Handler) Create(ctx echo.Context, entityType string) error { } } -func (h *Handler) Get(ctx echo.Context, entityType string, id int) error { - // TODO +func (h *Handler) Get(ctx echo.Context, entityType string, id int) (url.Values, error) { switch entityType { case "PasswordToken": return h.PasswordTokenGet(ctx, id) case "User": return h.UserGet(ctx, id) default: - return fmt.Errorf("unsupported entity type: %s", entityType) + return nil, fmt.Errorf("unsupported entity type: %s", entityType) } } @@ -157,14 +158,16 @@ func (h *Handler) PasswordTokenList(ctx echo.Context) (*EntityList, error) { return list, err } -func (h *Handler) PasswordTokenGet(ctx echo.Context, id int) error { - _, err := h.client.PasswordToken.Get(ctx.Request().Context(), id) +func (h *Handler) PasswordTokenGet(ctx echo.Context, id int) (url.Values, error) { + entity, err := h.client.PasswordToken.Get(ctx.Request().Context(), id) if err != nil { - return err + return nil, err } - // TODO - return nil + v := url.Values{} + v.Set("user_id", fmt.Sprint(entity.UserID)) + v.Set("created_at", entity.CreatedAt.Format(time.RFC3339)) + return v, err } func (h *Handler) UserCreate(ctx echo.Context) error { @@ -248,14 +251,17 @@ func (h *Handler) UserList(ctx echo.Context) (*EntityList, error) { return list, err } -func (h *Handler) UserGet(ctx echo.Context, id int) error { - _, err := h.client.User.Get(ctx.Request().Context(), id) +func (h *Handler) UserGet(ctx echo.Context, id int) (url.Values, error) { + entity, err := h.client.User.Get(ctx.Request().Context(), id) if err != nil { - return err + return nil, err } - // TODO - return nil + v := url.Values{} + v.Set("name", entity.Name) + v.Set("email", entity.Email) + v.Set("verified", fmt.Sprint(entity.Verified)) + return v, err } func (h *Handler) getOffset(ctx echo.Context) int { diff --git a/ent/admin/templates/handler.tmpl b/ent/admin/templates/handler.tmpl index d6fc38c..ba7eab6 100644 --- a/ent/admin/templates/handler.tmpl +++ b/ent/admin/templates/handler.tmpl @@ -8,6 +8,7 @@ import ( "fmt" + "net/url" "strconv" "entgo.io/ent/dialect/sql" @@ -42,15 +43,14 @@ } } - func (h *Handler) Get(ctx echo.Context, entityType string, id int) error { - // TODO + func (h *Handler) Get(ctx echo.Context, entityType string, id int) (url.Values, error) { switch entityType { {{- range $n := $.Nodes }} case "{{ $n.Name }}": return h.{{ $n.Name }}Get(ctx, id) {{- end }} default: - return fmt.Errorf("unsupported entity type: %s", entityType) + return nil, fmt.Errorf("unsupported entity type: %s", entityType) } } @@ -186,14 +186,25 @@ return list, err } - func (h *Handler) {{ $n.Name }}Get(ctx echo.Context, id int) error { - _, err := h.client.{{ $n.Name }}.Get(ctx.Request().Context(), id) + func (h *Handler) {{ $n.Name }}Get(ctx echo.Context, id int) (url.Values, error) { + entity, err := h.client.{{ $n.Name }}.Get(ctx.Request().Context(), id) if err != nil { - return err + return nil, err } - // TODO - return nil + v := url.Values{} + {{- range $f := $n.Fields }} + {{- if and (not $f.Sensitive) (not $f.Immutable) }} + {{- if eq $f.Type.String "string" }} + v.Set("{{ $f.Name }}", entity.{{ fieldName $f.Name }}) + {{- else if eq $f.Type.String "time.Time" }} + v.Set("{{ $f.Name }}", entity.{{ fieldName $f.Name }}.Format(time.RFC3339)) + {{- else }} + v.Set("{{ $f.Name }}", fmt.Sprint(entity.{{ fieldName $f.Name }})) + {{- end }} + {{- end }} + {{- end }} + return v, err } {{ end }} diff --git a/ent/client.go b/ent/client.go index 7995f57..44d7cd6 100644 --- a/ent/client.go +++ b/ent/client.go @@ -333,7 +333,8 @@ func (c *PasswordTokenClient) QueryUser(pt *PasswordToken) *UserQuery { // Hooks returns the client hooks. func (c *PasswordTokenClient) Hooks() []Hook { - return c.hooks.PasswordToken + hooks := c.hooks.PasswordToken + return append(hooks[:len(hooks):len(hooks)], passwordtoken.Hooks[:]...) } // Interceptors returns the client interceptors. diff --git a/ent/passwordtoken/passwordtoken.go b/ent/passwordtoken/passwordtoken.go index fd7a716..88cade3 100644 --- a/ent/passwordtoken/passwordtoken.go +++ b/ent/passwordtoken/passwordtoken.go @@ -5,6 +5,7 @@ package passwordtoken import ( "time" + "entgo.io/ent" "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" ) @@ -51,7 +52,13 @@ func ValidColumn(column string) bool { return false } +// Note that the variables below are initialized by the runtime +// package on the initialization of the application. Therefore, +// it should be imported in the main as follows: +// +// import _ "github.com/mikestefanello/pagoda/ent/runtime" var ( + Hooks [1]ent.Hook // HashValidator is a validator for the "hash" field. It is called by the builders before save. HashValidator func(string) error // DefaultCreatedAt holds the default value on creation for the "created_at" field. diff --git a/ent/passwordtoken_create.go b/ent/passwordtoken_create.go index b3a427a..d23dd17 100644 --- a/ent/passwordtoken_create.go +++ b/ent/passwordtoken_create.go @@ -59,7 +59,9 @@ func (ptc *PasswordTokenCreate) Mutation() *PasswordTokenMutation { // Save creates the PasswordToken in the database. func (ptc *PasswordTokenCreate) Save(ctx context.Context) (*PasswordToken, error) { - ptc.defaults() + if err := ptc.defaults(); err != nil { + return nil, err + } return withHooks(ctx, ptc.sqlSave, ptc.mutation, ptc.hooks) } @@ -86,11 +88,15 @@ func (ptc *PasswordTokenCreate) ExecX(ctx context.Context) { } // defaults sets the default values of the builder before save. -func (ptc *PasswordTokenCreate) defaults() { +func (ptc *PasswordTokenCreate) defaults() error { if _, ok := ptc.mutation.CreatedAt(); !ok { + if passwordtoken.DefaultCreatedAt == nil { + return fmt.Errorf("ent: uninitialized passwordtoken.DefaultCreatedAt (forgotten import ent/runtime?)") + } v := passwordtoken.DefaultCreatedAt() ptc.mutation.SetCreatedAt(v) } + return nil } // check runs all checks and user-defined validators on the builder. diff --git a/ent/runtime/runtime.go b/ent/runtime/runtime.go index 1f430e4..bf7d085 100644 --- a/ent/runtime/runtime.go +++ b/ent/runtime/runtime.go @@ -14,6 +14,8 @@ import ( // (default values, validators, hooks and policies) and stitches it // to their package variables. func init() { + passwordtokenHooks := schema.PasswordToken{}.Hooks() + passwordtoken.Hooks[0] = passwordtokenHooks[0] passwordtokenFields := schema.PasswordToken{}.Fields() _ = passwordtokenFields // passwordtokenDescHash is the schema descriptor for hash field. diff --git a/pkg/handlers/admin.go b/pkg/handlers/admin.go index 6c4f4e2..aa0b670 100644 --- a/pkg/handlers/admin.go +++ b/pkg/handlers/admin.go @@ -52,12 +52,18 @@ func (h *Admin) Routes(g *echo.Group) { // TODO: can we generate something we can loop instead? for _, n := range h.graph.Nodes { ng := entities.Group(fmt.Sprintf("/%s", strings.ToLower(n.Name))) - ng.GET("", h.EntityList(n)).Name = routenames.AdminEntityList(n.Name) - ng.POST("", h.EntityList(n)).Name = routenames.AdminEntityListSubmit(n.Name) - ng.GET("/add", h.EntityAdd(n)).Name = routenames.AdminEntityAdd(n.Name) - ng.POST("/add", h.EntityAddSubmit(n)).Name = routenames.AdminEntityAddSubmit(n.Name) - //ng.GET("/:id/edit", h.EntityEdit(n), h.entityPluginMiddleware(n.Name)).Name = RouteNameEdit(n.Name) - //ng.POST("/:id/edit", h.EntityEditSubmit(n), h.entityPluginMiddleware(n.Name)).Name = RouteNameEditSubmit(n.Name) + ng.GET("", h.EntityList(n)). + Name = routenames.AdminEntityList(n.Name) + ng.POST("", h.EntityList(n)). + Name = routenames.AdminEntityListSubmit(n.Name) + ng.GET("/add", h.EntityAdd(n)). + Name = routenames.AdminEntityAdd(n.Name) + ng.POST("/add", h.EntityAddSubmit(n)). + Name = routenames.AdminEntityAddSubmit(n.Name) + ng.GET("/:id/edit", h.EntityEdit(n), h.middlewareEntityLoad(n)). + Name = routenames.AdminEntityEdit(n.Name) + ng.POST("/:id/edit", h.EntityEditSubmit(n), h.middlewareEntityLoad(n)). + Name = routenames.AdminEntityEditSubmit(n.Name) ng.GET("/:id/delete", h.EntityDelete(n), h.middlewareEntityLoad(n)). Name = routenames.AdminEntityDelete(n.Name) ng.POST("/:id/delete", h.EntityDeleteSubmit(n), h.middlewareEntityLoad(n)). @@ -74,11 +80,11 @@ func (h *Admin) middlewareEntityLoad(n *gen.Type) echo.MiddlewareFunc { return echo.NewHTTPError(http.StatusBadRequest, "invalid entity ID") } - err = h.admin.Get(ctx, n.Name, id) + entity, err := h.admin.Get(ctx, n.Name, id) switch { case err == nil: ctx.Set(entityIDContextKey, id) - //ctx.Set(entityContextKey, entityValues) + ctx.Set(entityContextKey, map[string][]string(entity)) return next(ctx) case ent.IsNotFound(err): return echo.NewHTTPError(http.StatusNotFound, "entity not found") @@ -106,7 +112,7 @@ func (h *Admin) EntityList(n *gen.Type) echo.HandlerFunc { func (h *Admin) EntityAdd(n *gen.Type) echo.HandlerFunc { return func(ctx echo.Context) error { - return pages.AdminEntityForm(ctx, h.getEntitySchema(n), nil) + return pages.AdminEntityForm(ctx, true, h.getEntitySchema(n), nil) } } @@ -127,20 +133,31 @@ func (h *Admin) EntityAddSubmit(n *gen.Type) echo.HandlerFunc { } } -//func (h *Admin) EntityEdit(n *gen.Type) echo.HandlerFunc { -// return func(ctx echo.Context) error { -// v := ctx.Get(entityContextKey).(map[string][]string) -// return pages.AdminEntityForm(ctx, h.getEntitySchema(n), v) -// } -//} +func (h *Admin) EntityEdit(n *gen.Type) echo.HandlerFunc { + return func(ctx echo.Context) error { + v := ctx.Get(entityContextKey).(map[string][]string) + return pages.AdminEntityForm(ctx, false, h.getEntitySchema(n), v) + } +} + +func (h *Admin) EntityEditSubmit(n *gen.Type) echo.HandlerFunc { + return func(ctx echo.Context) error { + id := ctx.Get(entityIDContextKey).(int) + err := h.admin.Update(ctx, n.Name, id) + if err != nil { + msg.Danger(ctx, err.Error()) + return h.EntityEdit(n)(ctx) + } + + msg.Success(ctx, fmt.Sprintf("Updated %s.", n.Name)) + + return redirect. + New(ctx). + Route(routenames.AdminEntityList(n.Name)). + Go() + } +} -// -//func (h *Admin) EntityEditSubmit(p AdminEntityPlugin) echo.HandlerFunc { -// return func(ctx echo.Context) error { -// return nil -// } -//} -// func (h *Admin) EntityDelete(n *gen.Type) echo.HandlerFunc { return func(ctx echo.Context) error { return pages.AdminEntityDelete(ctx, n.Name) diff --git a/pkg/ui/pages/entity.go b/pkg/ui/pages/entity.go index a2de8f9..ab44ac4 100644 --- a/pkg/ui/pages/entity.go +++ b/pkg/ui/pages/entity.go @@ -39,9 +39,14 @@ func AdminEntityDelete(ctx echo.Context, entityTypeName string) error { return r.Render(layouts.Admin, form) } -func AdminEntityForm(ctx echo.Context, schema *load.Schema, values url.Values) error { +func AdminEntityForm(ctx echo.Context, isNew bool, schema *load.Schema, values url.Values) error { r := ui.NewRequest(ctx) - r.Title = fmt.Sprintf("Add %s", schema.Name) + if isNew { + r.Title = fmt.Sprintf("Add %s", schema.Name) + } else { + r.Title = fmt.Sprintf("Edit %s", schema.Name) + } + nodes := make(Group, 0) label := func(name string) string { @@ -67,6 +72,10 @@ func AdminEntityForm(ctx echo.Context, schema *load.Schema, values url.Values) e for _, f := range schema.Fields { // TODO cardinality? + if !isNew && f.Immutable { + continue + } + // TODO sensitive edits switch f.Info.Type { case field.TypeString: inputType := "text" @@ -100,7 +109,7 @@ func AdminEntityForm(ctx echo.Context, schema *load.Schema, values url.Values) e nodes = append(nodes, Checkbox(CheckboxParams{ Name: f.Name, Label: label(f.Name), - Checked: getValue(f.Name) != "", + Checked: getValue(f.Name) == "true", })) case field.TypeEnum: // TODO