Skip to content

Commit

Permalink
some DELETE testing and ensure our cache is clean before we generate …
Browse files Browse the repository at this point in the history
…results

Signed-off-by: Doug Davis <[email protected]>
  • Loading branch information
duglin committed Jan 13, 2025
1 parent 3944b06 commit 1b5b19e
Show file tree
Hide file tree
Showing 9 changed files with 84 additions and 18 deletions.
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,20 +121,18 @@ TODOs:
- support validating that xref points to the same resource def
- support readonly - remove resource.readonly
- remove "readonly" attribute from model Resources, add to Resource
- format is optional in schema spec
- allow $meta on hasdoc=false resources
- error w/o ?nested + children
- Add content-disposition header for hasdoc resources
- inline=model, requires ?nested. Make sure /meta requires ?nested too
- add "compatibility" to resources
- multi-delete must ignore all attributes except id and epoch
- fix init.sql, it's too slow due to latest xref stuff in commit 9c583e7
- add support for inline=endpoints.*
- support ETag/If-Match
- update epoch/modifiedat of parent when nested entity is added/removed
- test to ensure meta epoch changes as versions are added/removed
- ?nested isn't needed to update `model` or `meta` attributes
- test the timestamps in meta. Should change as versions are added/removed.
- ?nested isn't needed to update `model` or `meta` attributes
- remove entities from cache upon delete
- test creating a resource + lots of versions w/o ?defaultversionid-should fail
- PUT ../f1 POST .../f1/versions POT .../versions
Expand Down
19 changes: 19 additions & 0 deletions registry/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,25 @@ func (tx *Tx) RemoveFromCache(e *Entity) {
delete(tx.Cache, e.Registry.UID+"/"+e.Path)
}

func (tx *Tx) IsCacheDirty() bool {
dirty := false
for _, e := range tx.Cache {
if len(e.NewObject) != 0 {
log.Printf("Dirty: %q", e.Path)
log.Printf("NewObj:\n%s", ToJSON(e.NewObject))
log.Printf("Stack for NewObj:")
for _, s := range e.NewObjectStack {
log.Printf(" %s", s)
}
if len(e.NewObjectStack) == 0 {
log.Printf(" Enable this via entity.SetNewObject")
}
dirty = true
}
}
return dirty
}

func (tx *Tx) WriteCache(force bool) error {
for _, e := range tx.Cache {
PanicIf(!force && e.NewObject != nil, "Entity %s/%q not saved",
Expand Down
30 changes: 28 additions & 2 deletions registry/entity.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ type Entity struct {
EpochSet bool `json:"-"` // Has epoch been updated this transaction?
ModSet bool `json:"-"` // Has modifiedat been updated this transaction?
Self any `json:"-"` // Pointer to typed Entity (e.g. *Resource)

// Debugging
NewObjectStack []string `json:"-"` // stack when NewObj created via Ensure
}

type EntitySetter interface {
Expand Down Expand Up @@ -75,17 +78,32 @@ func (e *Entity) ToString() string {
return str
}

// We use this just to make sure we can set NewObjectStack when we need to
// debug stuff
func (e *Entity) SetNewObject(newObj map[string]any) {
e.NewObject = newObj

// Enable the next line when we need to debug when NewObject was created
// e.NewObjectStack = GetStack()
}

func (e *Entity) Touch() {
log.VPrintf(3, "Touch: %s/%s", e.Singular, e.UID)

// See if it's already been modified (and saved) this Tx, if so exit
if e.ModSet && e.EpochSet {
return
}

e.EnsureNewObject()
}

func (e *Entity) EnsureNewObject() {
if e.NewObject == nil {
if e.Object == nil {
e.NewObject = map[string]any{}
e.SetNewObject(map[string]any{})
} else {
e.NewObject = maps.Clone(e.Object)
e.SetNewObject(maps.Clone(e.Object))
}
}
}
Expand Down Expand Up @@ -914,6 +932,14 @@ var OrderedSpecProps = []*Attribute{
ToJSON(e.Object), ToJSON(e.NewObject))
ShowStack()
log.Printf(`========`)
log.Printf("Path: %s", e.Path)
log.Printf("Stack for NewObject:")
for _, s := range e.NewObjectStack {
log.Printf(" %s", s)
}
if len(e.NewObjectStack) == 0 {
log.Printf(" Enable this in entity.SetNewObject")
}
panic(fmt.Sprintf(`%q is nil - that's bad, fix it!`,
singular))
return fmt.Errorf(`%q is nil - that's bad, fix it!`,
Expand Down
8 changes: 8 additions & 0 deletions registry/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,10 @@ func (g *Group) UpsertResourceWithObject(rType string, id string, vID string, ob
"\"xref\" is set", strings.Join(SortedKeys(obj), ","))
}

if err = g.ValidateAndSave(); err != nil {
return nil, false, err
}

// All versions should have been deleted already so just return
return r, isNew, nil
}
Expand Down Expand Up @@ -398,5 +402,9 @@ func (g *Group) UpsertResourceWithObject(rType string, id string, vID string, ob
}
*/

if err = g.ValidateAndSave(); err != nil {
return nil, false, err
}

return r, isNew, err
}
7 changes: 6 additions & 1 deletion registry/httpStuff.go
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ func (pw *PageWriter) Done() {
name = "<b>" + name + "</b>"
}
roots += fmt.Sprintf(" <li><a href=\"%s?ui\">%s</a>\n",
pw.Info.BaseURL+"/"+r.u, name)
pw.Info.BaseURL+"/"+r.u+"</li>", name)
}

if pw.Info.RootPath == "" {
Expand Down Expand Up @@ -1197,6 +1197,11 @@ func SerializeQuery(info *RequestInfo, paths []string, what string,
filters [][]*FilterExpr) error {
start := time.Now()

// TODO comment this out at some point... maybe
// Make sure we've saved everything in the cache before we generate
// the results. If the stack isn't shown, enable it in entity.SetNewObject
PanicIf(info.tx.IsCacheDirty(), "Unwritten stuff in cache")

defer func() {
if log.GetVerbose() > 3 {
diff := time.Now().Sub(start).Truncate(time.Millisecond)
Expand Down
20 changes: 12 additions & 8 deletions registry/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,13 @@ func (reg *Registry) LoadModelFromFile(file string) error {
}

func (reg *Registry) Update(obj Object, addType AddType, doChildren bool) error {
reg.NewObject = obj
reg.SetNewObject(obj)

// Make sure we always have an ID
if IsNil(reg.NewObject["registryid"]) {
reg.EnsureNewObject()
reg.NewObject["registryid"] = reg.UID
}

if doChildren {
colls := reg.GetCollections()
Expand Down Expand Up @@ -455,12 +461,6 @@ func (reg *Registry) Update(obj Object, addType AddType, doChildren bool) error
}
}

// Make sure we always have an ID
if IsNil(reg.NewObject["registryid"]) {
reg.EnsureNewObject()
reg.NewObject["registryid"] = reg.UID
}

return reg.ValidateAndSave()
}

Expand Down Expand Up @@ -575,7 +575,7 @@ func (reg *Registry) UpsertGroupWithObject(gType string, id string, obj Object,

if isNew || obj != nil {
if obj != nil {
g.NewObject = obj
g.SetNewObject(obj)
}

if addType == ADD_PATCH {
Expand Down Expand Up @@ -629,6 +629,10 @@ func (reg *Registry) UpsertGroupWithObject(gType string, id string, obj Object,
}
}

if err = reg.ValidateAndSave(); err != nil {
return nil, false, err
}

return g, isNew, nil
}

Expand Down
8 changes: 6 additions & 2 deletions registry/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ func (r *Resource) UpsertMetaWithObject(obj Object, addType AddType, createVersi

// Apply properties
existingNewObj := meta.NewObject // Should be nil when using http
meta.NewObject = obj
meta.SetNewObject(obj)
meta.Entity.EnsureNewObject()

if meta.NewObject != nil && addType == ADD_PATCH {
Expand Down Expand Up @@ -631,6 +631,10 @@ func (r *Resource) UpsertMetaWithObject(obj Object, addType AddType, createVersi
}
}

if err = meta.ValidateAndSave(); err != nil {
return nil, false, err
}

return meta, isNew, nil
}
}
Expand Down Expand Up @@ -784,7 +788,7 @@ func (r *Resource) UpsertVersionWithObject(id string, obj Object, addType AddTyp
}
}

v.NewObject = obj
v.SetNewObject(obj)

if addType == ADD_PATCH {
// Copy existing props over if the incoming obj doesn't set them
Expand Down
6 changes: 4 additions & 2 deletions tests/http1_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6173,11 +6173,12 @@ func TestHTTPDelete(t *testing.T) {
`,
})

// Make sure we ignore random attributes too
xCheckHTTP(t, reg, &HTTPTest{
Name: "DELETE /dirs - d3",
URL: "/dirs",
Method: "DELETE",
ReqBody: `{"d3":{"dirid": "d3", "epoch":1}}`,
ReqBody: `{"d3":{"dirid": "d3", "epoch":1, "foo": "bar"}}`,
Code: 204,
ResBody: ``,
})
Expand Down Expand Up @@ -6474,8 +6475,9 @@ func TestHTTPDelete(t *testing.T) {
xHTTP(t, reg, "DELETE", "/dirs/d1/files",
`{"f2":{},"f4":{"meta":{"epoch":3}}}`,
400, "Epoch value for \"f4\" must be 1 not 3\n")
// Make sure we ignore random attributes too
xHTTP(t, reg, "DELETE", "/dirs/d1/files",
`{"f4":{},"f5":{"meta":{"epoch":1}}}`,
`{"f4":{},"f5":{"meta":{"epoch":1,"foo":"bar"}, "foo":"bar"}}`,
204, "")

xHTTP(t, reg, "DELETE", "/dirs/d1/files", `{}`, 204, "") // no-op
Expand Down
Empty file removed tests/type
Empty file.

0 comments on commit 1b5b19e

Please sign in to comment.