Complete CRUD: GET-single and DELETE endpoints #3
@@ -21,33 +21,72 @@ func (s *Server) mountResources(r chi.Router) {
|
||||
|
||||
r.Route("/fabrics", func(r chi.Router) {
|
||||
r.Get("/", s.listFabrics)
|
||||
r.Get("/{name}", s.getFabric)
|
||||
r.Put("/{name}", s.putFabric)
|
||||
r.Delete("/{name}", s.deleteFabric)
|
||||
})
|
||||
r.Route("/zones", func(r chi.Router) {
|
||||
r.Get("/", s.listZones)
|
||||
r.Get("/{name}", s.getZone)
|
||||
r.Put("/{name}", s.putZone)
|
||||
r.Delete("/{name}", s.deleteZone)
|
||||
})
|
||||
r.Route("/address-groups", func(r chi.Router) {
|
||||
r.Get("/", s.listAddressGroups)
|
||||
r.Get("/{name}", s.getAddressGroup)
|
||||
r.Put("/{name}", s.putAddressGroup)
|
||||
r.Delete("/{name}", s.deleteAddressGroup)
|
||||
})
|
||||
r.Route("/devices", func(r chi.Router) {
|
||||
r.Get("/", s.listDevices)
|
||||
r.Get("/{name}", s.getDevice)
|
||||
r.Put("/{name}", s.putDevice)
|
||||
r.Delete("/{name}", s.deleteDevice)
|
||||
r.Get("/{name}/bindings", s.listBindings)
|
||||
r.Get("/{name}/bindings/{zone}", s.getBinding)
|
||||
r.Put("/{name}/bindings/{zone}", s.putBinding)
|
||||
r.Delete("/{name}/bindings/{zone}", s.deleteBinding)
|
||||
})
|
||||
r.Route("/portgroups", func(r chi.Router) {
|
||||
r.Get("/", s.listPortGroups)
|
||||
r.Get("/{name}", s.getPortGroup)
|
||||
r.Put("/{name}", s.putPortGroup)
|
||||
r.Delete("/{name}", s.deletePortGroup)
|
||||
})
|
||||
r.Route("/rules", func(r chi.Router) {
|
||||
r.Get("/", s.listRules)
|
||||
r.Post("/", s.createRule)
|
||||
r.Get("/{id}", s.getRule)
|
||||
r.Delete("/{id}", s.deleteRule)
|
||||
})
|
||||
}
|
||||
|
||||
// respondOne writes a single resource, mapping ErrNotFound to 404.
|
||||
func respondOne(w http.ResponseWriter, v any, err error) {
|
||||
if err != nil {
|
||||
if errors.Is(err, store.ErrNotFound) {
|
||||
writeError(w, http.StatusNotFound, "not found")
|
||||
return
|
||||
}
|
||||
writeError(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, v)
|
||||
}
|
||||
|
||||
// respondDelete maps a delete result to 204/404/500.
|
||||
func respondDelete(w http.ResponseWriter, err error) {
|
||||
if err != nil {
|
||||
if errors.Is(err, store.ErrNotFound) {
|
||||
writeError(w, http.StatusNotFound, "not found")
|
||||
return
|
||||
}
|
||||
writeError(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (s *Server) listPortGroups(w http.ResponseWriter, r *http.Request) {
|
||||
list, err := s.store.ListPortGroups(r.Context())
|
||||
respondList(w, list, err)
|
||||
@@ -221,6 +260,72 @@ func (s *Server) deleteRule(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// ---- Get-single and Delete handlers ----------------------------------------
|
||||
|
||||
func (s *Server) getFabric(w http.ResponseWriter, r *http.Request) {
|
||||
f, err := s.store.GetFabric(r.Context(), chi.URLParam(r, "name"))
|
||||
respondOne(w, f, err)
|
||||
}
|
||||
|
||||
func (s *Server) deleteFabric(w http.ResponseWriter, r *http.Request) {
|
||||
respondDelete(w, s.store.DeleteFabric(r.Context(), chi.URLParam(r, "name")))
|
||||
}
|
||||
|
||||
func (s *Server) getZone(w http.ResponseWriter, r *http.Request) {
|
||||
z, err := s.store.GetZone(r.Context(), chi.URLParam(r, "name"))
|
||||
respondOne(w, z, err)
|
||||
}
|
||||
|
||||
func (s *Server) deleteZone(w http.ResponseWriter, r *http.Request) {
|
||||
respondDelete(w, s.store.DeleteZone(r.Context(), chi.URLParam(r, "name")))
|
||||
}
|
||||
|
||||
func (s *Server) getAddressGroup(w http.ResponseWriter, r *http.Request) {
|
||||
g, err := s.store.GetAddressGroup(r.Context(), chi.URLParam(r, "name"))
|
||||
respondOne(w, g, err)
|
||||
}
|
||||
|
||||
func (s *Server) deleteAddressGroup(w http.ResponseWriter, r *http.Request) {
|
||||
respondDelete(w, s.store.DeleteAddressGroup(r.Context(), chi.URLParam(r, "name")))
|
||||
}
|
||||
|
||||
func (s *Server) getPortGroup(w http.ResponseWriter, r *http.Request) {
|
||||
p, err := s.store.GetPortGroup(r.Context(), chi.URLParam(r, "name"))
|
||||
respondOne(w, p, err)
|
||||
}
|
||||
|
||||
func (s *Server) deletePortGroup(w http.ResponseWriter, r *http.Request) {
|
||||
respondDelete(w, s.store.DeletePortGroup(r.Context(), chi.URLParam(r, "name")))
|
||||
}
|
||||
|
||||
func (s *Server) getDevice(w http.ResponseWriter, r *http.Request) {
|
||||
d, err := s.store.GetDevice(r.Context(), chi.URLParam(r, "name"))
|
||||
respondOne(w, d, err)
|
||||
}
|
||||
|
||||
func (s *Server) deleteDevice(w http.ResponseWriter, r *http.Request) {
|
||||
respondDelete(w, s.store.DeleteDevice(r.Context(), chi.URLParam(r, "name")))
|
||||
}
|
||||
|
||||
func (s *Server) getBinding(w http.ResponseWriter, r *http.Request) {
|
||||
b, err := s.store.GetBinding(r.Context(), chi.URLParam(r, "name"), chi.URLParam(r, "zone"))
|
||||
respondOne(w, b, err)
|
||||
}
|
||||
|
||||
func (s *Server) deleteBinding(w http.ResponseWriter, r *http.Request) {
|
||||
respondDelete(w, s.store.DeleteBinding(r.Context(), chi.URLParam(r, "name"), chi.URLParam(r, "zone")))
|
||||
}
|
||||
|
||||
func (s *Server) getRule(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "id must be an integer")
|
||||
return
|
||||
}
|
||||
rule, err := s.store.GetRule(r.Context(), id)
|
||||
respondOne(w, rule, err)
|
||||
}
|
||||
|
||||
// ---- Agent endpoints -------------------------------------------------------
|
||||
|
||||
func (s *Server) handleDeviceConfig(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
+104
-1
@@ -480,8 +480,16 @@ func (s *Store) CreateRule(ctx context.Context, r model.Rule) (int64, error) {
|
||||
}
|
||||
|
||||
func (s *Store) DeleteRule(ctx context.Context, id int64) error {
|
||||
return s.deleteOne(ctx, `DELETE FROM rules WHERE id = $1`, id)
|
||||
}
|
||||
|
||||
// ---- Get-single and Delete completion --------------------------------------
|
||||
|
||||
// deleteOne runs a single-row delete, returning ErrNotFound when nothing matched
|
||||
// and bumping the generation on success.
|
||||
func (s *Store) deleteOne(ctx context.Context, query string, args ...any) error {
|
||||
return pgx.BeginFunc(ctx, s.pool, func(tx pgx.Tx) error {
|
||||
tag, err := tx.Exec(ctx, `DELETE FROM rules WHERE id = $1`, id)
|
||||
tag, err := tx.Exec(ctx, query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -491,3 +499,98 @@ func (s *Store) DeleteRule(ctx context.Context, id int64) error {
|
||||
return bump(ctx, tx)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Store) DeleteZone(ctx context.Context, name string) error {
|
||||
return s.deleteOne(ctx, `DELETE FROM zones WHERE name = $1`, name)
|
||||
}
|
||||
|
||||
func (s *Store) DeleteFabric(ctx context.Context, name string) error {
|
||||
return s.deleteOne(ctx, `DELETE FROM fabrics WHERE name = $1`, name)
|
||||
}
|
||||
|
||||
func (s *Store) DeleteDevice(ctx context.Context, name string) error {
|
||||
return s.deleteOne(ctx, `DELETE FROM devices WHERE name = $1`, name)
|
||||
}
|
||||
|
||||
func (s *Store) DeletePortGroup(ctx context.Context, name string) error {
|
||||
return s.deleteOne(ctx, `DELETE FROM portgroups WHERE name = $1`, name)
|
||||
}
|
||||
|
||||
func (s *Store) DeleteAddressGroup(ctx context.Context, name string) error {
|
||||
return s.deleteOne(ctx, `DELETE FROM address_groups WHERE name = $1`, name)
|
||||
}
|
||||
|
||||
func (s *Store) DeleteBinding(ctx context.Context, device, zone string) error {
|
||||
return s.deleteOne(ctx, `DELETE FROM bindings WHERE device = $1 AND zone = $2`, device, zone)
|
||||
}
|
||||
|
||||
func (s *Store) GetAddressGroup(ctx context.Context, name string) (model.AddressGroup, error) {
|
||||
var g model.AddressGroup
|
||||
var members, resolved []byte
|
||||
err := s.pool.QueryRow(ctx, `
|
||||
SELECT name, type, members, refresh, description, resolved, resolved_at
|
||||
FROM address_groups WHERE name = $1`, name,
|
||||
).Scan(&g.Name, &g.Type, &members, &g.Refresh, &g.Description, &resolved, &g.ResolvedAt)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return g, ErrNotFound
|
||||
}
|
||||
if err != nil {
|
||||
return g, err
|
||||
}
|
||||
if err := json.Unmarshal(members, &g.Members); err != nil {
|
||||
return g, err
|
||||
}
|
||||
return g, json.Unmarshal(resolved, &g.Resolved)
|
||||
}
|
||||
|
||||
func (s *Store) GetPortGroup(ctx context.Context, name string) (model.PortGroup, error) {
|
||||
var p model.PortGroup
|
||||
var ports []byte
|
||||
err := s.pool.QueryRow(ctx,
|
||||
`SELECT name, proto, ports FROM portgroups WHERE name = $1`, name,
|
||||
).Scan(&p.Name, &p.Proto, &ports)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return p, ErrNotFound
|
||||
}
|
||||
if err != nil {
|
||||
return p, err
|
||||
}
|
||||
return p, json.Unmarshal(ports, &p.Ports)
|
||||
}
|
||||
|
||||
func (s *Store) GetBinding(ctx context.Context, device, zone string) (model.Binding, error) {
|
||||
var b model.Binding
|
||||
var ifaces []byte
|
||||
err := s.pool.QueryRow(ctx,
|
||||
`SELECT device, zone, interfaces FROM bindings WHERE device = $1 AND zone = $2`, device, zone,
|
||||
).Scan(&b.Device, &b.Zone, &ifaces)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return b, ErrNotFound
|
||||
}
|
||||
if err != nil {
|
||||
return b, err
|
||||
}
|
||||
return b, json.Unmarshal(ifaces, &b.Interfaces)
|
||||
}
|
||||
|
||||
func (s *Store) GetRule(ctx context.Context, id int64) (model.Rule, error) {
|
||||
var r model.Rule
|
||||
var source, dest, ports []byte
|
||||
err := s.pool.QueryRow(ctx, `
|
||||
SELECT id, priority, action, source, dest, proto, COALESCE(portgroup, ''), ports, log, comment
|
||||
FROM rules WHERE id = $1`, id,
|
||||
).Scan(&r.ID, &r.Priority, &r.Action, &source, &dest, &r.Proto, &r.PortGroup, &ports, &r.Log, &r.Comment)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return r, ErrNotFound
|
||||
}
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
if err := json.Unmarshal(source, &r.Source); err != nil {
|
||||
return r, err
|
||||
}
|
||||
if err := json.Unmarshal(dest, &r.Dest); err != nil {
|
||||
return r, err
|
||||
}
|
||||
return r, json.Unmarshal(ports, &r.Ports)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user