Skip to content

Commit 7711ad5

Browse files
committed
added REST API endpoints for Signal Polls
see #765
1 parent e5e2151 commit 7711ad5

File tree

6 files changed

+1374
-1
lines changed

6 files changed

+1374
-1
lines changed

src/api/api.go

Lines changed: 203 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,30 @@ type DeleteLocalAccountDataRequest struct {
225225
}
226226

227227
type DeviceLinkUriResponse struct {
228-
DeviceLinkUri string `json:"device_link_uri"`
228+
DeviceLinkUri string `json:"device_link_uri"`
229+
}
230+
231+
type CreatePollRequest struct {
232+
Recipient string `json:"recipient" example:"<phone number> OR <username> OR <group id>"`
233+
Question string `json:"question" example:"What's your favourite fruit?"`
234+
Answers []string `json:"answers" example:"apple,banana,orange"`
235+
AllowMultipleSelections *bool `json:"allow_multiple_selections" example:"true"`
236+
}
237+
238+
type CreatePollResponse struct {
239+
Timestamp string `json:"timestamp" example:"1769271479"`
240+
}
241+
242+
type VoteRequest struct {
243+
Recipient string `json:"recipient" example:"<phone number> OR <username> OR <group id>"`
244+
PollAuthor string `json:"poll_author" example:"<phone number> OR <uuid>"`
245+
PollTimestamp string `json:"poll_timestamp" example:"1769271479"`
246+
SelectedAnswers []int32 `json:"selected_answers" example:"1"`
247+
}
248+
249+
type ClosePollRequest struct {
250+
Recipient string `json:"recipient" example:"<phone number> OR <username> OR <group id>"`
251+
PollTimestamp string `json:"poll_timestamp" example:"1769271479"`
229252
}
230253

231254
type Api struct {
@@ -2591,3 +2614,182 @@ func (a *Api) RemoteDelete(c *gin.Context) {
25912614
}
25922615
c.JSON(201, RemoteDeleteResponse{Timestamp: strconv.FormatInt(timestamp.Timestamp, 10)})
25932616
}
2617+
2618+
// @Summary Create a new poll.
2619+
// @Tags Polls
2620+
// @Description Create a new poll
2621+
// @Accept json
2622+
// @Produce json
2623+
// @Success 201 {object} CreatePollResponse
2624+
// @Failure 400 {object} Error
2625+
// @Param number path string true "Registered Phone Number"
2626+
// @Param data body CreatePollRequest true "Type"
2627+
// @Router /v1/polls/{number} [post]
2628+
func (a *Api) CreatePoll(c *gin.Context) {
2629+
var req CreatePollRequest
2630+
err := c.BindJSON(&req)
2631+
if err != nil {
2632+
c.JSON(400, Error{Msg: "Couldn't process request - invalid request"})
2633+
return
2634+
}
2635+
2636+
number, err := url.PathUnescape(c.Param("number"))
2637+
if err != nil {
2638+
c.JSON(400, Error{Msg: "Couldn't process request - malformed number"})
2639+
return
2640+
}
2641+
if number == "" {
2642+
c.JSON(400, Error{Msg: "Couldn't process request - number missing"})
2643+
return
2644+
}
2645+
2646+
if req.Recipient == "" {
2647+
c.JSON(400, Error{Msg: "Couldn't process request - recipient missing"})
2648+
return
2649+
}
2650+
2651+
if req.Question == "" {
2652+
c.JSON(400, Error{Msg: "Couldn't process request - question missing"})
2653+
return
2654+
}
2655+
2656+
if len(req.Answers) == 0 {
2657+
c.JSON(400, Error{Msg: "Couldn't process request - answers missing"})
2658+
return
2659+
}
2660+
2661+
allowMultipleSelections := true
2662+
if req.AllowMultipleSelections != nil {
2663+
allowMultipleSelections = *req.AllowMultipleSelections
2664+
}
2665+
2666+
timestamp, err := a.signalClient.CreatePoll(number, req.Recipient, req.Question, req.Answers, allowMultipleSelections)
2667+
if err != nil {
2668+
c.JSON(400, Error{Msg: err.Error()})
2669+
return
2670+
}
2671+
c.JSON(201, CreatePollResponse{Timestamp: timestamp})
2672+
}
2673+
2674+
// @Summary Answer a poll.
2675+
// @Tags Polls
2676+
// @Description Answer a poll
2677+
// @Accept json
2678+
// @Produce json
2679+
// @Success 204
2680+
// @Failure 400 {object} Error
2681+
// @Param number path string true "Registered Phone Number"
2682+
// @Param data body VoteRequest true "Type"
2683+
// @Router /v1/polls/{number}/vote [post]
2684+
func (a *Api) VoteInPoll(c *gin.Context) {
2685+
var req VoteRequest
2686+
err := c.BindJSON(&req)
2687+
if err != nil {
2688+
c.JSON(400, Error{Msg: "Couldn't process request - invalid request"})
2689+
return
2690+
}
2691+
2692+
number, err := url.PathUnescape(c.Param("number"))
2693+
if err != nil {
2694+
c.JSON(400, Error{Msg: "Couldn't process request - malformed number"})
2695+
return
2696+
}
2697+
if number == "" {
2698+
c.JSON(400, Error{Msg: "Couldn't process request - number missing"})
2699+
return
2700+
}
2701+
2702+
if req.PollTimestamp == "" {
2703+
c.JSON(400, Error{Msg: "Couldn't process request - poll_timestamp missing"})
2704+
return
2705+
}
2706+
2707+
if req.PollAuthor == "" {
2708+
c.JSON(400, Error{Msg: "Couldn't process request - poll_author missing"})
2709+
return
2710+
}
2711+
2712+
if len(req.SelectedAnswers) == 0 {
2713+
c.JSON(400, Error{Msg: "Couldn't process request - selected_answers missing"})
2714+
return
2715+
}
2716+
2717+
if req.Recipient == "" {
2718+
c.JSON(400, Error{Msg: "Couldn't process request - recipient missing"})
2719+
return
2720+
}
2721+
2722+
pollTimestamp, err := strconv.ParseInt(req.PollTimestamp, 10, 64)
2723+
if err != nil {
2724+
c.JSON(400, Error{Msg: "Couldn't process request - invalid timestamp"})
2725+
return
2726+
}
2727+
2728+
for _, index := range req.SelectedAnswers {
2729+
if index <= 0 {
2730+
c.JSON(400, Error{Msg: "Invalid index in selected_answers specified, index needs to be >= 1!"})
2731+
return
2732+
}
2733+
}
2734+
2735+
err = a.signalClient.VoteInPoll(number, req.Recipient, req.PollAuthor, pollTimestamp, req.SelectedAnswers)
2736+
if err != nil {
2737+
c.JSON(400, Error{Msg: err.Error()})
2738+
return
2739+
}
2740+
2741+
c.Status(204)
2742+
}
2743+
2744+
// @Summary Close a poll.
2745+
// @Tags Polls
2746+
// @Description Close a poll
2747+
// @Accept json
2748+
// @Produce json
2749+
// @Success 204
2750+
// @Failure 400 {object} Error
2751+
// @Param number path string true "Registered Phone Number"
2752+
// @Param data body ClosePollRequest true "Type"
2753+
// @Router /v1/polls/{number} [delete]
2754+
func (a *Api) ClosePoll(c *gin.Context) {
2755+
var req ClosePollRequest
2756+
err := c.BindJSON(&req)
2757+
if err != nil {
2758+
c.JSON(400, Error{Msg: "Couldn't process request - invalid request"})
2759+
return
2760+
}
2761+
2762+
number, err := url.PathUnescape(c.Param("number"))
2763+
if err != nil {
2764+
c.JSON(400, Error{Msg: "Couldn't process request - malformed number"})
2765+
return
2766+
}
2767+
if number == "" {
2768+
c.JSON(400, Error{Msg: "Couldn't process request - number missing"})
2769+
return
2770+
}
2771+
2772+
if req.PollTimestamp == "" {
2773+
c.JSON(400, Error{Msg: "Couldn't process request - poll_timestamp missing"})
2774+
return
2775+
}
2776+
2777+
if req.Recipient == "" {
2778+
c.JSON(400, Error{Msg: "Couldn't process request - recipient missing"})
2779+
return
2780+
}
2781+
2782+
pollTimestamp, err := strconv.ParseInt(req.PollTimestamp, 10, 64)
2783+
if err != nil {
2784+
c.JSON(400, Error{Msg: "Couldn't process request - invalid timestamp"})
2785+
return
2786+
}
2787+
2788+
err = a.signalClient.ClosePoll(number, req.Recipient, pollTimestamp)
2789+
if err != nil {
2790+
c.JSON(400, Error{Msg: err.Error()})
2791+
return
2792+
}
2793+
2794+
c.Status(204)
2795+
}

0 commit comments

Comments
 (0)