Skip to content

Commit

Permalink
support saving capabilities per registry, support ?inline/filter on w…
Browse files Browse the repository at this point in the history
…rite-ops

Signed-off-by: Doug Davis <[email protected]>
  • Loading branch information
duglin committed Dec 22, 2024
1 parent 455388d commit 4802d35
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 82 deletions.
44 changes: 3 additions & 41 deletions registry/capabilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ package registry

import (
"fmt"
log "github.com/duglin/dlog"
// "reflect"
"slices"
"sort"
"strings"

log "github.com/duglin/dlog"
)

type Capabilities struct {
Expand Down Expand Up @@ -144,51 +144,13 @@ func ParseCapabilitiesJSON(buf []byte) (*Capabilities, error) {
err := Unmarshal(buf, &cap)
if err != nil {
if strings.HasPrefix(err.Error(), "unknown field ") {
return nil, fmt.Errorf("Unknown capability: %s",
err.Error()[14:])
err = fmt.Errorf("Unknown capability: %s", err.Error()[14:])
}
return nil, err
}
return &cap, nil

/*
tmpmap := map[string]any{}
// check generial json syntax first
if err := Unmarshal(buf, &tmpmap); err != nil {
return nil, err
}
// Look for unknown keys
capValue := reflect.ValueOf(cap)
for key, _ := range tmpmap {
fieldVal := capValue.FieldByNameFunc(func(name string) bool {
return strings.ToLower(name) == key
})
if !fieldVal.IsValid() {
return nil, fmt.Errorf("Unknown capability: %s", key)
}
}
// Now parse into 'cap'
if err := Unmarshal(buf, &cap); err != nil {
return nil, err
}
return &cap, nil
*/
}

// Mutable []string `json:"mutable,omitempty"`
// Pagination *bool `json:"pagination,omitempty"`
// QueryParameters []string `json:"queryparameters,omitempty"`
// Schemas []string `json:"schemas,omitempty"`
// ShortSelf *bool `json:"shortself,omitempty"`
// SpecVersions []string `json:"specversions,omitempty"`

func (c *Capabilities) MutableEnabled(str string) bool {
return ArrayContains(c.Mutable, strings.ToLower(str))
}
Expand Down
37 changes: 33 additions & 4 deletions registry/entity.go
Original file line number Diff line number Diff line change
Expand Up @@ -1360,12 +1360,18 @@ var OrderedSpecProps = []*Attribute{
},
{
Name: "capabilities",
Type: STRING,
Type: OBJECT, // This ensures the client sent a map
ReadOnly: false,
Attributes: Attributes{
"*": &Attribute{
Name: "*",
Type: ANY,
},
},

internals: AttrInternals{
types: StrTypes(ENTITY_REGISTRY),
dontStore: false,
dontStore: true,
getFn: func(e *Entity, info *RequestInfo) any {
// Need to explicitly ask for "capabilities", ?inline=* won't
// do it
Expand All @@ -1374,8 +1380,31 @@ var OrderedSpecProps = []*Attribute{
}
return nil
},
checkFn: nil,
updateFn: nil,
checkFn: func(e *Entity) error {
// Yes it's weird to store it in #capabilities but
// it's actually easier to do it this way. Trying to covert
// map[string]any <-> Capabilities is really annoying
val := e.NewObject["capabilities"]
if !IsNil(val) {
// Is speed is ever a concern here, just save the raw
// json from the input stream instead from http processing
valStr := ToJSON(val)
cap, err := ParseCapabilitiesJSON([]byte(valStr))
if err != nil {
return err
}
if err = cap.Validate(); err != nil {
return err
}
e.NewObject["#capabilities"] = valStr
delete(e.NewObject, "capabilities")
e.Registry.Capabilities = cap
}
return nil
},
updateFn: func(e *Entity) error {
return nil
},
},
},
{
Expand Down
18 changes: 9 additions & 9 deletions registry/httpStuff.go
Original file line number Diff line number Diff line change
Expand Up @@ -1140,7 +1140,7 @@ func HTTPPutPost(info *RequestInfo) error {
}

// Return HTTP GET of Registry root
return SerializeQuery(info, []string{""}, "Registry", nil)
return SerializeQuery(info, []string{""}, "Registry", info.Filters)
}

// URL: /GROUPs[/gID]...
Expand Down Expand Up @@ -1183,7 +1183,7 @@ func HTTPPutPost(info *RequestInfo) error {
}

// Return HTTP GET of Groups created or updated
return SerializeQuery(info, paths, "Coll", nil)
return SerializeQuery(info, paths, "Coll", info.Filters)
}

if len(info.Parts) == 2 {
Expand Down Expand Up @@ -1212,7 +1212,8 @@ func HTTPPutPost(info *RequestInfo) error {
}

// Return HTTP GET of Group
return SerializeQuery(info, []string{group.Path}, "Entity", nil)
return SerializeQuery(info, []string{group.Path}, "Entity",
info.Filters)
}

// Must be PUT/POST /GROUPs/gID/...
Expand Down Expand Up @@ -1278,7 +1279,7 @@ func HTTPPutPost(info *RequestInfo) error {
}

// Return HTTP GET of Resources created or modified
return SerializeQuery(info, paths, "Coll", nil)
return SerializeQuery(info, paths, "Coll", info.Filters)
}

if len(info.Parts) > 3 {
Expand Down Expand Up @@ -1441,7 +1442,7 @@ func HTTPPutPost(info *RequestInfo) error {
info.StatusCode = http.StatusCreated
}

return SerializeQuery(info, []string{meta.Path}, "Entity", nil)
return SerializeQuery(info, []string{meta.Path}, "Entity", info.Filters)
}

// Just double-check
Expand Down Expand Up @@ -1552,7 +1553,7 @@ func HTTPPutPost(info *RequestInfo) error {
if len(paths) == 0 {
paths = []string{"!"} // Force an empty collection to be returned
}
return SerializeQuery(info, paths, "Coll", nil)
return SerializeQuery(info, paths, "Coll", info.Filters)
}

if len(info.Parts) == 6 {
Expand Down Expand Up @@ -1657,7 +1658,7 @@ func HTTPPutPost(info *RequestInfo) error {
paths = []string{strings.Join(info.Parts, "/")}
}

return SerializeQuery(info, paths, what, nil)
return SerializeQuery(info, paths, what, info.Filters)
}

func HTTPPUTCapabilities(info *RequestInfo) error {
Expand Down Expand Up @@ -1806,7 +1807,7 @@ func HTTPSetDefaultVersionID(info *RequestInfo) error {
return err
}

return SerializeQuery(info, []string{resource.Path}, "Entity", nil)
return SerializeQuery(info, []string{resource.Path}, "Entity", info.Filters)
}

func HTTPDelete(info *RequestInfo) error {
Expand Down Expand Up @@ -2293,7 +2294,6 @@ func ExtractIncomingObject(info *RequestInfo, body []byte) (Object, error) {
body = []byte("{}") // Be forgiving
}

// err = json.Unmarshal(body, &IncomingObj)
err := Unmarshal(body, &IncomingObj)
if err != nil {
info.StatusCode = http.StatusBadRequest
Expand Down
52 changes: 26 additions & 26 deletions registry/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,39 +169,39 @@ func ParseRequest(tx *Tx, w http.ResponseWriter, r *http.Request) (*RequestInfo,
info.HasNested = r.URL.Query().Has("nested")

if r.URL.Query().Has("inline") {
// Only pick up inlining values if we're doing a GET, not write ops
if strings.EqualFold(r.Method, "GET") {
for _, value := range r.URL.Query()["inline"] {
for _, p := range strings.Split(value, ",") {
if p == "" || p == "*" {
p = "*"
} else {
// if we're not at the root then we need to twiddle
// the inline path to add the HTTP Path as a prefix
if info.Abstract != "" {
// want: p = info.Abstract + "." + p in UI format
absPP, err := PropPathFromPath(info.Abstract)
if err != nil {
info.StatusCode = http.StatusBadRequest
return info, err
}
pPP, err := PropPathFromUI(p)
if err != nil {
info.StatusCode = http.StatusBadRequest
return info, err
}
p = absPP.Append(pPP).UI()
// OLD: Only pick up inlining values if we're doing a GET, not write ops
// if strings.EqualFold(r.Method, "GET")
for _, value := range r.URL.Query()["inline"] {
for _, p := range strings.Split(value, ",") {
if p == "" || p == "*" {
p = "*"
} else {
// if we're not at the root then we need to twiddle
// the inline path to add the HTTP Path as a prefix
if info.Abstract != "" {
// want: p = info.Abstract + "." + p in UI format
absPP, err := PropPathFromPath(info.Abstract)
if err != nil {
info.StatusCode = http.StatusBadRequest
return info, err
}
pPP, err := PropPathFromUI(p)
if err != nil {
info.StatusCode = http.StatusBadRequest
return info, err
}
p = absPP.Append(pPP).UI()
}
if err := info.AddInline(p); err != nil {
info.StatusCode = http.StatusBadRequest
return info, err
}
}
if err := info.AddInline(p); err != nil {
info.StatusCode = http.StatusBadRequest
return info, err
}
}
}
}

// if strings.EqualFold(r.Method, "GET")
err = info.ParseFilters()
if err != nil {
info.StatusCode = http.StatusBadRequest
Expand Down
1 change: 1 addition & 0 deletions registry/jsonWriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ func (jw *JsonWriter) WriteEntity() error {
jw.Indent()

jsonIt := func(e *Entity, info *RequestInfo, key string, val any, attr *Attribute) error {
log.VPrintf(4, "jsonIt: %q", key)
if key == "$space" {
addSpace = true
return nil
Expand Down
21 changes: 19 additions & 2 deletions registry/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,12 +251,13 @@ func FindRegistryBySID(tx *Tx, sid string) (*Registry, error) {
tx.Registry = reg
}
reg.Entity.Registry = reg
reg.Capabilities = DefaultCapabilities
reg.tx = tx

reg.tx.AddRegistry(reg)

reg.LoadCapabilities()
reg.LoadModel()

return reg, nil
}

Expand Down Expand Up @@ -332,16 +333,32 @@ func FindRegistry(tx *Tx, id string) (*Registry, error) {
}

reg.Entity.Registry = reg
reg.Capabilities = DefaultCapabilities
reg.tx = tx

reg.tx.AddRegistry(reg)

reg.LoadCapabilities()
reg.LoadModel()

return reg, nil
}

func (reg *Registry) LoadCapabilities() *Capabilities {
capVal, ok := reg.Object["#capabilities"]
if !ok {
// No custom capabilities, use the default one
reg.Capabilities = DefaultCapabilities
} else {
// Custom capabilities
capStr, ok := capVal.(string)
PanicIf(!ok, "not a byte array: %T", capVal)
cap, err := ParseCapabilitiesJSON([]byte(capStr))
Must(err)
reg.Capabilities = cap
}
return reg.Capabilities
}

func (reg *Registry) LoadModel() *Model {
return LoadModel(reg)
}
Expand Down

0 comments on commit 4802d35

Please sign in to comment.