diff --git a/lib/survey-gizmo-ruby.rb b/lib/survey-gizmo-ruby.rb index 7bd253d..1c8561f 100644 --- a/lib/survey-gizmo-ruby.rb +++ b/lib/survey-gizmo-ruby.rb @@ -17,4 +17,5 @@ path = File.join(File.expand_path(File.dirname(__FILE__)), 'survey_gizmo') Dir["#{path}/*.rb"].each { |f| require f } -Dir["#{path}/**/*.rb"].each { |f| require f } +Dir["#{path}/v4/*.rb"].each { |f| require f } +Dir["#{path}/v5/*.rb"].each { |f| require f } diff --git a/lib/survey_gizmo/api/api.rb b/lib/survey_gizmo/api/api.rb new file mode 100644 index 0000000..425ad70 --- /dev/null +++ b/lib/survey_gizmo/api/api.rb @@ -0,0 +1,7 @@ +module SurveyGizmo::API + if SurveyGizmo.configuration.v5? + include SurveyGizmo::V5 + else + include SurveyGizmo::V4 + end +end diff --git a/lib/survey_gizmo/configuration.rb b/lib/survey_gizmo/configuration.rb index d6f330e..085cbe0 100644 --- a/lib/survey_gizmo/configuration.rb +++ b/lib/survey_gizmo/configuration.rb @@ -32,6 +32,10 @@ def configure configuration.retriable_params = Configuration::DEFAULT_RETRIABLE_PARAMS.merge(configuration.retriable_params) @global_config = configuration + + path = File.expand_path(File.dirname(__FILE__)) + require File.join(path, 'api/api') + configuration end def reset! @@ -41,7 +45,7 @@ def reset! end class Configuration - DEFAULT_API_VERSION = 'v4' + DEFAULT_API_VERSION = 'v5' DEFAULT_RESULTS_PER_PAGE = 50 DEFAULT_TIMEOUT_SECONDS = 300 DEFAULT_REGION = :us @@ -113,5 +117,9 @@ def region=(region) @api_url = region_infos[:url] @api_time_zone = region_infos[:locale] end + + def v5? + api_version == 'v5' + end end end diff --git a/lib/survey_gizmo/faraday_middleware/parse_survey_gizmo.rb b/lib/survey_gizmo/faraday_middleware/parse_survey_gizmo.rb index ee65c7b..69866e3 100644 --- a/lib/survey_gizmo/faraday_middleware/parse_survey_gizmo.rb +++ b/lib/survey_gizmo/faraday_middleware/parse_survey_gizmo.rb @@ -19,6 +19,9 @@ class ParseSurveyGizmo < FaradayMiddleware::ResponseMiddleware 'datecreated', 'datemodified', 'datesubmitted', + 'date_created', + 'date_modified', + 'date_submitted', 'modified_on' ] diff --git a/lib/survey_gizmo/api/account_teams.rb b/lib/survey_gizmo/v4/account_teams.rb similarity index 94% rename from lib/survey_gizmo/api/account_teams.rb rename to lib/survey_gizmo/v4/account_teams.rb index 32e73c6..d85a483 100644 --- a/lib/survey_gizmo/api/account_teams.rb +++ b/lib/survey_gizmo/v4/account_teams.rb @@ -1,7 +1,7 @@ # This REST endpoint is only available to accounts with admin privileges # This code is untested. -module SurveyGizmo::API +module SurveyGizmo::V4 class AccountTeams include SurveyGizmo::Resource diff --git a/lib/survey_gizmo/api/answer.rb b/lib/survey_gizmo/v4/answer.rb similarity index 98% rename from lib/survey_gizmo/api/answer.rb rename to lib/survey_gizmo/v4/answer.rb index ed52e13..ad25f64 100644 --- a/lib/survey_gizmo/api/answer.rb +++ b/lib/survey_gizmo/v4/answer.rb @@ -1,4 +1,4 @@ -module SurveyGizmo::API +module SurveyGizmo::V4 class Answer include Virtus.model diff --git a/lib/survey_gizmo/api/campaign.rb b/lib/survey_gizmo/v4/campaign.rb similarity index 97% rename from lib/survey_gizmo/api/campaign.rb rename to lib/survey_gizmo/v4/campaign.rb index b54072e..81a6d09 100644 --- a/lib/survey_gizmo/api/campaign.rb +++ b/lib/survey_gizmo/v4/campaign.rb @@ -1,4 +1,4 @@ -module SurveyGizmo::API +module SurveyGizmo::V4 class Campaign include SurveyGizmo::Resource diff --git a/lib/survey_gizmo/api/contact.rb b/lib/survey_gizmo/v4/contact.rb similarity index 98% rename from lib/survey_gizmo/api/contact.rb rename to lib/survey_gizmo/v4/contact.rb index 977c290..87d6c0a 100644 --- a/lib/survey_gizmo/api/contact.rb +++ b/lib/survey_gizmo/v4/contact.rb @@ -1,4 +1,4 @@ -module SurveyGizmo::API +module SurveyGizmo::V4 class Contact include SurveyGizmo::Resource diff --git a/lib/survey_gizmo/api/email_message.rb b/lib/survey_gizmo/v4/email_message.rb similarity index 97% rename from lib/survey_gizmo/api/email_message.rb rename to lib/survey_gizmo/v4/email_message.rb index 27cb17c..424ee32 100644 --- a/lib/survey_gizmo/api/email_message.rb +++ b/lib/survey_gizmo/v4/email_message.rb @@ -1,4 +1,4 @@ -module SurveyGizmo::API +module SurveyGizmo::V4 class EmailMessage include SurveyGizmo::Resource diff --git a/lib/survey_gizmo/api/option.rb b/lib/survey_gizmo/v4/option.rb similarity index 94% rename from lib/survey_gizmo/api/option.rb rename to lib/survey_gizmo/v4/option.rb index 6d270fb..89aca8e 100644 --- a/lib/survey_gizmo/api/option.rb +++ b/lib/survey_gizmo/v4/option.rb @@ -1,4 +1,4 @@ -module SurveyGizmo::API +module SurveyGizmo::V4 class Option include SurveyGizmo::Resource include SurveyGizmo::MultilingualTitle diff --git a/lib/survey_gizmo/api/page.rb b/lib/survey_gizmo/v4/page.rb similarity index 94% rename from lib/survey_gizmo/api/page.rb rename to lib/survey_gizmo/v4/page.rb index 304bb4b..3f0cbb0 100644 --- a/lib/survey_gizmo/api/page.rb +++ b/lib/survey_gizmo/v4/page.rb @@ -1,6 +1,6 @@ -require 'survey_gizmo/api/question' +require 'survey_gizmo/v4/question' -module SurveyGizmo::API +module SurveyGizmo::V4 class Page include SurveyGizmo::Resource include SurveyGizmo::MultilingualTitle diff --git a/lib/survey_gizmo/api/question.rb b/lib/survey_gizmo/v4/question.rb similarity index 97% rename from lib/survey_gizmo/api/question.rb rename to lib/survey_gizmo/v4/question.rb index 1a2b137..b622187 100644 --- a/lib/survey_gizmo/api/question.rb +++ b/lib/survey_gizmo/v4/question.rb @@ -1,6 +1,6 @@ -require 'survey_gizmo/api/option' +require 'survey_gizmo/v4/option' -module SurveyGizmo::API +module SurveyGizmo::V4 class Question include SurveyGizmo::Resource include SurveyGizmo::MultilingualTitle diff --git a/lib/survey_gizmo/api/response.rb b/lib/survey_gizmo/v4/response.rb similarity index 98% rename from lib/survey_gizmo/api/response.rb rename to lib/survey_gizmo/v4/response.rb index f4bc614..4fdf9e7 100644 --- a/lib/survey_gizmo/api/response.rb +++ b/lib/survey_gizmo/v4/response.rb @@ -1,4 +1,4 @@ -module SurveyGizmo::API +module SurveyGizmo::V4 class Response include SurveyGizmo::Resource diff --git a/lib/survey_gizmo/api/survey.rb b/lib/survey_gizmo/v4/survey.rb similarity index 98% rename from lib/survey_gizmo/api/survey.rb rename to lib/survey_gizmo/v4/survey.rb index f43a647..17dc4b2 100644 --- a/lib/survey_gizmo/api/survey.rb +++ b/lib/survey_gizmo/v4/survey.rb @@ -1,6 +1,6 @@ -require 'survey_gizmo/api/page' +require 'survey_gizmo/v4/page' -module SurveyGizmo::API +module SurveyGizmo::V4 class Survey include SurveyGizmo::Resource diff --git a/lib/survey_gizmo/v5/account_teams.rb b/lib/survey_gizmo/v5/account_teams.rb new file mode 100644 index 0000000..ce6c961 --- /dev/null +++ b/lib/survey_gizmo/v5/account_teams.rb @@ -0,0 +1,18 @@ +# This REST endpoint is only available to accounts with admin privileges +# This code is untested. + +module SurveyGizmo::V5 + class AccountTeams + include SurveyGizmo::Resource + + attribute :id, Integer + attribute :default_role, String + attribute :status, String + + # v5 fields + attribute :team_name, String + attribute :description, String + + @route = '/accountteams' + end +end diff --git a/lib/survey_gizmo/v5/answer.rb b/lib/survey_gizmo/v5/answer.rb new file mode 100644 index 0000000..d7b0fb6 --- /dev/null +++ b/lib/survey_gizmo/v5/answer.rb @@ -0,0 +1,72 @@ +require 'survey_gizmo/v5/option' + +module SurveyGizmo::V5 + class Answer + include Virtus.model + + attribute :key, String + attribute :value, String + attribute :survey_id, Integer + attribute :response_id, Integer + attribute :question_id, Integer + attribute :question_text, String + attribute :question_type, String + attribute :options, Array[Option] + attribute :submitted_at, DateTime + attribute :answer_text, String + attribute :other_text, String + attribute :question_pipe, String + + def initialize(attrs = {}) + self.attributes = attrs + self.question_id = value['id'] + self.question_text = value['question'] + self.question_type = value['type'] + + if value['options'] + self.answer_text = selected_options_texts.join(', ') + else + self.answer_text = value['answer'] + end + end + + def single_option + [ + Option.new(attributes.merge( + id: value['answer_id'], + value: value['answer'], + title: value['original_answer'] || value['answer'] + )) + ] + end + + def selected_options_texts + selected_options.map do |opt| + opt['answer'] + end + end + + def selected_options + value['options'].values.reject { |opt| opt['answer'].nil? }.map do |opt| + Option.new(attributes.merge( + id: opt['id'], + value: opt['answer'], + title: opt['option'] + )) + end + end + + # Strips out the answer_text when there is a valid option_id + def to_hash + { + response_id: response_id, + question_id: question_id, + question_pipe: question_pipe, + submitted_at: submitted_at, + survey_id: survey_id, + other_text: other_text, + answer_text: other_text ? nil : answer_text + }.reject { |k, v| v.nil? } + end + end +end diff --git a/lib/survey_gizmo/v5/campaign.rb b/lib/survey_gizmo/v5/campaign.rb new file mode 100644 index 0000000..8b78e27 --- /dev/null +++ b/lib/survey_gizmo/v5/campaign.rb @@ -0,0 +1,32 @@ +module SurveyGizmo::V5 + class Campaign + include SurveyGizmo::Resource + + attribute :id, Integer + attribute :name, String + attribute :type, String + attribute :status, String + attribute :uri, String + attribute :SSL, Boolean + attribute :language, String + attribute :close_message, String + attribute :limit_responses, String + attribute :survey_id, Integer + + # v5 fields + attribute :token_variables, Array + attribute :invite_id, Integer + attribute :subtype, String + attribute :link_type, String + attribute :date_created, DateTime + attribute :date_modified, DateTime + attribute :link_open_date, DateTime + attribute :link_close_date, DateTime + + @route = '/survey/:survey_id/surveycampaign' + + def contacts(conditions = {}) + Contact.all(conditions.merge(children_params).merge(all_pages: !conditions[:page])) + end + end +end diff --git a/lib/survey_gizmo/v5/contact.rb b/lib/survey_gizmo/v5/contact.rb new file mode 100644 index 0000000..d36500d --- /dev/null +++ b/lib/survey_gizmo/v5/contact.rb @@ -0,0 +1,46 @@ +module SurveyGizmo::V5 + class Contact + include SurveyGizmo::Resource + + attribute :id, Integer + attribute :survey_id, Integer + attribute :campaign_id, Integer + + # v5 fields + attribute :date_last_sent, DateTime + attribute :division, String + attribute :team, String + attribute :group, String + attribute :role, String + attribute :status, String + attribute :subscriber_status, String + attribute :email_address, String + attribute :first_name, String + attribute :last_name, String + attribute :organization, String + attribute :department, String + attribute :business_phone, String + attribute :home_phone, String + attribute :fax_phone, String + attribute :mailing_address, String + attribute :mailing_address2, String + attribute :mailing_address_city, String + attribute :mailing_address_state, String + attribute :mailing_address_country, String + attribute :mailing_address_postal, String + attribute :title, String + attribute :url, String + attribute :customfield1, String + attribute :customfield2, String + attribute :customfield3, String + attribute :customfield4, String + attribute :customfield5, String + attribute :customfield6, String + attribute :customfield7, String + attribute :customfield8, String + attribute :customfield9, String + attribute :customfield10, String + + @route = '/survey/:survey_id/surveycampaign/:campaign_id/contact' + end +end diff --git a/lib/survey_gizmo/v5/email_message.rb b/lib/survey_gizmo/v5/email_message.rb new file mode 100644 index 0000000..c645d91 --- /dev/null +++ b/lib/survey_gizmo/v5/email_message.rb @@ -0,0 +1,29 @@ +module SurveyGizmo::V5 + class EmailMessage + include SurveyGizmo::Resource + + attribute :id, Integer + attribute :survey_id, Integer + attribute :campaign_id, Integer + attribute :invite_identity, Integer + attribute :subject, String + attribute :replies, String + attribute :medium, String + attribute :status, String + attribute :from, Hash + attribute :body, Hash + attribute :send, Boolean + + # v5 fields + attribute :type, String + attribute :subtype, String + attribute :message_type, String + attribute :footer, String + attribute :embed_question, Boolean + attribute :disable_styles, Boolean + attribute :date_created, DateTime + attribute :date_modified, DateTime + + @route = '/survey/:survey_id/surveycampaign/:campaign_id/emailmessage' + end +end diff --git a/lib/survey_gizmo/v5/option.rb b/lib/survey_gizmo/v5/option.rb new file mode 100644 index 0000000..142445b --- /dev/null +++ b/lib/survey_gizmo/v5/option.rb @@ -0,0 +1,18 @@ +module SurveyGizmo::V5 + class Option + include SurveyGizmo::Resource + include SurveyGizmo::MultilingualTitle + + attribute :id, Integer + attribute :survey_id, Integer + attribute :page_id, Integer + attribute :question_id, Integer + attribute :value, String + attribute :properties, Hash + + # v5 fields + attribute :title, String + + @route = '/survey/:survey_id/surveypage/:page_id/surveyquestion/:question_id/surveyoption' + end +end diff --git a/lib/survey_gizmo/v5/page.rb b/lib/survey_gizmo/v5/page.rb new file mode 100644 index 0000000..3025fce --- /dev/null +++ b/lib/survey_gizmo/v5/page.rb @@ -0,0 +1,27 @@ +require 'survey_gizmo/v5/question' + +module SurveyGizmo::V5 + class Page + include SurveyGizmo::Resource + include SurveyGizmo::MultilingualTitle + + attribute :id, Integer + attribute :description, String + attribute :properties, Hash + attribute :survey_id, Integer + attribute :questions, Array[Question] + + # v5 fields + attribute :title, String + + @route = '/survey/:survey_id/surveypage' + + def survey + @survey ||= Survey.first(id: survey_id) + end + + def questions + @questions.each { |q| q.attributes = children_params } + end + end +end diff --git a/lib/survey_gizmo/v5/question.rb b/lib/survey_gizmo/v5/question.rb new file mode 100644 index 0000000..fabf206 --- /dev/null +++ b/lib/survey_gizmo/v5/question.rb @@ -0,0 +1,50 @@ +require 'survey_gizmo/v5/option' + +module SurveyGizmo::V5 + class Question + include SurveyGizmo::Resource + include SurveyGizmo::MultilingualTitle + + attribute :id, Integer + attribute :type, String + attribute :description, String + attribute :shortname, String + attribute :properties, Hash + attribute :options, Array[Option] + attribute :survey_id, Integer + attribute :page_id, Integer, default: 1 + attribute :parent_question_id, Integer + + # v5 fields + attribute :base_type, String + attribute :subtype, String + attribute :varname, Array + attribute :has_showhide_deps, Boolean + attribute :comment, Boolean + attribute :sub_questions, Array[Question] + + @route = { + get: '/survey/:survey_id/surveyquestion/:id', + create: '/survey/:survey_id/surveypage/:page_id/surveyquestion', + update: '/survey/:survey_id/surveypage/:page_id/surveyquestion/:id' + } + @route[:delete] = @route[:update] + + def survey + @survey ||= Survey.first(id: survey_id) + end + + def options + return parent_question.options.dup.each { |o| o.question_id = id } if parent_question + + @options ||= Option.all(children_params.merge(all_pages: true)).to_a + @options.each { |o| o.attributes = children_params } + end + + def parent_question + return nil unless parent_question_id + + @parent_question ||= Question.first(survey_id: survey_id, id: parent_question_id) + end + end +end diff --git a/lib/survey_gizmo/v5/response.rb b/lib/survey_gizmo/v5/response.rb new file mode 100644 index 0000000..95f7ae9 --- /dev/null +++ b/lib/survey_gizmo/v5/response.rb @@ -0,0 +1,63 @@ +module SurveyGizmo::V5 + class Response + include SurveyGizmo::Resource + + # Filters + NO_TEST_DATA = { field: 'is_test_data', operator: '<>', value: 1 } + ONLY_COMPLETED = { field: 'status', operator: '=', value: 'Complete' } + + def self.submitted_since_filter(time) + { + field: 'date_submitted', + operator: '>=', + value: time.in_time_zone(SurveyGizmo.configuration.api_time_zone).strftime('%Y-%m-%d %H:%M:%S') + } + end + + attribute :id, Integer + attribute :survey_id, Integer + attribute :contact_id, Integer + attribute :status, String + attribute :is_test_data, Boolean + attribute :meta, Hash # READ-ONLY + attribute :url, Hash # READ-ONLY + + # v5 fields + attribute :date_submitted, DateTime + attribute :date_started, DateTime + attribute :session_id, String + attribute :language, String + attribute :url_variables, Hash + attribute :survey_data, Hash + attribute :comment, Hash + attribute :subquestions, Hash + attribute :options, Hash + attribute :link_id, String + attribute :ip_address, String + attribute :referer, String + attribute :user_agent, String + attribute :response_time, Integer + attribute :data_quality, Array + attribute :longitude, String + attribute :latitude, String + attribute :country, String + attribute :city, String + attribute :region, String + attribute :postal, String + attribute :dma, Boolean + alias_attribute :submitted_at, :date_submitted + alias_attribute :answers, :survey_data + + @route = '/survey/:survey_id/surveyresponse' + + def survey + @survey ||= Survey.first(id: survey_id) + end + + def parsed_answers + answers.map do |k, v| + Answer.new(children_params.merge(key: k, value: v, answer_text: v, submitted_at: submitted_at)) + end + end + end +end diff --git a/lib/survey_gizmo/v5/survey.rb b/lib/survey_gizmo/v5/survey.rb new file mode 100644 index 0000000..0144c13 --- /dev/null +++ b/lib/survey_gizmo/v5/survey.rb @@ -0,0 +1,91 @@ +require 'survey_gizmo/v5/page' + +module SurveyGizmo::V5 + class Survey + include SurveyGizmo::Resource + + attribute :id, Integer + attribute :team, Array + attribute :type, String + attribute :status, String + attribute :forward_only, Boolean + attribute :title, String + attribute :internal_title, String + attribute :title_ml, Hash + attribute :links, Hash + attribute :theme, Integer + attribute :blockby, String + attribute :languages, Array + attribute :statistics, Array + attribute :created_on, DateTime + attribute :modified_on, DateTime + # See comment in the #pages method for why this :pages can't be an attribute + # attribute :pages, Array[Page] + + @route = '/survey' + + def pages + # SurveyGizmo sends down the page info to .first requests but NOT to .all requests, so we must load pages + # manually. We should be able to just .reload this Survey BUT we can't make :pages a Virtus attribute without + # requiring a call to this method during Survey.save + @pages ||= Page.all(children_params.merge(all_pages: true)).to_a + @pages.each { |p| p.attributes = children_params } + end + + # Sub question handling is in resource.rb and page.rb. It should probably be here instead but if it gets moved + # here and people try to request all the questions for a specific page directly from a ::API::Question request or + # from Page.questions, sub questions will not be included! So I left it there for least astonishment. + def questions + @questions ||= pages.flat_map { |p| p.questions } + end + + def actual_questions + questions.select do |q| + q.base_type == 'Question' || q.base_type == 'SurveyQuestion' + end + end + + def responses(conditions = {}) + Response.all(conditions.merge(children_params).merge(all_pages: !conditions[:page])) + end + + # Statistics array of arrays looks like: + # [["Partial", 2], ["Disqualified", 28], ["Complete", 15]] + def number_of_completed_responses + if statistics && !statistics.empty? && (completed_data = statistics.find { |a| a[0] == 'Complete' }) + completed_data[1] + else + 0 + end + end + + def server_has_new_results_since?(time, filters = []) + Response.all( + children_params.merge( + page: 1, + resultsperpage: 1, + filters: filters + [Response.submitted_since_filter(time)] + ) + ).to_a.size > 0 + end + + # As of 2015-12-18, when you request data on multiple surveys from /survey, the team variable comes + # back as "0". If you request one survey at a time from /survey/{id}, it is populated correctly. + def teams + @individual_survey ||= self.reload + @individual_survey.team + end + + def team_names + teams.map { |t| t['name'] } + end + + def belongs_to?(team) + team_names.any? { |t| t == team } + end + + def campaigns + @campaigns ||= Campaign.all(children_params.merge(all_pages: true)).to_a + end + end +end diff --git a/spec/configuration_spec.rb b/spec/configuration_spec.rb index 65efd60..9652813 100644 --- a/spec/configuration_spec.rb +++ b/spec/configuration_spec.rb @@ -5,6 +5,7 @@ let(:api_token) { "token" } let(:api_token_secret) { "doken" } + let(:test_api_version) { 'v4' } before(:each) do SurveyGizmo.configure do |config| diff --git a/spec/logger_spec.rb b/spec/logger_spec.rb index 0c00333..76194c3 100644 --- a/spec/logger_spec.rb +++ b/spec/logger_spec.rb @@ -4,6 +4,7 @@ let(:progname) { 'TEST' } let(:severity) { 'INFO' } let(:time_string) { '2015-04-15 05:46:30' } + let(:test_api_version) { 'v4' } before(:each) do SurveyGizmo.configure do |config| diff --git a/spec/resource_spec.rb b/spec/resource_spec.rb index 5d7986f..6ca28de 100644 --- a/spec/resource_spec.rb +++ b/spec/resource_spec.rb @@ -3,6 +3,7 @@ describe 'Survey Gizmo Resource' do let(:create_attributes_to_compare) { } let(:get_attributes_to_compare) { } + let(:test_api_version) { 'v4' } describe SurveyGizmo::Resource do let(:described_class) { SurveyGizmoSpec::ResourceTest } @@ -50,7 +51,7 @@ end end - describe SurveyGizmo::API::Survey do + describe SurveyGizmo::V4::Survey do let(:create_attributes) { { title: 'Spec', type: 'survey', status: 'In Design' } } let(:get_attributes) { create_attributes.merge(first_params) } let(:update_attributes) { { title: 'Updated'} } @@ -78,7 +79,7 @@ end end - describe SurveyGizmo::API::Question do + describe SurveyGizmo::V4::Question do let(:base_params) { {survey_id: 1234, page_id: 1} } let(:create_attributes) { base_params.merge(title: 'Spec Question', type: 'radio', properties: { 'required' => true, 'option_sort' => false }) } let(:update_attributes) { base_params.merge(title: 'Updated') } @@ -190,7 +191,7 @@ end end - describe SurveyGizmo::API::Option do + describe SurveyGizmo::V4::Option do let(:survey_and_page) { {survey_id: 1234, page_id: 1} } let(:create_attributes) { survey_and_page.merge(question_id: 1, title: 'Spec Question', value: 'Spec Answer') } let(:update_attributes) { survey_and_page.merge(question_id: 1, title: 'Updated') } @@ -206,7 +207,7 @@ it_should_behave_like 'an object with errors' end - describe SurveyGizmo::API::Page do + describe SurveyGizmo::V4::Page do let(:create_attributes) { {:survey_id => 1234, :title => 'Spec Page' } } let(:get_attributes) { create_attributes.merge(:id => 1) } let(:update_attributes) { {:survey_id => 1234, :title => 'Updated'} } @@ -221,7 +222,7 @@ it_should_behave_like 'an object with errors' end - describe SurveyGizmo::API::Response do + describe SurveyGizmo::V4::Response do let(:create_attributes) { {:survey_id => 1234, :datesubmitted => "2015-04-15 05:46:30" } } let(:create_attributes_to_compare) { create_attributes.merge(:datesubmitted => Time.parse("2015-04-15 05:46:30 -0400")) } let(:get_attributes) { create_attributes.merge(:id => 1) } @@ -295,7 +296,7 @@ end end - describe SurveyGizmo::API::AccountTeams do + describe SurveyGizmo::V4::AccountTeams do pending('Need an account with admin privileges to test this') let(:create_attributes) { { teamid: 1234, teamname: 'team' } } let(:get_attributes) { create_attributes.merge(id: 1234) } diff --git a/spec/resource_v5_spec.rb b/spec/resource_v5_spec.rb new file mode 100644 index 0000000..8cd0812 --- /dev/null +++ b/spec/resource_v5_spec.rb @@ -0,0 +1,301 @@ +require 'spec_helper' + +describe 'Survey Gizmo Resource V5' do + let(:create_attributes_to_compare) { } + let(:get_attributes_to_compare) { } + let(:test_api_version) { 'v5' } + + describe SurveyGizmo::Resource do + let(:described_class) { SurveyGizmoSpec::ResourceTest } + let(:create_attributes) { { title: 'Spec', test_id: 5 } } + let(:update_attributes) { { title: 'Updated' } } + let(:first_params) { { id: 1, test_id: 5 } } + let(:get_attributes) { create_attributes.merge(id: 1) } + let(:uri_paths){ + { + get: '/test/1', + create: '/test/5/resource', + update: '/test/5/resource/1', + delete: '/test/5/resource/1' + } + } + + it '#reload' do + stub_request(:get, /#{@base}/).to_return(json_response(true, get_attributes)) + obj = described_class.new(get_attributes.merge(update_attributes)) + expect(obj.attributes.reject { |k, v| v.blank? }).to eq(get_attributes.merge(update_attributes)) + obj.reload + expect(obj.attributes.reject { |k, v| v.blank? }).to eq(get_attributes) + end + + it 'should raise an error if params are missing' do + expect(lambda { + SurveyGizmoSpec::ResourceTest.destroy(test_id: 5) + }).to raise_error(SurveyGizmo::URLError, 'Missing RESTful parameters in request: `:id`') + end + + it_should_behave_like 'an API object' + it_should_behave_like 'an object with errors' + + context '#filters_to_query_string' do + let(:page) { 2 } + let(:filters) { { page: page, filters: [{ field: 'istestdata', operator: '<>', value: 1 }] } } + + it 'should generate the correct page request' do + expect(SurveyGizmoSpec::ResourceTest.send(:filters_to_query_string, { page: page })).to eq("?page=#{page}") + end + + it 'should generate the correct filter fragment' do + expect(SurveyGizmoSpec::ResourceTest.send(:filters_to_query_string, filters)).to eq("?filter%5Bfield%5D%5B0%5D=istestdata&filter%5Boperator%5D%5B0%5D=%3C%3E&filter%5Bvalue%5D%5B0%5D=1&page=#{page}") + end + end + end + + describe SurveyGizmo::V5::Survey do + let(:create_attributes) { { title: 'Spec', type: 'survey', status: 'In Design' } } + let(:get_attributes) { create_attributes.merge(first_params) } + let(:update_attributes) { { title: 'Updated'} } + let(:first_params) { { id: 1234} } + let(:uri_paths){ + h = { :create => '/survey' } + h.default = '/survey/1234' + h + } + + it_should_behave_like 'an API object' + it_should_behave_like 'an object with errors' + + it 'should parse the number of completed records correctly' do + survey = described_class.new('statistics' => [['Partial', 2], ['Disqualified', 28], ['Complete', 15]]) + expect(survey.number_of_completed_responses).to eq(15) + end + + it 'should determine if there are new results' do + stub_request(:get, /#{@base}\/survey\/1\/surveyresponse/).to_return(json_response(true, [])) + + survey = described_class.new(id: 1) + expect(survey.server_has_new_results_since?(Time.now)).to be_falsey + expect(a_request(:get, /#{@base}\/survey\/1\/surveyresponse/)).to have_been_made + end + end + + describe SurveyGizmo::V5::Question do + let(:base_params) { {survey_id: 1234, page_id: 1} } + let(:create_attributes) { base_params.merge(title: 'Spec Question', type: 'radio', properties: { 'required' => true, 'option_sort' => false }) } + let(:update_attributes) { base_params.merge(title: 'Updated') } + let(:first_params) { base_params.merge(id: 1) } + let(:get_attributes) { create_attributes.merge(id: 1).reject { |k, v| k == :properties } } + let(:uri_paths) { + { :get => '/survey/1234/surveyquestion/1', + :create => '/survey/1234/surveypage/1/surveyquestion', + :update => '/survey/1234/surveypage/1/surveyquestion/1', + :delete => '/survey/1234/surveypage/1/surveyquestion/1' + } + } + + it_should_behave_like 'an API object' + it_should_behave_like 'an object with errors' + + it 'should handle the title hash returned from the API' do + expect(described_class.new('title' => {'English' => 'Some title'}).title).to eq('Some title') + end + + it 'should find the survey' do + stub_request(:get, /#{@base}\/survey\/1234/).to_return(json_response(true, get_attributes)) + described_class.new(base_params).survey + expect(a_request(:get, /#{@base}\/survey\/1234/)).to have_been_made + end + + context 'options' do + let(:survey_id) { 15 } + let(:question_id) { 23 } + let(:body_data) do + { + "id" => question_id, + "title" => {"English"=>"How likely are you to bang your head to Bohemian Rhapsody?"}, + "options"=> + [ + { + "id" => 10014, + "title" => {"English"=>"0 = Not at all likely"}, + "value" => "0 = Not at all likely" + }, + { + "id" => 10015, + "title" => {"English"=>"1"}, + "value" => "1" + } + ] + } + end + + context 'option parsing' do + before do + stub_request(:get, /#{@base}\/survey\/#{survey_id}\/surveyquestion\/#{question_id}/).to_return(json_response(true, body_data)) + end + + it 'correctly parses options out of question data' do + question = described_class.first(survey_id: survey_id, id: question_id) + expect(question.options.all? { |o| o.question_id == question_id && o.survey_id == survey_id }).to be_truthy + expect(question.options.map { |o| o.id }).to eq([10014, 10015]) + expect(a_request(:get, /#{@base}\/.*surveyoption/)).to_not have_been_made + end + + it 'correctly parses sub question options' do + question = described_class.new(survey_id: survey_id, id: question_id + 1, parent_question_id: question_id) + expect(question.parent_question.id).to eq(described_class.new(body_data).id) + expect(question.options.all? { |o| o.question_id == question.id && o.survey_id == survey_id }).to be_truthy + expect(question.options.map { |o| o.id }).to eq([10014, 10015]) + expect(a_request(:get, /#{@base}\/survey\/#{survey_id}\/surveyquestion\/#{question_id}/)).to have_been_made + end + end + end + + context 'subquestions' do + let(:parent_id) { 33 } + let(:subquestions) { + [ + described_class.new(id: 544, survey_id: 1234, parent_question_id: parent_id), + described_class.new(id: 322, survey_id: 1234, parent_question_id: parent_id) + ] + } + let(:question_with_subquestions) { described_class.new(id: parent_id, survey_id: 1234, sub_questions: subquestions) } + + it 'should have no subquestions' do + expect(described_class.new.sub_questions).to eq([]) + end + + it 'should have 2 subquestions and they should have the right parent question' do + stub_request(:get, /#{@base}/).to_return(json_response(true, get_attributes)) + expect(question_with_subquestions.sub_questions.size).to eq(2) + + question_with_subquestions.sub_questions.first.parent_question + question_with_subquestions.sub_questions.last.parent_question + expect(a_request(:get, /#{@base}\/survey\/1234\/surveyquestion\/#{parent_id}/)).to have_been_made.twice + end + + context 'and shortname' do + let(:sku) { 6 } + let(:subquestions) { + [ + described_class.new(id: 6, shortname: '0', survey_id: 1234, parent_question_id: parent_id), + described_class.new(id: 8, shortname: 'foo', survey_id: 1234, parent_question_id: parent_id) + ] + } + let(:question_with_subquestions) { described_class.new(id: parent_id, survey_id: 1234, sub_questions: subquestions) } + + it 'should have 2 subquestions and they should have the right parent question' do + stub_request(:get, /#{@base}/).to_return(json_response(true, get_attributes)) + expect(question_with_subquestions.sub_questions.size).to eq(2) + + question_with_subquestions.sub_questions.first.parent_question + expect(a_request(:get, /#{@base}\/survey\/1234\/surveyquestion\/#{parent_id}/)).to have_been_made + end + end + end + end + + describe SurveyGizmo::V5::Option do + let(:survey_and_page) { {survey_id: 1234, page_id: 1} } + let(:create_attributes) { survey_and_page.merge(question_id: 1, title: 'Spec Question', value: 'Spec Answer') } + let(:update_attributes) { survey_and_page.merge(question_id: 1, title: 'Updated') } + let(:first_params) { survey_and_page.merge(id: 1, question_id: 1) } + let(:get_attributes) { create_attributes.merge(id: 1) } + let(:uri_paths) { + h = { :create => '/survey/1234/surveypage/1/surveyquestion/1/surveyoption' } + h.default = '/survey/1234/surveypage/1/surveyquestion/1/surveyoption/1' + h + } + + it_should_behave_like 'an API object' + it_should_behave_like 'an object with errors' + end + + describe SurveyGizmo::V5::Page do + let(:create_attributes) { {:survey_id => 1234, :title => 'Spec Page' } } + let(:get_attributes) { create_attributes.merge(:id => 1) } + let(:update_attributes) { {:survey_id => 1234, :title => 'Updated'} } + let(:first_params) { {:id => 1, :survey_id => 1234 } } + let(:uri_paths){ + h = { :create => '/survey/1234/surveypage' } + h.default = '/survey/1234/surveypage/1' + h + } + + it_should_behave_like 'an API object' + it_should_behave_like 'an object with errors' + end + + describe SurveyGizmo::V5::Response do + # date_submitted is specified as DateTime, not Time + let(:create_attributes) { {:survey_id => 1234, :date_submitted => "2015-04-15 05:46:30 -0400" } } + let(:create_attributes_to_compare) { create_attributes.merge(:date_submitted => DateTime.parse("2015-04-15 05:46:30 -0400")) } + let(:get_attributes) { create_attributes.merge(:id => 1) } + let(:get_attributes_to_compare) { create_attributes_to_compare.merge(:id => 1) } + let(:update_attributes) { {:survey_id => 1234, :title => 'Updated'} } + let(:first_params) { {:id => 1, :survey_id => 1234 } } + let(:uri_paths){ + h = { :create => '/survey/1234/surveyresponse' } + h.default = '/survey/1234/surveyresponse/1' + h + } + + it_should_behave_like 'an API object' + it_should_behave_like 'an object with errors' + + context 'during EST' do + let(:create_attributes) { {:survey_id => 1234, :date_submitted => "2015-01-15 05:46:30 -0500" } } + let(:create_attributes_to_compare) { create_attributes.merge(:date_submitted => DateTime.parse("2015-01-15 05:46:30 -0500")) } + + it_should_behave_like 'an API object' + it_should_behave_like 'an object with errors' + end + + context 'answers' do + let(:survey_id) { 6 } + let(:response_id) { 7 } + let(:timestamp) { '2015-01-02'.to_time(:utc) } + let(:answers) do + { + 5 => { + 'id' => 5, + 'type' => "TEXTBOX", + 'shown' => true, + 'answer' => 'VERY important' + }, + 6 => { + 'id' => 6, + 'type' => "TEXTBOX", + 'shown' => false + } + } + end + + it 'should propagate time, survey_id, and response_id' do + response = described_class.new( + answers: answers.select { |k, v| k == 5}, + survey_id: survey_id, + id: response_id, + submitted_at: timestamp + ) + expect(response.parsed_answers.map { |a| a.to_hash }).to eq([ { survey_id: survey_id, response_id: response_id, question_id: 5, answer_text: "VERY important", submitted_at: timestamp }]) + end + end + end + + describe SurveyGizmo::V5::AccountTeams do + pending('Need an account with admin privileges to test this') + let(:create_attributes) { { team_name: 'team' } } + let(:get_attributes) { create_attributes.merge(id: 1234) } + let(:update_attributes) { get_attributes } + let(:first_params) { get_attributes } + let(:uri_paths) do + h = { :create => '/accountteams' } + h.default = '/accountteams/1234' + h + end + + it_should_behave_like 'an API object' + it_should_behave_like 'an object with errors' + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 3e64206..f60b11b 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -16,6 +16,7 @@ config.api_token_secret = 'dreamword' config.retriable_params = { tries: 1, base_interval: 0 } config.logger.level = Logger::FATAL + config.api_version = test_api_version end @base = "#{SurveyGizmo.configuration.api_url}/#{SurveyGizmo.configuration.api_version}"