From 5bb473404fba3033b51e21ec601cbcfb6d0c1737 Mon Sep 17 00:00:00 2001 From: Gibheer Date: Sun, 9 May 2021 21:22:52 +0200 Subject: [PATCH] add support for attributes in pools Add support to show all pool attributes and set them. Also add some helper functions to FieldMap to change and check the requested attributes. This was needed because the layer3domain needs to be set through attributes instead of a link function (this should be changed, but for now we will be compatible to ndcli). So we filter out the layer3domain name and replace it with the ID, so that the update can do both at the same time. Maybe there is a better way to handle this in the future. --- pool.go | 86 +++++++++++++++++++++++++++++++++++++++++++++++++ types/fields.go | 9 ++++++ 2 files changed, 95 insertions(+) diff --git a/pool.go b/pool.go index ce6c1ef..dc8c7ae 100644 --- a/pool.go +++ b/pool.go @@ -1,6 +1,8 @@ package main import ( + "database/sql" + "encoding/json" "fmt" "dim/query" @@ -141,9 +143,93 @@ func PoolList(c *Context, req Request, res *Response) error { } func PoolGetAttr(c *Context, req Request, res *Response) error { + name := "" + if err := req.ParseAtLeast(1, &name); err != nil { + res.AddMessage(LevelError, "could not parse name: %s", err) + return nil + } + if name == "" { + res.AddMessage(LevelError, "empty name was provided") + return nil + } + + result := json.RawMessage{} + selClause := query.FieldsToJSON("p", map[string]string{ + "name": "p.name", + "modified_by": "p.modified_by", + "modified_at": "p.modified_at", + "created_by": "p.created_by", + "created_at": "p.created_at", + "layer3domain": "l.name", + }) + queryStr := fmt.Sprintf(`select %s from pools p join layer3domains l + on p.layer3domain_id = l.id + where p.name = $1`, selClause) + err := c.tx.QueryRow(queryStr, name).Scan(&result) + if err != nil { + res.AddMessage(LevelError, "could not return result") + return fmt.Errorf("could not get pool '%s': %s - query: %s", name, err, queryStr) + } + res.Result = result return nil } func PoolSetAttr(c *Context, req Request, res *Response) error { + name := "" + attrs := types.FieldMap{} + if err := req.ParseAtLeast(2, &name, &attrs); err != nil { + res.AddMessage(LevelError, "could not parse options: %s", err) + return nil + } + if name == "" { + res.AddMessage(LevelError, "empty name was provided") + return nil + } + if attrs.Size() == 0 { + res.AddMessage(LevelError, "no key/value pairs provided to update") + return nil + } + + // TODO this is ugly. Can we have better API somehow? + fieldMap := map[string]string{ + "name": "", + "modified_by": "", + "modified_at": "", + "created_by": "", + "created_at": "", + "layer3domain_id": "", + } + if attrs.Contains("subnets") { + res.AddMessage(LevelError, "can not set subnets as attributes") + return nil + } + if attrs.Contains("layer3domain") { + l3name := attrs.Fields()["layer3domain"] + attrs.Delete("layer3domain") + l3id := 0 + err := c.tx.QueryRow(`select id from layer3domains where name = $1`, l3name).Scan(&l3id) + if err != nil { + if err == sql.ErrNoRows { + res.AddMessage(LevelError, "layer3domain '%s' does not exist", l3name) + return nil + } + res.AddMessage(LevelError, "could not get layer3domain") + return fmt.Errorf("could not fetch layer3domain id for name '%s': %#v", l3name, err) + } + attrs.Set("layer3domain_id", l3id) + } + setClause, args, err := query.FieldMapToUpdate(attrs, fieldMap) + if err != nil { + res.AddMessage(LevelError, "could not encode requested attributes: %s", err) + return nil + } + queryStr := fmt.Sprintf("update pools p set %s where name = $%d", setClause, len(args)+1) + args = append(args, name) // don't forget to add the where clause parameter + c.Logf(LevelInfo, "query: %s - args: %#v", queryStr, args) + if _, err := c.tx.Exec(queryStr, args...); err != nil { + res.AddMessage(LevelError, "could not set attributes") + c.Logf(LevelError, "could not set attributes on layer3domain '%s': %s - query: `%s` - args: `%#v`", name, err, queryStr, args) + return nil + } return nil } diff --git a/types/fields.go b/types/fields.go index 81a8e12..0a4232a 100644 --- a/types/fields.go +++ b/types/fields.go @@ -94,11 +94,20 @@ func (fm *FieldMap) UnmarshalJSON(raw []byte) error { return nil } +func (fm FieldMap) Contains(key string) bool { + _, found := fm.fields[key] + return found +} + // Set adds a key to the field map. func (fm FieldMap) Set(key string, val interface{}) { fm.fields[key] = val } +func (fm FieldMap) Delete(key string) { + delete(fm.fields, key) +} + // Fields returns all key/value pairs. func (fm FieldMap) Fields() map[string]interface{} { return fm.fields