surrealpatch/db/lives.go
Tobie Morgan Hitchcock 36e7d8ed3a Flush websocket notifications correctly
Websocket notifications were cleared/flushed regardless of whether individual statements were successful or not.

Now notifications are shifted onto the stack, or removed if the statement is unsuccessful. Once the full query has been processed, all pending notifications are flushed to all websockets (ignoring the current connection frin which th query originated).
2018-01-31 09:15:29 +00:00

152 lines
3.1 KiB
Go

// Copyright © 2016 Abcum Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"context"
"github.com/abcum/surreal/sql"
"github.com/abcum/surreal/util/data"
)
// Lives checks if any table views are specified for
// this table, and executes them in name order.
func (d *document) lives(ctx context.Context, when method) (err error) {
// Get the ID of the current fibre
// connection so that we can check
// against the ID of live queries.
id := ctx.Value(ctxKeyId).(string)
// If this document has not changed
// then there is no need to update
// any registered live queries.
if !d.changed() {
return nil
}
// Get the foreign read-only tables
// specified for this table, and
// update values which have changed.
lvs, err := d.getLV()
if err != nil {
return err
}
if len(lvs) > 0 {
for _, lv := range lvs {
var ok bool
var con *socket
var doc *data.Doc
if con, ok = sockets[lv.FB]; ok {
ctx = con.ctx(d.ns, d.db)
// Check whether the change was made by
// the same connection as the live query,
// and if it is then don't notify changes.
if id == lv.FB {
continue
}
// Check whether this live query has the
// necessary permissions to view this
// document, or continue to the next query.
ok, err = d.grant(ctx, when)
if err != nil {
continue
} else if !ok {
continue
}
// Check whether this document matches the
// filter conditions for the live query and
// if not, then continue to the next query.
ok, err = d.check(ctx, lv.Cond)
if err != nil {
continue
} else if !ok {
continue
}
switch lv.Diff {
// If the live query has specified to only
// receive diff changes, then there will be
// no project fields for this query.
case true:
doc = d.diff()
// If the query has project fields which it
// wants to receive, then let's fetch thses
// fields, and return them to the socket.
case false:
for _, v := range lv.Expr {
if _, ok := v.Expr.(*sql.All); ok {
doc = d.current
break
}
}
if doc == nil {
doc = data.New()
}
for _, e := range lv.Expr {
switch v := e.Expr.(type) {
case *sql.All:
break
default:
v, err := d.i.e.fetch(ctx, v, d.current)
if err != nil {
continue
}
doc.Set(v, e.Field)
}
}
}
switch when {
case _CREATE:
con.queue(id, lv.ID, "CREATE", doc.Data())
case _UPDATE:
con.queue(id, lv.ID, "UPDATE", doc.Data())
case _DELETE:
con.queue(id, lv.ID, "DELETE", d.id)
}
}
}
}
return
}