func ServiceSchedulerTest(tr *TestRunner) {
	Convey("should receive simple task", func() {
		tr.When(sarif.CreateMessage("schedule/duration", map[string]interface{}{
			"duration": "300ms",
		}))

		reply := tr.Expect()
		So(reply, ShouldBeAction, "schedule/created")
	})

	Convey("should receive complex task", func() {
		tr.When(sarif.CreateMessage("schedule/duration", map[string]interface{}{
			"duration": "100ms",
			"reply": sarif.Message{
				Action:      "push/text",
				Destination: tr.Id,
				Text:        "reminder finished",
			},
		}))

		reply := tr.Expect()
		So(reply, ShouldBeAction, "schedule/created")
	})

	Convey("should emit both tasks", func() {
		reply := tr.Expect()
		So(reply, ShouldBeAction, "push/text")
		So(reply.Text, ShouldEqual, "reminder finished")

		reply = tr.Expect()
		So(reply, ShouldBeAction, "schedule/finished")
		So(reply.Text, ShouldStartWith, "Reminder from")
	})
}
Beispiel #2
0
func (s *Service) handlePut(msg sarif.Message) {
	collection, key := parseAction("store/put/", msg.Action)
	if collection == "" {
		s.ReplyBadRequest(msg, errors.New("No collection specified."))
		return
	}

	if len(msg.Payload.Raw) == 0 && msg.Text != "" {
		v, _ := json.Marshal(msg.Text)
		msg.Payload.Raw = json.RawMessage(v)
	}
	var p interface{}
	if err := msg.DecodePayload(&p); err != nil {
		s.ReplyBadRequest(msg, err)
		return
	}
	// TODO: maybe a JSON payload consistency check
	doc, err := s.Store.Put(&Document{
		Collection: collection,
		Key:        key,
		Value:      msg.Payload.Raw,
	})
	if err != nil {
		s.ReplyInternalError(msg, err)
		return
	}

	doc.Value = nil
	reply := sarif.CreateMessage("store/updated/"+doc.Collection+"/"+doc.Key, doc)
	s.Reply(msg, reply)

	pub := sarif.CreateMessage("store/updated/"+doc.Collection+"/"+doc.Key, doc)
	s.Publish(pub)
}
Beispiel #3
0
func (s *Service) handleQuery(msg sarif.Message) {
	query := msg.Text

	// Query and wait for first answer
	answers, errs := know.Ask(query)
	ans, ok := <-answers

	if !ok {
		// No answer found? Check for errors.
		err, ok := <-errs
		if !ok {
			// No errors found? Send negative answer
			pl := MessageAnswer{
				Query: query,
			}
			s.Reply(msg, sarif.CreateMessage("knowledge/noanswer", pl))
			return
		}

		// Error received, forward.
		s.ReplyInternalError(msg, err)
		return
	}

	// Send answer.
	pl := MessageAnswer{
		ans.Question,
		ans.Answer,
		ans.Provider,
	}
	s.Reply(msg, sarif.CreateMessage("knowledge/answer", pl))
	return
}
func ServiceEventsTest(tr *TestRunner) {
	Convey("should store a new event", func() {
		tr.When(sarif.CreateMessage("event/new", map[string]interface{}{
			"action": "user/drink/coffee",
			"text":   "User drinks coffee.",
		}))

		reply := tr.Expect()
		So(reply, ShouldBeAction, "event/created")
	})

	Convey("should store return last event", func() {
		tr.When(sarif.CreateMessage("event/last", map[string]interface{}{
			"action": "user/drink/coffee",
		}))

		reply := tr.Expect()
		So(reply, ShouldBeAction, "event/found")

		payload := Event{}
		reply.DecodePayload(&payload)
		So(payload.Text, ShouldEqual, "User drinks coffee.")
	})

	Convey("should record other messages", func() {
		// Create test events
		tr.When(sarif.CreateMessage("event/record", map[string]interface{}{
			"action": "some/value/changed",
			"time":   time.Now().Add(-100 * time.Minute),
		}))
		So(tr.Expect(), ShouldBeAction, "event/recording")

		tr.When(sarif.Message{
			Action: "some/value/changed",
			Text:   "some value has changed",
		})
		tr.Wait()

		tr.When(sarif.CreateMessage("event/last", map[string]interface{}{
			"action": "some/value/changed",
		}))

		reply := tr.Expect()
		So(reply, ShouldBeAction, "event/found")

		payload := Event{}
		reply.DecodePayload(&payload)
		So(payload.Text, ShouldEqual, "some value has changed")
	})
}
Beispiel #5
0
func (s *Service) handleDel(msg sarif.Message) {
	collection, key := parseAction("store/del/", msg.Action)
	if collection == "" || key == "" {
		s.ReplyBadRequest(msg, errors.New("No collection or key specified."))
		return
	}
	if err := s.Store.Del(collection, key); err != nil {
		s.ReplyInternalError(msg, err)
		return
	}

	s.Reply(msg, sarif.CreateMessage("store/deleted/"+collection+"/"+key, nil))
	s.Publish(sarif.CreateMessage("store/deleted/"+collection+"/"+key, nil))
}
Beispiel #6
0
func (s *Service) handleScan(msg sarif.Message) {
	collection, prefix := parseAction("store/scan/", msg.Action)
	if collection == "" {
		s.ReplyBadRequest(msg, errors.New("No collection specified."))
		return
	}

	var p scanMessage
	if err := msg.DecodePayload(&p); err != nil {
		s.ReplyBadRequest(msg, err)
		return
	}
	if p.Start == "" && p.End == "" {
		if p.Prefix == "" {
			p.Prefix = prefix
		}
	}

	got, err := s.doScan(collection, p)
	if err != nil {
		s.ReplyInternalError(msg, err)
		return
	}
	s.Reply(msg, sarif.CreateMessage("store/scanned/"+collection, got))
}
Beispiel #7
0
func (cv *Conversation) handleUnknownUserMessage(msg sarif.Message) {
	pl := &MsgErrNatural{
		Original: msg.Text,
	}

	cv.SendToClient(msg.Reply(sarif.CreateMessage("err/natural", pl)))
}
Beispiel #8
0
func (t *TestRunner) UseConn(conn sarif.Conn) {
	t.conn = conn
	t.Publish(sarif.CreateMessage("proto/sub", map[string]string{
		"device": t.Id,
	}))
	go t.listen()
}
Beispiel #9
0
func (s *Service) handleEventList(msg sarif.Message) {
	var filter map[string]interface{}
	if err := msg.DecodePayload(&filter); err != nil {
		s.ReplyBadRequest(msg, err)
		return
	}
	if filter == nil {
		filter = make(map[string]interface{})
	}
	reverse := false
	if len(filter) == 0 {
		filter["time >="] = time.Now().Add(-24 * time.Hour)
		reverse = true
	}

	s.Log("debug", "list by filter:", filter)
	var events []Event
	err := s.Store.Scan("events", store.Scan{
		Only:    "values",
		Filter:  filter,
		Reverse: reverse,
	}, &events)
	if err != nil {
		s.ReplyInternalError(msg, err)
	}
	s.Log("debug", "list - found", len(events))

	s.Reply(msg, sarif.CreateMessage("events/listed", &aggPayload{
		Type:   "list",
		Filter: filter,
		Events: events,
		Value:  float64(len(events)),
	}))
}
Beispiel #10
0
func (s *Service) HandleQuery(msg sarif.Message) {
	var f Fact
	if err := msg.DecodePayload(&f); err != nil {
		s.ReplyBadRequest(msg, err)
		return
	}

	f, err := s.InterpretLiterals(f)
	if err != nil {
		s.ReplyInternalError(msg, err)
		return
	}

	var facts []*Fact
	if err := s.DB.Where(f).Limit(100).Find(&facts).Error; err != nil {
		s.ReplyInternalError(msg, err)
		return
	}
	if facts, err = s.AddLabelFacts(facts); err != nil {
		s.ReplyInternalError(msg, err)
		return
	}

	s.Reply(msg, sarif.CreateMessage("concepts/result", &resultPayload{
		ToJsonLd(facts),
		facts,
	}))
}
Beispiel #11
0
func (s *Service) HandleQueryExternal(msg sarif.Message) {
	var f Fact
	if err := msg.DecodePayload(&f); err != nil {
		s.ReplyBadRequest(msg, err)
		return
	}
	facts := []*Fact{&f}
	FillVariables(facts)

	var r sparql.ResourceResponse
	q := sparql.DBPedia.Query()
	q = BuildQuery(q, facts)
	if err := q.Exec(&r); err != nil {
		s.ReplyInternalError(msg, err)
		return
	}

	result := ApplyBindings(facts, r.Results.Bindings, sparql.CommonPrefixes)
	s.Reply(msg, sarif.CreateMessage("concepts/result", &resultPayload{
		ToJsonLd(result),
		result,
	}))

	for _, f := range result {
		if err := s.DB.FirstOrCreate(&f, &f).Error; err != nil {
			s.Log("err", "[reasoner] error updating external fact: "+err.Error())
		}
	}
}
Beispiel #12
0
func (s *Service) handleCounter(msg sarif.Message) {
	if msg.Text == "" {
		s.ReplyBadRequest(msg, errors.New("Please specify a counter name!"))
		return
	}
	name := msg.Text
	cnt, err := s.counterGet(name)
	if err != nil {
		s.ReplyInternalError(msg, err)
		return
	}
	newCnt := cnt
	if msg.IsAction("cmd/decrement") {
		newCnt--
	} else if msg.IsAction("cmd/increment") {
		newCnt++
	}
	if newCnt != cnt {
		if err := s.counterSet(name, newCnt); err != nil {
			s.ReplyInternalError(msg, err)
			return
		}
	}
	s.Reply(msg, sarif.CreateMessage("counter/changed/"+name, &counterMessage{name, newCnt}))
}
Beispiel #13
0
func (s *Service) handleEventRecord(msg sarif.Message) {
	var p recordPayload
	if err := msg.DecodePayload(&p); err != nil {
		s.ReplyBadRequest(msg, err)
		return
	}
	if p.Action == "" {
		s.ReplyBadRequest(msg, errors.New("No action specified"))
		return
	}

	var cfg Config
	s.cfg.Get(&cfg)
	if cfg.RecordedActions == nil {
		cfg.RecordedActions = make(map[string]bool)
	}
	if enabled := cfg.RecordedActions[p.Action]; !enabled {
		cfg.RecordedActions[p.Action] = true
		s.cfg.Set(cfg)
		s.Subscribe(p.Action, "", s.handleEventNew)
	}

	s.Log("debug", "recording action:", p.Action)
	s.Reply(msg, sarif.CreateMessage("event/recording", p))
}
Beispiel #14
0
func (s *Service) handleAuthOtp(msg sarif.Message) {
	tok := "otp/std:" + GenerateDigits()
	s.SessionTokens[tok] = time.Now().Add(time.Minute)

	s.Reply(msg, sarif.CreateMessage("auth/generated", sarif.ClientInfo{
		Auth: tok,
	}))
}
Beispiel #15
0
func (s *Service) handleLuaLoad(msg sarif.Message) {
	gen := false
	name := strings.TrimPrefix(strings.TrimPrefix(msg.Action, "lua/load"), "/")
	if name == "" {
		name, gen = sarif.GenerateId(), true
	}
	if _, ok := s.Machines[name]; ok {
		s.destroyMachine(name)
	}

	m, err := s.createMachine(name)
	if err != nil {
		s.ReplyInternalError(msg, err)
		return
	}

	var ctp ContentPayload
	if err := msg.DecodePayload(&ctp); err != nil {
		s.ReplyBadRequest(msg, err)
		return
	}
	text := msg.Text
	if ctp.Content.Url != "" {
		ct, err := content.Get(ctp.Content)
		if err != nil {
			s.ReplyBadRequest(msg, err)
		}
		text = string(ct.Data)
	}

	var gp interface{}
	msg.DecodePayload(&gp)
	out, err, _ := m.Do(text, gp)
	if err != nil {
		s.ReplyBadRequest(msg, err)
		s.destroyMachine(name)
		return
	}

	if !gen {
		f, err := os.Create(s.cfg.ScriptDir + "/" + name + ".lua")
		if err == nil {
			_, err = f.Write([]byte(text))
			defer f.Close()
		}
		if err != nil {
			s.ReplyInternalError(msg, err)
			s.destroyMachine(name)
			return
		}
	}

	s.Reply(msg, sarif.CreateMessage("lua/loaded", &MsgMachineStatus{
		name,
		"up",
		out,
	}))
}
Beispiel #16
0
func (s *Store) Get(key string, result interface{}) error {
	req := sarif.CreateMessage("store/get/"+key, nil)
	req.Destination = s.StoreName
	reply, ok := <-s.client.Request(req)
	if err := checkErr(reply, ok); err != nil {
		return err
	}
	return reply.DecodePayload(result)
}
Beispiel #17
0
func (s *Scheduler) simpleCron() {
	for {
		now := time.Now()
		nextHour := now.Add(30 * time.Minute).Round(time.Hour)
		time.Sleep(nextHour.Sub(now))
		action := strings.ToLower(time.Now().Add(5 * time.Minute).Format("cron/15h/Mon/2d/1m"))
		s.Publish(sarif.CreateMessage(action, nil))
	}
}
Beispiel #18
0
func (s *Service) handleAuthToken(msg sarif.Message) {
	tok := "token/std:" + sarif.GenerateId() + sarif.GenerateId() + sarif.GenerateId()
	s.Config.Tokens[tok] = true
	s.cfg.Set(s.Config)

	s.Reply(msg, sarif.CreateMessage("auth/generated", sarif.ClientInfo{
		Auth: tok,
	}))
}
Beispiel #19
0
func (s *Service) handleSimple(f func() error) func(sarif.Message) {
	return func(msg sarif.Message) {
		if err := f(); err != nil {
			s.ReplyInternalError(msg, err)
			return
		}
		s.Reply(msg, sarif.CreateMessage("ack/"+msg.Action, nil))
	}
}
Beispiel #20
0
func (s *Service) checkGeofences(last, curr Location) {
	var lastFences, currFences []Geofence
	err := s.Store.Scan("location_geofences", store.Scan{
		Only: "values",
		Filter: map[string]interface{}{
			"lat_min <=": last.Latitude,
			"lat_max >=": last.Latitude,
			"lng_min <=": last.Longitude,
			"lng_max >=": last.Longitude,
		},
	}, &lastFences)
	if err != nil {
		s.Log("err/internal", "retrieve last fences: "+err.Error())
	}
	err = s.Store.Scan("location_geofences", store.Scan{
		Only: "values",
		Filter: map[string]interface{}{
			"lat_min <=": curr.Latitude,
			"lat_max >=": curr.Latitude,
			"lng_min <=": curr.Longitude,
			"lng_max >=": curr.Longitude,
		},
	}, &currFences)
	if err != nil {
		s.Log("err/internal", "retrieve curr fences: "+err.Error())
	}

	for _, g := range lastFences {
		if !fenceInSlice(g, currFences) {
			s.Log("debug", "geofence leave", g)
			pl := GeofenceEventPayload{curr, g, "leave"}
			msg := sarif.CreateMessage("location/fence/leave/"+g.Name, pl)
			s.Publish(msg)
		}
	}
	for _, g := range currFences {
		if !fenceInSlice(g, lastFences) {
			s.Log("debug", "geofence enter", g)
			pl := GeofenceEventPayload{curr, g, "enter"}
			msg := sarif.CreateMessage("location/fence/enter/"+g.Name, pl)
			s.Publish(msg)
		}
	}
}
Beispiel #21
0
func (s *Service) HandleCard(msg sarif.Message) {
	uid := strings.TrimPrefix(msg.Action, "vdir/card/")
	c, ok := s.cards[uid]
	if !ok {
		s.ReplyBadRequest(msg, errors.New("No card with with UID "+uid+" found!"))
		return
	}

	s.Reply(msg, sarif.CreateMessage("vdir/card", c.Card))
}
Beispiel #22
0
func (s *Service) counterSet(name string, cnt int) error {
	ack, ok := <-s.Request(sarif.CreateMessage("store/put/counter/"+name, cnt))
	if !ok {
		return errors.New("Timeout while setting new value")
	}
	if ack.IsAction("err") {
		return errors.New(ack.Text)
	}
	return nil
}
Beispiel #23
0
func (s *Store) Batch(p []Command, result interface{}) error {
	req := sarif.CreateMessage("store/batch", &p)
	req.Destination = s.StoreName
	reply, ok := <-s.client.Request(req)
	if err := checkErr(reply, ok); err != nil {
		return err
	}

	return reply.DecodePayload(result)
}
Beispiel #24
0
func (s *Store) Put(key string, doc interface{}) (*Document, error) {
	res := &Document{}
	req := sarif.CreateMessage("store/put/"+key, doc)
	req.Destination = s.StoreName
	reply, ok := <-s.client.Request(req)
	if err := checkErr(reply, ok); err != nil {
		return nil, err
	}

	return res, reply.DecodePayload(res)
}
Beispiel #25
0
func (s *Scheduler) handle(msg sarif.Message) {
	var t ScheduleMessage
	if err := msg.DecodePayload(&t); err != nil {
		s.ReplyBadRequest(msg, err)
		return
	}

	now := time.Now()
	t.Task.Time = now
	if t.Time != "" {
		t.Task.Time = futureTime(util.ParseTime(t.Time, now))
	}
	if t.RandomAfter != "" && t.RandomBefore != "" {
		after := futureTime(util.ParseTime(t.RandomAfter, t.Task.Time))
		before := futureTime(util.ParseTime(t.RandomBefore, t.Task.Time))
		if before.Before(after) {
			after, before = before, after
		}
		maxDur := int64(before.Sub(after))
		ranDur := time.Duration(rand.Int63n(maxDur))
		t.Task.Time = after.Add(ranDur)
	}
	if t.Duration != "" {
		dur, err := util.ParseDuration(t.Duration)
		if err != nil {
			s.ReplyBadRequest(msg, err)
			return
		}
		t.Task.Time = t.Task.Time.Add(dur)
	}
	if t.Task.Reply.Action == "" {
		text := msg.Text
		if text == "" {
			text = "Reminder from " + time.Now().Format(time.RFC3339) + " finished."
		}
		t.Task.Reply = sarif.Message{
			Action:      "schedule/finished",
			Destination: msg.Source,
			Text:        text,
		}
	}
	if t.Task.Reply.CorrId == "" {
		t.Reply.CorrId = msg.Id
	}
	s.Log("info", "new task:", t)

	if _, err := s.Store.Put(t.Task.Key(), &t.Task); err != nil {
		s.ReplyInternalError(msg, err)
		return
	}

	go s.recalculateTimer()
	s.Reply(msg, sarif.CreateMessage("schedule/created", t.Task))
}
Beispiel #26
0
func (s *Service) handleServingRecord(msg sarif.Message) {
	var sv Serving
	if err := msg.DecodePayload(&sv); err != nil {
		s.ReplyBadRequest(msg, err)
		return
	}
	size, name := splitSizeName(msg.Text)
	if sv.Size == 0 {
		sv.Size = size
	}
	if sv.Name == "" {
		sv.Name = name
	}
	if sv.Product == nil {
		ps, err := s.findProduct(sv.Name)
		if err != nil {
			s.ReplyBadRequest(msg, err)
			return
		}

		if len(ps) > 1 {
			pList := ""
			for _, p := range ps {
				pList += "\n- " + p.Name
			}
			s.ReplyBadRequest(msg, fmt.Errorf("%d products named %s found.%s", len(ps), sv.Name, pList))
			return
		}
		if len(ps) == 1 {
			sv.Product = &ps[0]
		}
	}
	if sv.AmountWeight <= 0 {
		sv.AmountWeight = Weight(sv.Size) * sv.Product.ServingWeight
	}
	if sv.AmountVolume <= 0 {
		sv.AmountVolume = Volume(sv.Size) * sv.Product.ServingVolume
	}
	if sv.AmountWeight <= 0 && sv.AmountVolume <= 0 {
		s.ReplyBadRequest(msg, errors.New("No serving amount specified."))
		return
	}
	if sv.Time.IsZero() {
		sv.Time = time.Now()
	}

	if err := s.DB.Save(&sv).Error; err != nil {
		s.ReplyInternalError(msg, err)
		return
	}

	s.Reply(msg, sarif.CreateMessage("meal/serving/recorded", &sv))
}
Beispiel #27
0
func (s *Service) handleMockAttachments(msg sarif.Message) {
	s.Log("debug", "received", msg)
	pl := AttachmentPayload{
		Attachments: []schema.Attachment{
			{
				Fallback: "Image of a cat.",
				Title:    "Cat",
				ImageUrl: "data:image/jpeg;base64,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",
			},
		},
	}
	s.Reply(msg, sarif.CreateMessage("mock/attached", pl))
}
Beispiel #28
0
func (s *Service) getConversation(device string) *Conversation {
	cv, ok := s.conversations[device]
	if !ok {
		cv = &Conversation{
			service: s,
			Device:  device,
		}
		s.conversations[device] = cv
		s.Subscribe("", s.DeviceId+"/"+device, s.handleNetworkMessage)
		cv.PublishForClient(sarif.CreateMessage("natural/client/new", nil))
	}
	return cv
}
Beispiel #29
0
func (s *Service) handleLocationUpdate(msg sarif.Message) {
	loc := Location{}
	if err := msg.DecodePayload(&loc); err != nil {
		s.ReplyBadRequest(msg, err)
		return
	}
	if loc.Time.IsZero() {
		loc.Time = time.Now()
	}
	loc.Geohash = EncodeGeohash(loc.Latitude, loc.Longitude, 12)
	s.Log("debug", "store update", loc)

	var last []Location
	err := s.Store.Scan("locations", store.Scan{
		Reverse: true,
		Only:    "values",
		Limit:   1,
	}, &last)
	if err != nil {
		s.Log("err/internal", "retrieve last err: "+err.Error())
	}
	if len(last) > 0 {
		loc.Distance = HaversineDistance(last[0], loc)
		loc.Speed = loc.Distance / loc.Time.Sub(last[0].Time).Seconds()
	}

	if _, err := s.Store.Put(loc.Key(), &loc); err != nil {
		s.ReplyInternalError(msg, err)
	}

	if changed := s.Clusters.Advance(loc); changed {
		c := s.Clusters.Current()
		status := "enter"
		if c.Status != ConfirmedCluster {
			status = "leave"
			c = s.Clusters.LastCompleted()
			s.Clusters.ClearCompleted()
		}

		// TODO: make optional
		if place, err := ReverseGeocode(c.Location); err == nil {
			c.Address = place.Pretty()
		}

		s.Publish(sarif.CreateMessage("location/cluster/"+status, c))
	}

	if len(last) > 0 {
		s.checkGeofences(last[0], loc)
	}
}
Beispiel #30
0
func (s *Service) handleBatch(msg sarif.Message) {
	cmds := make([]BatchCommand, 0)
	if err := msg.DecodePayload(&cmds); err != nil {
		s.ReplyInternalError(msg, err)
		return
	}
	results := make([]interface{}, len(cmds))
	for i, cmd := range cmds {
		collection, key := parseAction("", cmd.Key)
		switch cmd.Type {
		case "put":
			doc, err := s.Store.Put(&Document{
				Collection: collection,
				Key:        key,
				Value:      []byte(cmd.Value),
			})
			if err != nil {
				s.ReplyInternalError(msg, err)
				return
			}
			results[i] = doc
		case "get":
			doc, err := s.Store.Get(collection, key)
			if err != nil {
				s.ReplyInternalError(msg, err)
				return
			}
			results[i] = doc
		case "del":
			if err := s.Store.Del(collection, key); err != nil {
				s.ReplyInternalError(msg, err)
				return
			}
			results[i] = true
		case "scan":
			var p scanMessage
			if err := json.Unmarshal(cmd.Value, &p); err != nil {
				s.ReplyInternalError(msg, err)
				return
			}
			got, err := s.doScan(collection, p)
			if err != nil {
				s.ReplyInternalError(msg, err)
				return
			}
			results[i] = got
		}
	}

	s.Reply(msg, sarif.CreateMessage("store/batched/", results))
}