transition the polling api to the jsonapi standard

fixes CNVS-13271
this commit changes the formatting of the JSON returned by the polling
endpoints to conform to the adopted jsonapi standard. It also adds a
'created_at' field to the poll and poll_session endpoints

Test plan
- All polling endpoints should adhere to the jsonapi standard (nested
  under a collection, e.g. all returned JSON items will be contained under
  a pluralized root, polls will be under a 'polls' key, poll choices
  will be under a 'poll_choices' key, even for a single resource)

- There are no 'meta' or 'links' attributes to worry about yet

- The poll and poll_session endpoints should return a 'created_at'
  field.  This is viewable by both students and teachers

Change-Id: Ie0fd49a2c699fba5d8257ee1e8dbb062c81662cb
Reviewed-on: https://gerrit.instructure.com/35388
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Ahmad Amireh <ahmad@instructure.com>
QA-Review: Caleb Guanzon <cguanzon@instructure.com>
Product-Review: Josh Simpson <jsimpson@instructure.com>
This commit is contained in:
Josh Simpson 2014-05-23 09:58:22 -06:00
parent feb9644b0f
commit 705fdb7bef
12 changed files with 253 additions and 143 deletions

View File

@ -59,13 +59,17 @@ module Polling
#
# Returns the list of PollChoices in this poll.
#
# @returns [PollChoice]
# @example_response
# {
# "poll_choices": [PollChoice]
# }
#
def index
if authorized_action(@poll, @current_user, :read)
@poll_choices = @poll.poll_choices
@poll_choices = Api.paginate(@poll_choices, self, api_v1_poll_choices_url(@poll))
render json: serialize_json(@poll_choices)
render json: serialize_jsonapi(@poll_choices)
end
end
@ -74,12 +78,16 @@ module Polling
#
# Returns the poll choice with the given id
#
# @returns PollChoice
# @example_response
# {
# "poll_choices": [PollChoice]
# }
#
def show
@poll_choice = @poll.poll_choices.find(params[:id])
if authorized_action(@poll, @current_user, :read)
render json: serialize_json(@poll_choice, true)
render json: serialize_jsonapi(@poll_choice)
end
end
@ -88,20 +96,25 @@ module Polling
#
# Create a new poll choice for this poll
#
# @argument poll_choice[text] [Required, String]
# @argument poll_choices[][text] [Required, String]
# The descriptive text of the poll choice.
#
# @argument poll_choice[is_correct] [Optional, Boolean]
# @argument poll_choices[][is_correct] [Optional, Boolean]
# Whether this poll choice is considered correct or not. Defaults to false.
#
# @returns PollChoice
# @example_response
# {
# "poll_choices": [PollChoice]
# }
#
def create
@poll_choice = @poll.poll_choices.new(params[:poll_choice])
@poll_choice.is_correct = false if params[:poll_choice] && params[:poll_choice][:is_correct].blank?
poll_choice_params = params[:poll_choices][0]
@poll_choice = @poll.poll_choices.new(poll_choice_params)
@poll_choice.is_correct = false if poll_choice_params && poll_choice_params[:is_correct].blank?
if authorized_action(@poll, @current_user, :update)
if @poll_choice.save
render json: serialize_json(@poll_choice)
render json: serialize_jsonapi(@poll_choice)
else
render json: @poll_choice.errors, status: :bad_request
end
@ -113,23 +126,28 @@ module Polling
#
# Update an existing poll choice for this poll
#
# @argument poll_choice[text] [Required, String]
# @argument poll_choices[][text] [Required, String]
# The descriptive text of the poll choice.
#
# @argument poll_choice[is_correct] [Optional, Boolean]
# @argument poll_choices[][is_correct] [Optional, Boolean]
# Whether this poll choice is considered correct or not. Defaults to false.
#
# @returns Poll
# @example_response
# {
# "poll_choices": [PollChoice]
# }
#
def update
poll_choice_params = params[:poll_choices][0]
@poll_choice = @poll.poll_choices.find(params[:id])
if params[:poll_choice] && params[:poll_choice][:is_correct].blank?
params[:poll_choice][:is_correct] = @poll_choice.is_correct
if poll_choice_params && poll_choice_params[:is_correct].blank?
poll_choice_params[:is_correct] = @poll_choice.is_correct
end
if authorized_action(@poll, @current_user, :update)
if @poll_choice.update_attributes(params[:poll_choice])
render json: serialize_json(@poll_choice)
if @poll_choice.update_attributes(poll_choice_params)
render json: serialize_jsonapi(@poll_choice)
else
render json: @poll_choice.errors, status: :bad_request
end
@ -150,18 +168,18 @@ module Polling
end
protected
def serialize_json(poll_choices, single=false)
poll_choices = Array(poll_choices)
def serialize_jsonapi(poll_choices)
poll_choices = Array.wrap(poll_choices)
serialized_set = poll_choices.map do |poll_choice|
Polling::PollChoiceSerializer.new(poll_choice, {
controller: self,
root: false,
include_root: false,
scope: @current_user
}).as_json
end
single ? serialized_set.first : serialized_set
serialized_set = Canvas::APIArraySerializer.new(poll_choices, {
each_serializer: Polling::PollChoiceSerializer,
controller: self,
root: false,
scope: @current_user,
include_root: false
}).as_json
{ poll_choices: serialized_set }
end
end

View File

@ -54,6 +54,12 @@ module Polling
# "description": "Specifies whether the results are viewable by students.",
# "example": "true",
# "type": "boolean"
# },
# "created_at": {
# "description": "The time at which the poll session was created.",
# "example": "2014-01-07T15:16:18Z",
# "type": "string",
# "format": "date-time"
# }
# }
# }
@ -69,13 +75,17 @@ module Polling
#
# Returns the list of PollSessions in this poll.
#
# @returns [PollSession]
# @example_response
# {
# "poll_sessions": [PollSession]
# }
#
def index
if authorized_action(@poll, @current_user, :update)
@poll_sessions = @poll.poll_sessions
@poll_sessions = Api.paginate(@poll_sessions, self, api_v1_poll_sessions_url(@poll))
render json: serialize_json(@poll_sessions)
render json: serialize_jsonapi(@poll_sessions)
end
end
@ -84,12 +94,16 @@ module Polling
#
# Returns the poll session with the given id
#
# @returns PollSession
# @example_response
# {
# "poll_sessions": [PollSession]
# }
#
def show
@poll_session = @poll.poll_sessions.find(params[:id])
if authorized_action(@poll_session, @current_user, :read)
render json: serialize_json(@poll_session, true)
render json: serialize_jsonapi(@poll_session)
end
end
@ -98,29 +112,35 @@ module Polling
#
# Create a new poll session for this poll
#
# @argument poll_session[course_id] [Required, Integer]
# @argument poll_sessions[][course_id] [Required, Integer]
# The id of the course this session is associated with.
#
# @argument poll_session[course_section_id] [Required, Integer]
# @argument poll_sessions[][course_section_id] [Required, Integer]
# The id of the course section this session is associated with.
#
# @argument poll_session[has_public_results] [Optional, Boolean]
# @argument poll_sessions[][has_public_results] [Optional, Boolean]
# Whether or not results are viewable by students.
#
# @returns PollSession
# @example_response
# {
# "poll_sessions": [PollSession]
# }
#
def create
if params[:poll_session] && course_id = params[:poll_session].delete(:course_id)
poll_session_params = params[:poll_sessions][0]
if course_id = poll_session_params.delete(:course_id)
@course = Course.find(course_id)
end
raise ActiveRecord::RecordNotFound.new(I18n.t("polling.poll_sessions.errors.course_required", "Course is required.")) unless @course
@poll_session = @poll.poll_sessions.new(params[:poll_session])
@poll_session = @poll.poll_sessions.new(poll_session_params)
@poll_session.course = @course
if authorized_action(@poll, @current_user, :create) && authorized_action(@course, @current_user, :update)
if @poll_session.save
render json: serialize_json(@poll_session)
render json: serialize_jsonapi(@poll_session)
else
render json: @poll_session.errors, status: :bad_request
end
@ -132,22 +152,26 @@ module Polling
#
# Update an existing poll session for this poll
#
# @argument poll_session[course_id] [Required, Integer]
# @argument poll_sessions[][course_id] [Required, Integer]
# The id of the course this session is associated with.
#
# @argument poll_session[course_section_id] [Required, Integer]
# @argument poll_sessions[][course_section_id] [Required, Integer]
# The id of the course section this session is associated with.
#
# @argument poll_session[has_public_results] [Optional, Boolean]
# @argument poll_sessions[][has_public_results] [Optional, Boolean]
# Whether or not results are viewable by students.
#
# @returns PollSession
# @example_response
# {
# "poll_sessions": [PollSession]
# }
#
def update
@poll_session = @poll.poll_sessions.find(params[:id])
poll_session_params = params[:poll_sessions][0]
if authorized_action(@poll, @current_user, :update)
if @poll_session.update_attributes(params[:poll_session])
render json: serialize_json(@poll_session)
if @poll_session.update_attributes(poll_session_params)
render json: serialize_jsonapi(@poll_session)
else
render json: @poll_session.errors, status: :bad_request
end
@ -170,42 +194,50 @@ module Polling
# @API Publish a poll session
# @beta
#
# @returns PollSession
# @example_response
# {
# "poll_sessions": [PollSession]
# }
#
def publish
@poll_session = @poll.poll_sessions.find(params[:id])
if authorized_action(@poll_session, @current_user, :publish)
@poll_session.publish!
render json: serialize_json(@poll_session)
render json: serialize_jsonapi(@poll_session)
end
end
# @API Close a published poll session
# @beta
#
# @returns PollSession
# @example_response
# {
# "poll_sessions": [PollSession]
# }
#
def close
@poll_session = @poll.poll_sessions.find(params[:id])
if authorized_action(@poll_session, @current_user, :publish)
@poll_session.close!
render json: serialize_json(@poll_session)
render json: serialize_jsonapi(@poll_session)
end
end
protected
def serialize_json(poll_sessions, single=false)
poll_sessions = Array(poll_sessions)
def serialize_jsonapi(poll_sessions)
poll_sessions = Array.wrap(poll_sessions)
serialized_set = poll_sessions.map do |poll_session|
Polling::PollSessionSerializer.new(poll_session, {
controller: self,
root: false,
include_root: false,
scope: @current_user
}).as_json
end
single ? serialized_set.first : serialized_set
serialized_set = Canvas::APIArraySerializer.new(poll_sessions, {
each_serializer: Polling::PollSessionSerializer,
controller: self,
root: false,
scope: @current_user,
include_root: false
}).as_json
{ poll_sessions: serialized_set }
end
end

View File

@ -30,12 +30,14 @@ module Polling
# "example": 1023,
# "type": "integer"
# },
# "poll_choice": {
# "description": "The chosen poll choice for this submission. See the Poll Choice API for details.",
# "$ref": "PollChoice"
# "poll_choice_id": {
# "description": "The id of the chosen poll choice for this submission.",
# "example": 55,
# "type": "integer"
# }
# }
# }
#
class PollSubmissionsController < ApplicationController
include Filters::Polling
@ -48,11 +50,15 @@ module Polling
#
# Returns the poll submission with the given id
#
# @returns PollSubmission
# @example_response
# {
# "poll_submissions": [PollSubmission]
# }
#
def show
@poll_submission = @poll_session.poll_submissions.find(params[:id])
if authorized_action(@poll_submission, @current_user, :read)
render json: serialize_json(@poll_submission, true)
render json: serialize_jsonapi(@poll_submission)
end
end
@ -61,19 +67,24 @@ module Polling
#
# Create a new poll submission for this poll session
#
# @argument poll_submission[poll_choice_id] [Required, Integer]
# @argument poll_submissions[][poll_choice_id] [Required, Integer]
# The chosen poll choice for this submission.
#
# @returns PollSubmission
# @example_response
# {
# "poll_submissions": [PollSubmission]
# }
#
def create
poll_submission_params = params[:poll_submissions][0]
@poll_submission = @poll_session.poll_submissions.new
@poll_submission.poll = @poll
@poll_submission.user = @current_user
@poll_submission.poll_choice = @poll.poll_choices.find(params[:poll_submission][:poll_choice_id])
@poll_submission.poll_choice = @poll.poll_choices.find(poll_submission_params[:poll_choice_id])
if authorized_action(@poll_submission, @current_user, :submit)
if @poll_submission.save
render json: serialize_json(@poll_submission)
render json: serialize_jsonapi(@poll_submission)
else
render json: @poll_submission.errors, status: :bad_request
end
@ -81,18 +92,18 @@ module Polling
end
protected
def serialize_json(poll_submissions, single=false)
poll_submissions = Array(poll_submissions)
def serialize_jsonapi(poll_submissions)
poll_submissions = Array.wrap(poll_submissions)
serialized_set = poll_submissions.map do |poll_submission|
Polling::PollSubmissionSerializer.new(poll_submission, {
controller: self,
root: false,
include_root: false,
scope: @current_user
}).as_json
end
single ? serialized_set.first : serialized_set
serialized_set = Canvas::APIArraySerializer.new(poll_submissions, {
each_serializer: Polling::PollSubmissionSerializer,
controller: self,
root: false,
scope: @current_user,
include_root: false
}).as_json
{ poll_submissions: serialized_set }
end
end

View File

@ -39,6 +39,12 @@ module Polling
# "description": "A short description of the poll.",
# "type": "string",
# "example": "This poll is to determine what priorities the students in the course have."
# },
# "created_at": {
# "description": "The time at which the poll was created.",
# "example": "2014-01-07T15:16:18Z",
# "type": "string",
# "format": "date-time"
# }
# }
# }
@ -53,12 +59,16 @@ module Polling
#
# Returns the list of polls for the current user.
#
# @returns [Poll]
# @example_response
# {
# "polls": [Poll]
# }
#
def index
@polls = @current_user.polls
@polls = Api.paginate(@polls, self, api_v1_polls_url)
render json: serialize_json(@polls)
render json: serialize_jsonapi(@polls)
end
# @API Get a single poll
@ -66,12 +76,16 @@ module Polling
#
# Returns the poll with the given id
#
# @returns Poll
# @example_response
# {
# "polls": [Poll]
# }
#
def show
@poll = Polling::Poll.find(params[:id])
if authorized_action(@poll, @current_user, :read)
render json: serialize_json(@poll, true)
render json: serialize_jsonapi(@poll)
end
end
@ -80,18 +94,23 @@ module Polling
#
# Create a new poll for the current user
#
# @argument poll[title] [Required, String]
# @argument polls[][title] [Required, String]
# The title of the poll.
#
# @argument poll[description] [Optional, String]
# @argument polls[][description] [Optional, String]
# A brief description or instructions for the poll.
#
# @returns Poll
# @example_response
# {
# "polls": [Poll]
# }
#
def create
@poll = @current_user.polls.new(params[:poll])
poll_params = params[:polls][0]
@poll = @current_user.polls.new(poll_params)
if authorized_action(@poll, @current_user, :create)
if @poll.save
render json: serialize_json(@poll)
render json: serialize_jsonapi(@poll)
else
render json: @poll.errors, status: :bad_request
end
@ -103,21 +122,26 @@ module Polling
#
# Update an existing poll belonging to the current user
#
# @argument poll[title] [Required, String]
# @argument polls[][title] [Required, String]
# The title of the poll.
#
# @argument poll[description] [Optional, String]
# @argument polls[][description] [Optional, String]
# A brief description or instructions for the poll.
#
# @returns Poll
# @example_response
# {
# "polls": [Poll]
# }
#
def update
@poll = Polling::Poll.find(params[:id])
poll_params = params[:polls][0]
if authorized_action(@poll, @current_user, :update)
params[:poll].delete(:is_correct) if params[:poll] && params[:poll][:is_correct].blank?
poll_params.delete(:is_correct) if poll_params && poll_params[:is_correct].blank?
if @poll.update_attributes(params[:poll])
render json: serialize_json(@poll)
if @poll.update_attributes(poll_params)
render json: serialize_jsonapi(@poll)
else
render json: @poll.errors, status: :bad_request
end
@ -137,19 +161,18 @@ module Polling
end
protected
def serialize_json(polls, single=false)
polls = Array(polls)
def serialize_jsonapi(polls)
polls = Array.wrap(polls)
serialized_set = polls.map do |poll|
Polling::PollSerializer.new(poll, {
controller: self,
root: false,
include_root: false,
scope: @current_user
}).as_json
end
serialized_set = Canvas::APIArraySerializer.new(polls, {
each_serializer: Polling::PollSerializer,
controller: self,
root: false,
scope: @current_user,
include_root: false
}).as_json
single ? serialized_set.first : serialized_set
{ polls: serialized_set }
end
end

View File

@ -1,5 +1,7 @@
module Polling
class PollChoiceSerializer < Canvas::APISerializer
root :poll_choice
attributes :id, :text, :is_correct
has_one :poll, embed: :id

View File

@ -1,6 +1,8 @@
module Polling
class PollSerializer < Canvas::APISerializer
attributes :id, :question, :description, :total_results
root :poll
attributes :id, :question, :description, :total_results, :created_at
has_many :poll_choices, embed: :ids
@ -26,7 +28,7 @@ module Polling
end
def student_keys
[:id, :question, :description]
[:id, :question, :description, :created_at]
end
end
end

View File

@ -1,6 +1,8 @@
module Polling
class PollSessionSerializer < Canvas::APISerializer
attributes :id, :is_published, :has_public_results, :results, :course_id, :course_section_id
root :poll_session
attributes :id, :is_published, :has_public_results, :results, :course_id, :course_section_id, :created_at
def_delegators :object, :results, :poll
@ -19,7 +21,7 @@ module Polling
end
def student_keys
[:id, :is_published, :course_id, :course_section_id]
[:id, :is_published, :course_id, :course_section_id, :created_at]
end
end
end

View File

@ -1,5 +1,7 @@
module Polling
class PollSubmissionSerializer < Canvas::APISerializer
root :poll_submission
attributes :id, :poll_choice_id
end
end

View File

@ -42,9 +42,10 @@ describe Polling::PollChoicesController, type: :request do
it "returns all existing poll choices" do
json = get_index
json.size.should == 5
poll_choices_json = json['poll_choices']
poll_choices_json.size.should == 5
json.each_with_index do |pc, i|
poll_choices_json.each_with_index do |pc, i|
pc['text'].should == "Poll Choice #{5-i}"
end
end
@ -64,7 +65,9 @@ describe Polling::PollChoicesController, type: :request do
session.publish!
json = get_index
json.each do |poll_choice|
poll_choices_json = json['poll_choices']
poll_choices_json.each do |poll_choice|
poll_choice.should_not have_key('is_correct')
end
end
@ -89,8 +92,10 @@ describe Polling::PollChoicesController, type: :request do
it "retrieves the poll specified" do
json = get_show
json['text'].should == 'A Poll Choice'
json['is_correct'].should be_true
poll_choice_json = json['poll_choices'].first
poll_choice_json['text'].should == 'A Poll Choice'
poll_choice_json['is_correct'].should be_true
end
context "as a student" do
@ -108,7 +113,9 @@ describe Polling::PollChoicesController, type: :request do
session.publish!
json = get_show
json.should_not have_key('is_correct')
poll_choice_json = json['poll_choices'].first
poll_choice_json.should_not have_key('is_correct')
end
end
end
@ -125,7 +132,7 @@ describe Polling::PollChoicesController, type: :request do
{ controller: 'polling/poll_choices', action: 'create', format: 'json',
poll_id: @poll.id.to_s
},
{ poll_choice: params }, {}, {})
{ poll_choices: [params] }, {}, {})
end
context "as a teacher" do
@ -165,8 +172,7 @@ describe Polling::PollChoicesController, type: :request do
poll_id: @poll.id.to_s,
id: @poll_choice.id.to_s
},
{ poll_choice: params }, {}, {})
{ poll_choices: [params] }, {}, {})
end
context "as a teacher" do

View File

@ -43,10 +43,11 @@ describe Polling::PollSessionsController, type: :request do
it "returns all existing poll sessions" do
json = get_index
poll_sessions_json = json['poll_sessions']
session_ids = @poll.poll_sessions.map(&:id)
json.size.should == 3
poll_sessions_json.size.should == 3
json.each_with_index do |session, i|
poll_sessions_json.each_with_index do |session, i|
session_ids.should include(session['id'].to_i)
session['is_published'].should be_false
end
@ -85,16 +86,19 @@ describe Polling::PollSessionsController, type: :request do
it "retrieves the poll session specified" do
json = get_show
json['id'].should == @poll_session.id.to_s
json['is_published'].should be_true
poll_session_json = json['poll_sessions'].first
poll_session_json['id'].should == @poll_session.id.to_s
poll_session_json['is_published'].should be_true
end
context "as a teacher" do
it "retrieves the poll session specified even if closed" do
@poll_session.close!
json = get_show
json['id'].should == @poll_session.id.to_s
json['is_published'].should be_false
poll_json = json['poll_sessions'].first
poll_json['id'].should == @poll_session.id.to_s
poll_json['is_published'].should be_false
end
it "shows the results of a current poll session" do
@ -106,10 +110,11 @@ describe Polling::PollSessionsController, type: :request do
@user = @teacher
json = get_show
poll_session_json = json['poll_sessions'].first
json.should have_key('results')
json['results'][choice1.id.to_s].should == 3
json['results'][choice2.id.to_s].should == 2
poll_session_json.should have_key('results')
poll_session_json['results'][choice1.id.to_s].should == 3
poll_session_json['results'][choice2.id.to_s].should == 2
end
end
@ -126,8 +131,9 @@ describe Polling::PollSessionsController, type: :request do
@user = student
json = get_show
poll_session_json = json['poll_sessions'].first
json.should_not have_key('results')
poll_session_json.should_not have_key('results')
end
end
@ -145,8 +151,9 @@ describe Polling::PollSessionsController, type: :request do
@user = student
json = get_show
poll_session_json = json['poll_sessions'].first
json.should have_key('results')
poll_session_json.should have_key('results')
end
end
end
@ -164,7 +171,7 @@ describe Polling::PollSessionsController, type: :request do
{ controller: 'polling/poll_sessions', action: 'create', format: 'json',
poll_id: @poll.id.to_s
},
{ poll_session: params }, {}, {})
{ poll_sessions: [params] }, {}, {})
end
context "as a teacher" do
@ -192,7 +199,7 @@ describe Polling::PollSessionsController, type: :request do
poll_id: @poll.id.to_s,
id: @poll_session.id.to_s
},
{ poll_session: params }, {}, {})
{ poll_sessions: [params] }, {}, {})
end
context "as a teacher" do

View File

@ -62,8 +62,9 @@ describe Polling::PollSubmissionsController, type: :request do
it "retrieves the poll submission specified" do
user_session(@student)
json = get_show
json['id'].should == @submission.id.to_s
json['poll_choice_id'].should == @selected.id.to_s
poll_submission_json = json['poll_submissions'].first
poll_submission_json['id'].should == @submission.id.to_s
poll_submission_json['poll_choice_id'].should == @selected.id.to_s
end
end
@ -80,7 +81,7 @@ describe Polling::PollSubmissionsController, type: :request do
poll_id: @poll.id.to_s,
poll_session_id: @session.id.to_s
},
{ poll_submission: params }, {}, {})
{ poll_submissions: [params] }, {}, {})
end
context "as a student" do

View File

@ -40,9 +40,10 @@ describe Polling::PollsController, type: :request do
it "returns all existing polls" do
json = get_index
json.size.should == 5
poll_json = json['polls']
poll_json.size.should == 5
json.each_with_index do |poll, i|
poll_json.each_with_index do |poll, i|
poll['question'].should == "Example Poll #{5-i}"
end
end
@ -69,13 +70,15 @@ describe Polling::PollsController, type: :request do
it "retrieves the poll specified" do
json = get_show
json['question'].should == 'An Example Poll'
poll_json = json['polls'].first
poll_json['question'].should == 'An Example Poll'
end
context "as a teacher" do
it "displays the total results of all sessions" do
json = get_show
json.should have_key("total_results")
poll_json = json['polls'].first
poll_json.should have_key("total_results")
end
end
@ -87,7 +90,8 @@ describe Polling::PollsController, type: :request do
session.publish!
json = get_show
json.should_not have_key("total_results")
poll_json = json['polls'].first
poll_json.should_not have_key("total_results")
end
it "is unauthorized if there are no published sessions" do
@ -109,7 +113,7 @@ describe Polling::PollsController, type: :request do
helper.call(:post,
"/api/v1/polls",
{ controller: 'polling/polls', action: 'create', format: 'json' },
{ poll: params }, {}, {})
{ polls: [params] }, {}, {})
end
context "as a teacher" do
@ -142,7 +146,7 @@ describe Polling::PollsController, type: :request do
"/api/v1/polls/#{@poll.id}",
{ controller: 'polling/polls', action: 'update', format: 'json',
id: @poll.id.to_s },
{ poll: params }, {}, {})
{ polls: [params] }, {}, {})
end