Example #1
0
File: say.go Project: metcalf/saypi
func (c *Controller) SetMood(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	userID := mustUserID(ctx)
	name := pat.Param(ctx, "mood")

	var mood Mood
	r.ParseForm()
	err := decoder.Decode(&mood, r.PostForm)
	if err != nil {
		respond.InternalError(ctx, w, err)
		return
	}

	mood.Eyes = strings.Replace(mood.Eyes, "\x00", "", -1)
	mood.Tongue = strings.Replace(mood.Tongue, "\x00", "", -1)

	var uerr usererrors.InvalidParams
	if !(mood.Eyes == "" || utf8.RuneCountInString(mood.Eyes) == 2) {
		uerr = append(uerr, usererrors.InvalidParamsEntry{
			Params:  []string{"eyes"},
			Message: "must be a string containing two characters",
		})
	}

	if !(mood.Tongue == "" || utf8.RuneCountInString(mood.Tongue) == 2) {
		uerr = append(uerr, usererrors.InvalidParamsEntry{
			Params:  []string{"tongue"},
			Message: "must be a string containing two characters",
		})
	}

	if uerr != nil {
		respond.UserError(ctx, w, http.StatusBadRequest, uerr)
		return
	}

	mood.Name = name
	mood.UserDefined = true

	err = c.repo.SetMood(userID, &mood)
	if err == errBuiltinMood {
		respond.UserError(ctx, w, http.StatusBadRequest, usererrors.ActionNotAllowed{
			Action: fmt.Sprintf("update built-in mood %s", name),
		})
		return
	} else if err != nil {
		respond.InternalError(ctx, w, err)
		return
	}

	respond.Data(ctx, w, http.StatusOK, mood)
}
Example #2
0
File: say.go Project: metcalf/saypi
func (c *Controller) ListMoods(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	userID := mustUserID(ctx)

	lArgs, uerr := getListArgs(r)
	if uerr != nil {
		respond.UserError(ctx, w, http.StatusBadRequest, uerr)
		return
	}

	moods, hasMore, err := c.repo.ListMoods(userID, lArgs)
	if err == errCursorNotFound {
		respondCursorNotFound(ctx, w, lArgs)
		return
	} else if err != nil {
		respond.InternalError(ctx, w, err)
		return
	}

	respond.Data(ctx, w, http.StatusOK, listRes{
		Cursor:  moods[len(moods)-1].Name,
		Type:    "mood",
		HasMore: hasMore,
		Data:    moods,
	})
}
Example #3
0
func (c *Controller) CreateUser(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	id := make([]byte, idLen)
	if _, err := rand.Read(id); err != nil {
		respond.InternalError(ctx, w, err)
		return
	}

	mac := hmac.New(sha256.New, c.secret)
	if _, err := mac.Write(id); err != nil {
		respond.InternalError(ctx, w, err)
		return
	}

	msg := mac.Sum(id)

	res := struct {
		ID string `json:"id"`
	}{base64.URLEncoding.EncodeToString(msg)}

	respond.Data(ctx, w, http.StatusOK, res)
}
Example #4
0
File: say.go Project: metcalf/saypi
func (c *Controller) DeleteConversation(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	userID := mustUserID(ctx)
	convoID := pat.Param(ctx, "conversation")

	if err := c.repo.DeleteConversation(userID, convoID); err == errRecordNotFound {
		respond.NotFound(ctx, w, r)
	} else if err != nil {
		respond.InternalError(ctx, w, err)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}
Example #5
0
func (c *Controller) GetUser(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	id := pat.Param(ctx, "id")
	if id == "" {
		respond.InternalError(ctx, w, errors.New("GetUser called without an `id` URL Var"))
		return
	}

	if c.getUser(id) != nil {
		w.WriteHeader(204)
	} else {
		respond.NotFound(ctx, w, r)
	}
}
Example #6
0
File: say.go Project: metcalf/saypi
func (c *Controller) GetLine(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	userID := mustUserID(ctx)
	convoID := pat.Param(ctx, "conversation")
	lineID := pat.Param(ctx, "line")

	line, err := c.repo.GetLine(userID, convoID, lineID)
	if err != nil {
		respond.InternalError(ctx, w, err)
		return
	}
	if line == nil {
		respond.NotFound(ctx, w, r)
		return
	}

	line.Output, err = c.renderLine(line)
	if err != nil {
		respond.InternalError(ctx, w, err)
		return
	}

	respond.Data(ctx, w, http.StatusOK, line)
}
Example #7
0
File: say.go Project: metcalf/saypi
func (c *Controller) GetConversation(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	userID := mustUserID(ctx)
	convoID := pat.Param(ctx, "conversation")

	convo, err := c.repo.GetConversation(userID, convoID)
	if err != nil {
		respond.InternalError(ctx, w, err)
		return
	}
	if convo == nil {
		respond.NotFound(ctx, w, r)
		return
	}

	for i, Line := range convo.Lines {
		convo.Lines[i].Output, err = c.renderLine(&Line)
		if err != nil {
			respond.InternalError(ctx, w, err)
			return
		}
	}

	respond.Data(ctx, w, http.StatusOK, convo)
}
Example #8
0
File: say.go Project: metcalf/saypi
func (c *Controller) GetMood(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	userID := mustUserID(ctx)
	name := pat.Param(ctx, "mood")

	res, err := c.repo.GetMood(userID, name)
	if err != nil {
		respond.InternalError(ctx, w, err)
		return
	}
	if res == nil {
		respond.NotFound(ctx, w, r)
		return
	}

	respond.Data(ctx, w, http.StatusOK, res)
}
Example #9
0
File: say.go Project: metcalf/saypi
func (c *Controller) CreateConversation(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	userID := mustUserID(ctx)

	heading := strings.Replace(r.PostFormValue("heading"), "\x00", "", -1)
	if cnt := utf8.RuneCountInString(heading); cnt > maxHeadingLength {
		respond.UserError(ctx, w, http.StatusBadRequest, usererrors.InvalidParams{{
			Params:  []string{"heading"},
			Message: fmt.Sprintf("must be a string of less than %d characters", maxHeadingLength),
		}})
		return
	}

	convo, err := c.repo.NewConversation(userID, heading)
	if err != nil {
		respond.InternalError(ctx, w, err)
		return
	}

	respond.Data(ctx, w, http.StatusOK, convo)
}
Example #10
0
File: say.go Project: metcalf/saypi
func (c *Controller) DeleteMood(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	userID := mustUserID(ctx)
	name := pat.Param(ctx, "mood")

	if err := c.repo.DeleteMood(userID, name); err == errBuiltinMood {
		respond.UserError(ctx, w, http.StatusBadRequest, usererrors.ActionNotAllowed{
			Action: fmt.Sprintf("delete built-in mood %s", name),
		})
	} else if err == errRecordNotFound {
		respond.NotFound(ctx, w, r)
	} else if conflict, ok := err.(conflictErr); ok {
		respond.UserError(ctx, w, http.StatusBadRequest, usererrors.ActionNotAllowed{
			Action: fmt.Sprintf("delete a mood associated with %d conversation lines", len(conflict.IDs)),
		})
	} else if err != nil {
		respond.InternalError(ctx, w, err)
	} else {
		w.WriteHeader(http.StatusNoContent)
	}
}
Example #11
0
File: say.go Project: metcalf/saypi
// TODO: use gorilla schema here
func (c *Controller) CreateLine(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	userID := mustUserID(ctx)
	convoID := pat.Param(ctx, "conversation")

	var uerr usererrors.InvalidParams

	var think bool
	switch r.PostFormValue("think") {
	case "", "false":
		think = false
	case "true":
		think = true
	default:
		uerr = append(uerr, usererrors.InvalidParamsEntry{
			Params:  []string{"think"},
			Message: "must be either 'true' or 'false'",
		})
	}

	animal := r.PostFormValue("animal")
	if animal == "" {
		animal = "default"
	}
	if _, ok := c.cows[animal]; !ok {
		uerr = append(uerr, usererrors.InvalidParamsEntry{
			Params:  []string{"animal"},
			Message: fmt.Sprintf("%q does not exist", animal),
		})
	}

	text := strings.Replace(r.PostFormValue("text"), "\x00", "", -1)
	if cnt := utf8.RuneCountInString(text); cnt > maxTextLength {
		respond.UserError(ctx, w, http.StatusBadRequest, usererrors.InvalidParams{{
			Params:  []string{"text"},
			Message: fmt.Sprintf("must be a string of less than %d characters", maxTextLength),
		}})
		return
	}

	moodName := strings.Replace(r.PostFormValue("mood"), "\x00", "", -1)
	if moodName == "" {
		moodName = "default"
	}

	mood, err := c.repo.GetMood(userID, moodName)
	if err != nil {
		respond.InternalError(ctx, w, err)
		return
	}
	if mood == nil {
		uerr = append(uerr, usererrors.InvalidParamsEntry{
			Params:  []string{"mood"},
			Message: fmt.Sprintf("%q does not exist", moodName),
		})
	}

	if uerr != nil {
		respond.UserError(ctx, w, http.StatusBadRequest, uerr)
		return
	}

	line := Line{
		Animal:   animal,
		Think:    think,
		MoodName: moodName,
		Text:     text,
		mood:     mood,
	}

	if err := c.repo.InsertLine(userID, convoID, &line); err == sql.ErrNoRows {
		// The underlying conversation does not exist
		respond.NotFound(ctx, w, r)
	} else if err != nil {
		respond.InternalError(ctx, w, err)
		return
	}

	line.Output, err = c.renderLine(&line)
	if err != nil {
		respond.InternalError(ctx, w, err)
		return
	}

	respond.Data(ctx, w, http.StatusOK, line)
}