func upsertBaseComponentTx(tx *sqlx.Tx, deviceID types.DeviceID, name string, c types.Component) (int64, error) { base := c.GetBaseComponent() existingBaseComp, found := getBaseComponentTx(tx, deviceID, name) if !found { // not found: do insert q := "INSERT INTO component (device_id, name, make, model, type) VALUES (?, ?, ?, ?, ?)" res, err := tx.Exec(q, deviceID, name, base.Make, base.Model, c.Type()) if err != nil { return 0, fmt.Errorf("error inserting component: %v", err) } id, err := res.LastInsertId() // Get ID from insert if err != nil || id == 0 { return 0, fmt.Errorf("error or zero-value ID (id: %v, err: %v)", id, err) } Log.Debug("inserted component", "id", id, "base_component", base, "stmt", q) return id, nil } // found: do update q := "UPDATE component SET make=?, model=?, type=? WHERE id=?;" _, err := tx.Exec(q, base.Make, base.Model, c.Type(), existingBaseComp.ID) if err != nil { return 0, fmt.Errorf("error updating base component: %v", err) } Log.Debug("updated component", "base", base, "query", q, "update_err", err) return existingBaseComp.ID, err }
// PostComponent will notify all listeners of a change to the provided // Component. The specific type of change should by provided in the // ActionsMask. func (n *Notifier) PostComponent(id types.ComponentID, comp types.Component, amask ActionsMask) { nchans := make(map[chan interface{}]struct{}) // A list of channels to notify // Get all of the notification channels that match this component & action n.lock.RLock() defer n.lock.RUnlock() // Get notification channels listening for components with matching IDs if filterList, ok := n.componentListenersFilteredByID[id]; ok { for nchan, atypes := range filterList { // atypes == 0 means the filter is listening to all actions // atypes & atype should be nonzero if atypes contains the bit representing atype if atypes == 0 || atypes&amask != 0 { nchans[nchan] = struct{}{} } } } // Get notification channels listening for components with matching types if filterList, ok := n.componentListenersFilteredByType[comp.Type()]; ok { for nchan, atypes := range filterList { // atypes == 0 means the filter is listening to all actions // atypes & atype should be nonzero if atypes contains the bit representing atype if atypes == 0 || atypes&amask != 0 { nchans[nchan] = struct{}{} } } } // Get notification channels listening for all components for nchan, atypes := range n.unfilteredComponentListeners { // atypes == 0 means the filter is listening to all actions // atypes & atype should be nonzero if atypes contains the bit representing atype if atypes == 0 || atypes&amask != 0 { nchans[nchan] = struct{}{} } } // Get notification channels listening for any-and-all notifications for nchan, atypes := range n.allNotificationListeners { // atypes == 0 means the filter is listening to all actions // atypes & atype should be nonzero if atypes contains the bit representing atype if atypes == 0 || atypes&amask != 0 { nchans[nchan] = struct{}{} } } n.log.Debug("channels during PostComponent", "componentListenersFilteredByID", n.componentListenersFilteredByID, "componentListenersFilteredByType", n.componentListenersFilteredByType, "unfilteredComponentListeners", n.unfilteredComponentListeners, "allNotificationListeners", n.allNotificationListeners) n.log.Debug("matching (but not yet authorized) channels", "nchans", nchans) // Post to authorized channels cnotif := ComponentNotification{ ID: id, Component: comp, Action: amask, } for nchan := range nchans { if token, ok := n.authTokenByChannel[nchan]; ok { if n.authorizor.Authorize(token, "components:-unimplemented-data-type:") { n.doPost(nchan, cnotif) } } } }