Generate ent schema in admin code. (#127)
This commit is contained in:
parent
67a97832a5
commit
9e6d9fd063
13 changed files with 303 additions and 142 deletions
|
|
@ -7,8 +7,6 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"entgo.io/ent/entc/gen"
|
||||
"entgo.io/ent/entc/load"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/mikestefanello/backlite/ui"
|
||||
"github.com/mikestefanello/pagoda/ent"
|
||||
|
|
@ -25,7 +23,6 @@ import (
|
|||
|
||||
type Admin struct {
|
||||
orm *ent.Client
|
||||
graph *gen.Graph
|
||||
admin *admin.Handler
|
||||
backlite *ui.Handler
|
||||
}
|
||||
|
|
@ -36,7 +33,6 @@ func init() {
|
|||
|
||||
func (h *Admin) Init(c *services.Container) error {
|
||||
var err error
|
||||
h.graph = c.Graph
|
||||
h.orm = c.ORM
|
||||
h.admin = admin.NewHandler(h.orm, admin.HandlerConfig{
|
||||
ItemsPerPage: 25,
|
||||
|
|
@ -56,22 +52,22 @@ func (h *Admin) Routes(g *echo.Group) {
|
|||
ag := g.Group("/admin", middleware.RequireAdmin)
|
||||
|
||||
entities := ag.Group("/entity")
|
||||
for _, n := range h.graph.Nodes {
|
||||
ng := entities.Group(fmt.Sprintf("/%s", strings.ToLower(n.Name)))
|
||||
for _, n := range admin.GetEntityTypes() {
|
||||
ng := entities.Group(fmt.Sprintf("/%s", strings.ToLower(n.GetName())))
|
||||
ng.GET("", h.EntityList(n)).
|
||||
Name = routenames.AdminEntityList(n.Name)
|
||||
Name = routenames.AdminEntityList(n.GetName())
|
||||
ng.GET("/add", h.EntityAdd(n)).
|
||||
Name = routenames.AdminEntityAdd(n.Name)
|
||||
Name = routenames.AdminEntityAdd(n.GetName())
|
||||
ng.POST("/add", h.EntityAddSubmit(n)).
|
||||
Name = routenames.AdminEntityAddSubmit(n.Name)
|
||||
Name = routenames.AdminEntityAddSubmit(n.GetName())
|
||||
ng.GET("/:id/edit", h.EntityEdit(n), h.middlewareEntityLoad(n)).
|
||||
Name = routenames.AdminEntityEdit(n.Name)
|
||||
Name = routenames.AdminEntityEdit(n.GetName())
|
||||
ng.POST("/:id/edit", h.EntityEditSubmit(n), h.middlewareEntityLoad(n)).
|
||||
Name = routenames.AdminEntityEditSubmit(n.Name)
|
||||
Name = routenames.AdminEntityEditSubmit(n.GetName())
|
||||
ng.GET("/:id/delete", h.EntityDelete(n), h.middlewareEntityLoad(n)).
|
||||
Name = routenames.AdminEntityDelete(n.Name)
|
||||
Name = routenames.AdminEntityDelete(n.GetName())
|
||||
ng.POST("/:id/delete", h.EntityDeleteSubmit(n), h.middlewareEntityLoad(n)).
|
||||
Name = routenames.AdminEntityDeleteSubmit(n.Name)
|
||||
Name = routenames.AdminEntityDeleteSubmit(n.GetName())
|
||||
}
|
||||
|
||||
tasks := ag.Group("/tasks")
|
||||
|
|
@ -84,7 +80,7 @@ func (h *Admin) Routes(g *echo.Group) {
|
|||
}
|
||||
|
||||
// middlewareEntityLoad is middleware to extract the entity ID and attempt to load the given entity.
|
||||
func (h *Admin) middlewareEntityLoad(n *gen.Type) echo.MiddlewareFunc {
|
||||
func (h *Admin) middlewareEntityLoad(n admin.EntityType) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(ctx echo.Context) error {
|
||||
id, err := strconv.Atoi(ctx.Param("id"))
|
||||
|
|
@ -92,7 +88,7 @@ func (h *Admin) middlewareEntityLoad(n *gen.Type) echo.MiddlewareFunc {
|
|||
return echo.NewHTTPError(http.StatusBadRequest, "invalid entity ID")
|
||||
}
|
||||
|
||||
entity, err := h.admin.Get(ctx, n.Name, id)
|
||||
entity, err := h.admin.Get(ctx, n, id)
|
||||
switch {
|
||||
case err == nil:
|
||||
ctx.Set(context.AdminEntityIDKey, id)
|
||||
|
|
@ -107,100 +103,91 @@ func (h *Admin) middlewareEntityLoad(n *gen.Type) echo.MiddlewareFunc {
|
|||
}
|
||||
}
|
||||
|
||||
func (h *Admin) EntityList(n *gen.Type) echo.HandlerFunc {
|
||||
func (h *Admin) EntityList(n admin.EntityType) echo.HandlerFunc {
|
||||
return func(ctx echo.Context) error {
|
||||
list, err := h.admin.List(ctx, n.Name)
|
||||
list, err := h.admin.List(ctx, n)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, err)
|
||||
}
|
||||
|
||||
return pages.AdminEntityList(ctx, n.Name, list)
|
||||
return pages.AdminEntityList(ctx, n, list)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Admin) EntityAdd(n *gen.Type) echo.HandlerFunc {
|
||||
func (h *Admin) EntityAdd(n admin.EntityType) echo.HandlerFunc {
|
||||
return func(ctx echo.Context) error {
|
||||
return pages.AdminEntityInput(ctx, h.getEntitySchema(n), nil)
|
||||
return pages.AdminEntityInput(ctx, n, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Admin) EntityAddSubmit(n *gen.Type) echo.HandlerFunc {
|
||||
func (h *Admin) EntityAddSubmit(n admin.EntityType) echo.HandlerFunc {
|
||||
return func(ctx echo.Context) error {
|
||||
err := h.admin.Create(ctx, n.Name)
|
||||
err := h.admin.Create(ctx, n)
|
||||
if err != nil {
|
||||
msg.Error(ctx, err.Error())
|
||||
return h.EntityAdd(n)(ctx)
|
||||
}
|
||||
|
||||
msg.Success(ctx, fmt.Sprintf("Successfully added %s.", n.Name))
|
||||
msg.Success(ctx, fmt.Sprintf("Successfully added %s.", n.GetName()))
|
||||
|
||||
return redirect.
|
||||
New(ctx).
|
||||
Route(routenames.AdminEntityList(n.Name)).
|
||||
Route(routenames.AdminEntityList(n.GetName())).
|
||||
StatusCode(http.StatusFound).
|
||||
Go()
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Admin) EntityEdit(n *gen.Type) echo.HandlerFunc {
|
||||
func (h *Admin) EntityEdit(n admin.EntityType) echo.HandlerFunc {
|
||||
return func(ctx echo.Context) error {
|
||||
v := ctx.Get(context.AdminEntityKey).(map[string][]string)
|
||||
return pages.AdminEntityInput(ctx, h.getEntitySchema(n), v)
|
||||
return pages.AdminEntityInput(ctx, n, v)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Admin) EntityEditSubmit(n *gen.Type) echo.HandlerFunc {
|
||||
func (h *Admin) EntityEditSubmit(n admin.EntityType) echo.HandlerFunc {
|
||||
return func(ctx echo.Context) error {
|
||||
id := ctx.Get(context.AdminEntityIDKey).(int)
|
||||
err := h.admin.Update(ctx, n.Name, id)
|
||||
err := h.admin.Update(ctx, n, id)
|
||||
if err != nil {
|
||||
msg.Error(ctx, err.Error())
|
||||
return h.EntityEdit(n)(ctx)
|
||||
}
|
||||
|
||||
msg.Success(ctx, fmt.Sprintf("Updated %s.", n.Name))
|
||||
msg.Success(ctx, fmt.Sprintf("Updated %s.", n.GetName()))
|
||||
|
||||
return redirect.
|
||||
New(ctx).
|
||||
Route(routenames.AdminEntityList(n.Name)).
|
||||
Route(routenames.AdminEntityList(n.GetName())).
|
||||
StatusCode(http.StatusFound).
|
||||
Go()
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Admin) EntityDelete(n *gen.Type) echo.HandlerFunc {
|
||||
func (h *Admin) EntityDelete(n admin.EntityType) echo.HandlerFunc {
|
||||
return func(ctx echo.Context) error {
|
||||
return pages.AdminEntityDelete(ctx, n.Name)
|
||||
return pages.AdminEntityDelete(ctx, n)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Admin) EntityDeleteSubmit(n *gen.Type) echo.HandlerFunc {
|
||||
func (h *Admin) EntityDeleteSubmit(n admin.EntityType) echo.HandlerFunc {
|
||||
return func(ctx echo.Context) error {
|
||||
id := ctx.Get(context.AdminEntityIDKey).(int)
|
||||
if err := h.admin.Delete(ctx, n.Name, id); err != nil {
|
||||
if err := h.admin.Delete(ctx, n, id); err != nil {
|
||||
msg.Error(ctx, err.Error())
|
||||
return h.EntityDelete(n)(ctx)
|
||||
}
|
||||
|
||||
msg.Success(ctx, fmt.Sprintf("Successfully deleted %s (ID %d).", n.Name, id))
|
||||
msg.Success(ctx, fmt.Sprintf("Successfully deleted %s (ID %d).", n.GetName(), id))
|
||||
|
||||
return redirect.
|
||||
New(ctx).
|
||||
Route(routenames.AdminEntityList(n.Name)).
|
||||
Route(routenames.AdminEntityList(n.GetName())).
|
||||
StatusCode(http.StatusFound).
|
||||
Go()
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Admin) getEntitySchema(n *gen.Type) *load.Schema {
|
||||
for _, s := range h.graph.Schemas {
|
||||
if s.Name == n.Name {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Admin) Backlite(handler func(http.ResponseWriter, *http.Request) error) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
if id := c.Param("id"); id != "" {
|
||||
|
|
|
|||
|
|
@ -7,14 +7,9 @@ import (
|
|||
"log/slog"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
entsql "entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/entc"
|
||||
"entgo.io/ent/entc/gen"
|
||||
"github.com/labstack/echo/v4"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"github.com/mikestefanello/backlite"
|
||||
|
|
@ -51,9 +46,6 @@ type Container struct {
|
|||
// ORM stores a client to the ORM.
|
||||
ORM *ent.Client
|
||||
|
||||
// Graph is the entity graph defined by your Ent schema.
|
||||
Graph *gen.Graph
|
||||
|
||||
// Mail stores an email sending client.
|
||||
Mail *MailClient
|
||||
|
||||
|
|
@ -192,16 +184,6 @@ func (c *Container) initORM() {
|
|||
if err := c.ORM.Schema.Create(context.Background()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Load the graph.
|
||||
_, b, _, _ := runtime.Caller(0)
|
||||
d := path.Join(path.Dir(b))
|
||||
p := filepath.Join(filepath.Dir(d), "../ent/schema")
|
||||
g, err := entc.LoadGraph(p, &gen.Config{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
c.Graph = g
|
||||
}
|
||||
|
||||
// initAuth initializes the authentication client.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"entgo.io/ent/entc/load"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/mikestefanello/pagoda/ent/admin"
|
||||
"github.com/mikestefanello/pagoda/pkg/routenames"
|
||||
|
|
@ -14,7 +13,7 @@ import (
|
|||
. "maragu.dev/gomponents/html"
|
||||
)
|
||||
|
||||
func AdminEntity(r *ui.Request, schema *load.Schema, values url.Values) Node {
|
||||
func AdminEntity(r *ui.Request, entityType admin.EntityType, values url.Values) Node {
|
||||
// TODO inline validation?
|
||||
isNew := values == nil
|
||||
nodes := make(Group, 0)
|
||||
|
|
@ -34,13 +33,13 @@ func AdminEntity(r *ui.Request, schema *load.Schema, values url.Values) Node {
|
|||
}
|
||||
|
||||
// Attempt to add form elements for all editable entity fields.
|
||||
for _, f := range schema.Fields {
|
||||
for _, f := range entityType.GetSchema() {
|
||||
// TODO cardinality?
|
||||
if !isNew && f.Immutable {
|
||||
continue
|
||||
}
|
||||
|
||||
switch f.Info.Type {
|
||||
switch f.Type {
|
||||
case field.TypeString:
|
||||
p := InputFieldParams{
|
||||
Name: f.Name,
|
||||
|
|
@ -93,8 +92,8 @@ func AdminEntity(r *ui.Request, schema *load.Schema, values url.Values) Node {
|
|||
}
|
||||
for _, enum := range f.Enums {
|
||||
options = append(options, Choice{
|
||||
Label: enum.V,
|
||||
Value: enum.V,
|
||||
Label: enum.Label,
|
||||
Value: enum.Value,
|
||||
})
|
||||
}
|
||||
nodes = append(nodes, SelectList(OptionsParams{
|
||||
|
|
@ -116,7 +115,7 @@ func AdminEntity(r *ui.Request, schema *load.Schema, values url.Values) Node {
|
|||
FormButton(ColorPrimary, "Submit"),
|
||||
ButtonLink(
|
||||
ColorNone,
|
||||
r.Path(routenames.AdminEntityList(schema.Name)),
|
||||
r.Path(routenames.AdminEntityList(entityType.GetName())),
|
||||
"Cancel",
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package forms
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/mikestefanello/pagoda/ent/admin"
|
||||
"github.com/mikestefanello/pagoda/pkg/routenames"
|
||||
"github.com/mikestefanello/pagoda/pkg/ui"
|
||||
. "github.com/mikestefanello/pagoda/pkg/ui/components"
|
||||
|
|
@ -10,17 +11,17 @@ import (
|
|||
. "maragu.dev/gomponents/html"
|
||||
)
|
||||
|
||||
func AdminEntityDelete(r *ui.Request, entityTypeName string) Node {
|
||||
func AdminEntityDelete(r *ui.Request, entityType admin.EntityType) Node {
|
||||
return Form(
|
||||
Method(http.MethodPost),
|
||||
P(
|
||||
Textf("Are you sure you want to delete this %s?", entityTypeName),
|
||||
Textf("Are you sure you want to delete this %s?", entityType.GetName()),
|
||||
),
|
||||
ControlGroup(
|
||||
FormButton(ColorError, "Delete"),
|
||||
ButtonLink(
|
||||
ColorNone,
|
||||
r.Path(routenames.AdminEntityList(entityTypeName)),
|
||||
r.Path(routenames.AdminEntityList(entityType.GetName())),
|
||||
"Cancel",
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -121,10 +121,12 @@ func sidebarMenu(r *ui.Request) Node {
|
|||
}
|
||||
|
||||
adminSubMenu := func() Node {
|
||||
entityTypeNames := admin.GetEntityTypeNames()
|
||||
entityTypeLinks := make(Group, len(entityTypeNames))
|
||||
for _, n := range entityTypeNames {
|
||||
entityTypeLinks = append(entityTypeLinks, MenuLink(r, icons.PencilSquare(), n, routenames.AdminEntityList(n)))
|
||||
entityTypeLinks := make(Group, len(admin.GetEntityTypes()))
|
||||
for _, n := range admin.GetEntityTypes() {
|
||||
entityTypeLinks = append(
|
||||
entityTypeLinks,
|
||||
MenuLink(r, icons.PencilSquare(), n.GetName(), routenames.AdminEntityList(n.GetName())),
|
||||
)
|
||||
}
|
||||
|
||||
return Group{
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import (
|
|||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"entgo.io/ent/entc/load"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/mikestefanello/pagoda/ent/admin"
|
||||
"github.com/mikestefanello/pagoda/pkg/routenames"
|
||||
|
|
@ -16,37 +15,37 @@ import (
|
|||
. "maragu.dev/gomponents/html"
|
||||
)
|
||||
|
||||
func AdminEntityDelete(ctx echo.Context, entityTypeName string) error {
|
||||
func AdminEntityDelete(ctx echo.Context, entityType admin.EntityType) error {
|
||||
r := ui.NewRequest(ctx)
|
||||
r.Title = fmt.Sprintf("Delete %s", entityTypeName)
|
||||
r.Title = fmt.Sprintf("Delete %s", entityType.GetName())
|
||||
|
||||
return r.Render(
|
||||
layouts.Primary,
|
||||
forms.AdminEntityDelete(r, entityTypeName),
|
||||
forms.AdminEntityDelete(r, entityType),
|
||||
)
|
||||
}
|
||||
|
||||
func AdminEntityInput(ctx echo.Context, schema *load.Schema, values url.Values) error {
|
||||
func AdminEntityInput(ctx echo.Context, entityType admin.EntityType, values url.Values) error {
|
||||
r := ui.NewRequest(ctx)
|
||||
if values == nil {
|
||||
r.Title = fmt.Sprintf("Add %s", schema.Name)
|
||||
r.Title = fmt.Sprintf("Add %s", entityType.GetName())
|
||||
} else {
|
||||
r.Title = fmt.Sprintf("Edit %s", schema.Name)
|
||||
r.Title = fmt.Sprintf("Edit %s", entityType.GetName())
|
||||
}
|
||||
|
||||
return r.Render(
|
||||
layouts.Primary,
|
||||
forms.AdminEntity(r, schema, values),
|
||||
forms.AdminEntity(r, entityType, values),
|
||||
)
|
||||
}
|
||||
|
||||
func AdminEntityList(
|
||||
ctx echo.Context,
|
||||
entityTypeName string,
|
||||
entityType admin.EntityType,
|
||||
entityList *admin.EntityList,
|
||||
) error {
|
||||
r := ui.NewRequest(ctx)
|
||||
r.Title = entityTypeName
|
||||
r.Title = entityType.GetName()
|
||||
|
||||
genHeader := func() Node {
|
||||
g := make(Group, 0, len(entityList.Columns)+2)
|
||||
|
|
@ -68,13 +67,13 @@ func AdminEntityList(
|
|||
Td(
|
||||
ButtonLink(
|
||||
ColorInfo,
|
||||
r.Path(routenames.AdminEntityEdit(entityTypeName), row.ID),
|
||||
r.Path(routenames.AdminEntityEdit(entityType.GetName()), row.ID),
|
||||
"Edit",
|
||||
),
|
||||
Span(Class("mr-2")),
|
||||
ButtonLink(
|
||||
ColorError,
|
||||
r.Path(routenames.AdminEntityDelete(entityTypeName), row.ID),
|
||||
r.Path(routenames.AdminEntityDelete(entityType.GetName()), row.ID),
|
||||
"Delete",
|
||||
),
|
||||
),
|
||||
|
|
@ -95,8 +94,8 @@ func AdminEntityList(
|
|||
Class("form-control mb-2"),
|
||||
ButtonLink(
|
||||
ColorAccent,
|
||||
r.Path(routenames.AdminEntityAdd(entityTypeName)),
|
||||
fmt.Sprintf("Add %s", entityTypeName),
|
||||
r.Path(routenames.AdminEntityAdd(entityType.GetName())),
|
||||
fmt.Sprintf("Add %s", entityType.GetName()),
|
||||
),
|
||||
),
|
||||
Table(
|
||||
|
|
@ -108,7 +107,7 @@ func AdminEntityList(
|
|||
),
|
||||
Pager(
|
||||
entityList.Page,
|
||||
r.Path(routenames.AdminEntityAdd(entityTypeName)),
|
||||
r.Path(routenames.AdminEntityAdd(entityType.GetName())),
|
||||
entityList.HasNextPage,
|
||||
"",
|
||||
),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue