From ded6756afe214dec9712453906967b9061241b55 Mon Sep 17 00:00:00 2001 From: brittany Date: Mon, 23 May 2022 11:24:31 -0700 Subject: [PATCH 1/4] enable state-level emails to be created in the admin panel and sent from a template generated by email actions --- .env.example | 2 + README.md | 3 + app/assets/javascripts/admin.js | 1 + .../javascripts/admin/action_pages/email.js | 15 +++ .../application/tools/congress_message.js | 19 --- .../application/tools/email.js.erb | 20 +++ .../javascripts/application/tools/shared.js | 18 +++ .../admin/action_pages_controller.rb | 12 +- app/controllers/tools_controller.rb | 29 ++++- app/helpers/email_campaign_helper.rb | 15 +++ app/models/email_campaign.rb | 36 +++++- app/views/action_page/_state_reps.html.erb | 20 +++ .../admin/action_pages/_email_fields.html.erb | 36 +++++- app/views/tools/_email.html.erb | 121 ++++++++++-------- app/views/tools/_send_email.html.erb | 57 +++++++++ config/application.rb | 1 + config/routes.rb | 1 + config/secrets.yml | 2 + ...15_add_state_targets_to_email_campaigns.rb | 7 + ...0927185632_add_state_to_email_campaigns.rb | 5 + db/schema.rb | 14 +- lib/civic_api.rb | 64 +++++++++ spec/controllers/tools_controller_spec.rb | 40 +++++- spec/factories/email_campaigns.rb | 14 +- .../action_pages/custom_email_action_spec.rb | 15 +++ .../action_pages/email_action_spec.rb | 13 -- .../state_leg_email_action_spec.rb | 30 +++++ spec/features/admin/action_creation_spec.rb | 31 ++++- spec/features/users_spec.rb | 2 +- spec/lib/civic_api_spec.rb | 51 ++++++++ spec/models/email_campaign_spec.rb | 39 ++++-- spec/rails_helper.rb | 2 +- spec/spec_helper.rb | 2 +- spec/tasks/signatures_spec.rb | 4 +- 34 files changed, 610 insertions(+), 131 deletions(-) create mode 100644 app/assets/javascripts/admin/action_pages/email.js create mode 100644 app/assets/javascripts/application/tools/shared.js create mode 100644 app/helpers/email_campaign_helper.rb create mode 100644 app/views/action_page/_state_reps.html.erb create mode 100644 app/views/tools/_send_email.html.erb create mode 100644 db/migrate/20220923210415_add_state_targets_to_email_campaigns.rb create mode 100644 db/migrate/20220927185632_add_state_to_email_campaigns.rb create mode 100644 lib/civic_api.rb create mode 100644 spec/features/action_pages/custom_email_action_spec.rb delete mode 100644 spec/features/action_pages/email_action_spec.rb create mode 100644 spec/features/action_pages/state_leg_email_action_spec.rb create mode 100644 spec/lib/civic_api_spec.rb diff --git a/.env.example b/.env.example index 7c05f6e69..a25b320af 100644 --- a/.env.example +++ b/.env.example @@ -63,6 +63,8 @@ amazon_authorize_key= # Third party integration (required) congress_forms_url=http://phantomdc.example.com +google_civic_api_url=https://www.googleapis.com/civicinfo/v2/representatives/ +google_civic_api_key= smarty_streets_id= smarty_streets_token= diff --git a/README.md b/README.md index 526985ac8..a4a97b15b 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,9 @@ Follow these instructions to run the Action Center using Docker (recommended). T * Allows users to submit e-messages to congress * [Call Congress](https://github.com/EFForg/call-congress) url and API key * Connects calls between citizens and their congress person using the Twilio API +* [Google Civic Information API](https://developers.google.com/civic-information) url and API key + * Representative information powered by the Civic Information API + * We use this when we need to give a user the ability to find their representatives to complete a state-level email action ## Using the Action Center diff --git a/app/assets/javascripts/admin.js b/app/assets/javascripts/admin.js index a3cf8d238..118f8643c 100644 --- a/app/assets/javascripts/admin.js +++ b/app/assets/javascripts/admin.js @@ -8,6 +8,7 @@ //= require admin/gallery //= require admin/action_pages //= require admin/action_pages/petition-targets +//= require admin/action_pages/email //= require admin/analytics //= require_tree ./admin/components diff --git a/app/assets/javascripts/admin/action_pages/email.js b/app/assets/javascripts/admin/action_pages/email.js new file mode 100644 index 000000000..fdb5167cc --- /dev/null +++ b/app/assets/javascripts/admin/action_pages/email.js @@ -0,0 +1,15 @@ +$(document).ready(function() { + var stateLevelTarget = $("#action_page_email_campaign_attributes_state"); + var stateLevelTargetSelection = $("#state-level-target-selection"); + + if (stateLevelTarget.val() === "") + stateLevelTargetSelection.hide(); + + stateLevelTarget.on("change", function() { + if (stateLevelTarget.val() !== "") + stateLevelTargetSelection.show(); + else + stateLevelTargetSelection.hide(); + }); +}); + diff --git a/app/assets/javascripts/application/tools/congress_message.js b/app/assets/javascripts/application/tools/congress_message.js index c1c155291..7539e0b33 100644 --- a/app/assets/javascripts/application/tools/congress_message.js +++ b/app/assets/javascripts/application/tools/congress_message.js @@ -105,23 +105,4 @@ $(document).on("ready", function() { e.preventDefault(); $('#customize-message .notice').addClass('down'); }); - - function show_progress_bars() { - $(".progress-striped").show(); - $("#tools :submit").hide(); - $("#tools input,textarea,button,select", $(this)).attr("disabled", "disabled"); - } - - function show_error(error, form) { - $(".progress-striped").hide(); - form.find(":submit").show(); - form.find(".alert-danger").remove(); - $("#errors").append($('
').text(error)); - $("#tools input,textarea,button,select", form).removeAttr("disabled"); - } - - function update_tabs(from, to) { - $(".page-indicator div.page" + from).removeClass('active'); - $(".page-indicator div.page" + to).addClass('active'); - } }); diff --git a/app/assets/javascripts/application/tools/email.js.erb b/app/assets/javascripts/application/tools/email.js.erb index 7159ed624..d5dcdec9c 100644 --- a/app/assets/javascripts/application/tools/email.js.erb +++ b/app/assets/javascripts/application/tools/email.js.erb @@ -12,4 +12,24 @@ $(document).on('ready', function() { $('.thank-you').show(); $('#email-tool').hide(); }); + + $(".state-rep-email").hide(); + + $("form.state-rep-lookup").on("ajax:complete", function(e, xhr, status) { + var $form = $(this); + var data = xhr.responseJSON; + $('.state-rep-lookup').hide(); + $('.state-reps').replaceWith(data.content); + if (status == "success") { + $form.remove(); + $(".state-reps").html(data); + + if ($("#action-content").length) { + $(window).scrollTop( $("#action-content").offset().top ); // go to top of page if on action center site + } + update_tabs(1, 2); + } else { + show_error("Something went wrong. Please try again later.", $form); + } + }); }); diff --git a/app/assets/javascripts/application/tools/shared.js b/app/assets/javascripts/application/tools/shared.js new file mode 100644 index 000000000..97d07e676 --- /dev/null +++ b/app/assets/javascripts/application/tools/shared.js @@ -0,0 +1,18 @@ +function show_progress_bars() { + $(".progress-striped").show(); + $("#tools :submit").hide(); + $("#tools input,textarea,button,select", $(this)).attr("disabled", "disabled"); +} + +function show_error(error, form) { + $(".progress-striped").hide(); + form.find(":submit").show(); + form.find(".alert-danger").remove(); + $("#errors").append($('
').text(error)); + $("#tools input,textarea,button,select", form).removeAttr("disabled"); +} + +function update_tabs(from, to) { + $(".page-indicator div.page" + from).removeClass('active'); + $(".page-indicator div.page" + to).addClass('active'); +} \ No newline at end of file diff --git a/app/controllers/admin/action_pages_controller.rb b/app/controllers/admin/action_pages_controller.rb index 5045b8f7a..82a35ca2d 100644 --- a/app/controllers/admin/action_pages_controller.rb +++ b/app/controllers/admin/action_pages_controller.rb @@ -194,14 +194,16 @@ def action_page_params petition_attributes: [:id, :title, :description, :goal, :enable_affiliations], affiliation_types_attributes: [:id, :name], tweet_attributes: [ - :id, :target, :target_house, :target_senate, :message, :cta, :bioguide_id, + :id, :target, :target_house, :target_senate, :target_state_lower_chamber, + :target_state_upper_chamber, :message, :cta, :bioguide_id, tweet_targets_attributes: [:id, :_destroy, :twitter_id, :image] ], email_campaign_attributes: [ - :id, :message, :subject, :target_house, :target_senate, :target_email, - :email_addresses, :target_bioguide_id, :bioguide_id, :alt_text_email_your_rep, - :alt_text_look_up_your_rep, :alt_text_extra_fields_explain, :topic_category_id, - :alt_text_look_up_helper, :alt_text_customize_message_helper, :campaign_tag + :id, :message, :subject, :state, :target_state_lower_chamber, :target_state_upper_chamber, + :target_governor, :target_email, :email_addresses, :target_bioguide_id, + :bioguide_id, :alt_text_email_your_rep, :alt_text_look_up_your_rep, + :alt_text_extra_fields_explain, :topic_category_id, :alt_text_look_up_helper, + :alt_text_customize_message_helper, :campaign_tag ], congress_message_campaign_attributes: [ :id, :message, :subject, :target_house, :target_senate, { target_bioguide_list: [] }, diff --git a/app/controllers/tools_controller.rb b/app/controllers/tools_controller.rb index d5fe9158d..1cb4f2a1a 100644 --- a/app/controllers/tools_controller.rb +++ b/app/controllers/tools_controller.rb @@ -123,7 +123,34 @@ def email @actionPage = @action_page render "email_target" else - redirect_to @action_page.email_campaign.service_uri(params[:service]) + if params[:state_rep_email] + redirect_to @action_page.email_campaign.service_uri(params[:service], { email: params[:state_rep_email] }) + else + redirect_to @action_page.email_campaign.service_uri(params[:service]) + end + end + end + + # GET /tools/state_reps + # + # This endpoint is hit by the js for state legislator lookup-by-address actions. + # It renders json containing html markup for presentation on the view + def state_reps + @email_campaign = EmailCampaign.find(params[:email_campaign_id]) + @actionPage = @email_campaign.action_page + address = "#{params[:street_address]} #{params[:zipcode]}" + civic_api_response = CivicApi.state_rep_search(address, @email_campaign.leg_level) + @state_reps = JSON.parse(civic_api_response.body)["officials"] + state_rep_emails = [] + @state_reps.each do |sr| + state_rep_emails << sr["emails"] if !sr["emails"].nil? + end + # single-rep lookup only + @state_rep_email = state_rep_emails.flatten.first + if @state_reps.present? + render json: { content: render_to_string(partial: "action_page/state_reps") }, status: 200 + else + render json: { error: "No representatives found" }, status: 200 end end diff --git a/app/helpers/email_campaign_helper.rb b/app/helpers/email_campaign_helper.rb new file mode 100644 index 000000000..54c9f28b1 --- /dev/null +++ b/app/helpers/email_campaign_helper.rb @@ -0,0 +1,15 @@ +module EmailCampaignHelper + def legislative_level_from_state_representative_info(legislator_info) + role = case legislator_info + when "legislatorLowerBody" + "state representative of the lower chamber" + when "legislatorUpperBody" + "state representative of the upper chamber" + when "headOfGovernment" + "Governor of the State" + else + "Invalid info" + end + role + end +end diff --git a/app/models/email_campaign.rb b/app/models/email_campaign.rb index 604935b9b..3f4cff5b5 100644 --- a/app/models/email_campaign.rb +++ b/app/models/email_campaign.rb @@ -2,6 +2,9 @@ class EmailCampaign < ActiveRecord::Base belongs_to :topic_category has_one :action_page + # No DC + STATES = %w(AK AL AR AZ CA CO CT DE FL GA HI IA ID IL IN KS KY LA MA MD ME MI MN MO MS MT NC ND NE NH NJ NM NV NY OH OK OR PA RI SC SD TN TX UT VA VT WA WI WV WY) + def email_your_rep_text(default) target_bioguide_text_or_default alt_text_email_your_rep, default end @@ -22,19 +25,40 @@ def extra_fields_explain_text(default) target_bioguide_text_or_default alt_text_extra_fields_explain, default end + def leg_level + role = "legislatorLowerBody" if self.target_state_lower_chamber + role = "legislatorUpperBody" if self.target_state_upper_chamber + role = "headOfGovernment" if self.target_governor + + return role + end + include ERB::Util - def service_uri(service) - mailto_addresses = email_addresses.split(/\s*,\s*/).map do |email| - u(email.gsub(" ", "")).gsub("%40", "@") + def service_uri(service, opts = {}) + mailto_addresses = opts[:email] + mailto_addresses ||= email_addresses + # look for custom email addresses set on the back end if there is no email param from the front-end, + # as is the case when we send state-level emails -- we cannot store these email address in our db, + # reason below: + + # https://developers.google.com/terms#e_prohibitions_on_content + # Section 5.e.1., as of December 2022 + # e. Prohibitions on Content + # Unless expressly permitted by the content owner or by applicable law, you will not, and will not permit your end users or others acting on your behalf to, do the following with content returned from the APIs: + # Scrape, build databases, or otherwise create permanent copies of such content, or keep cached copies longer than permitted by the cache header; + + # results in comma-separated string of email addresses + default_mailto_addresses ||= mailto_addresses.split(/\s*,\s*/).map do |email| + u(email.gsub(" ", "")).gsub("%40", "@").gsub("%2B", "+") end.join(",") { - default: "mailto:#{mailto_addresses}?#{query(body: message, subject: subject)}", + default: "mailto:#{default_mailto_addresses}?#{query(body: message, subject: subject)}", - gmail: "https://mail.google.com/mail/?view=cm&fs=1&#{{ to: email_addresses, body: message, su: subject }.to_query}", + gmail: "https://mail.google.com/mail/?view=cm&fs=1&#{{ to: mailto_addresses, body: message, su: subject }.to_query}", - hotmail: "https://outlook.live.com/default.aspx?rru=compose&#{{ to: email_addresses, body: message, subject: subject }.to_query}#page=Compose" + hotmail: "https://outlook.live.com/default.aspx?rru=compose&#{{ to: mailto_addresses, body: message, subject: subject }.to_query}#page=Compose" }.fetch(service.to_sym) end diff --git a/app/views/action_page/_state_reps.html.erb b/app/views/action_page/_state_reps.html.erb new file mode 100644 index 000000000..ccb5f9f13 --- /dev/null +++ b/app/views/action_page/_state_reps.html.erb @@ -0,0 +1,20 @@ +
+ <%= "This action is for the #{legislative_level_from_state_representative_info(@email_campaign.leg_level)}." %> + <% @state_reps.each do |sr| -%> +
+ <%= "Your representative is #{sr["name"]}." %> + <% if !sr["emails"].nil? %> + <%= "They can be reached at: " %> + <% sr["emails"].each do |e| %> + <%= "#{e}" %> + <% end %> + <% else %> + <%= "We could not find their email address." %> + <% end %> +
+ <% end -%> +
+ +
+ <%= render 'tools/send_email' %> +
diff --git a/app/views/admin/action_pages/_email_fields.html.erb b/app/views/admin/action_pages/_email_fields.html.erb index e8ee74353..0a0c98ab7 100644 --- a/app/views/admin/action_pages/_email_fields.html.erb +++ b/app/views/admin/action_pages/_email_fields.html.erb @@ -1,8 +1,4 @@ <%= f.fields_for(:email_campaign) do |sf| %> -
- <%= sf.label :email_addresses, "To" %> - <%= sf.text_field :email_addresses %> -
<%= sf.label :subject %> @@ -13,4 +9,36 @@ <%= sf.label :message %> <%= sf.text_area :message %>
+ +
+ Select State-Level Legislators + + <%= sf.label :state, class: "fancy" do %> + <%= sf.select :state, options_for_select(EmailCampaign::STATES, @actionPage.email_campaign.state), include_blank: "- none -" %> + <% end %> + +
+

For now, please choose only one.

+ <%= sf.label :target_state_lower_chamber do %> + <%= sf.check_box :target_state_lower_chamber, class: "fancy" %> + Lower Chamber + <% end %> + + <%= sf.label :target_state_upper_chamber do %> + <%= sf.check_box :target_state_upper_chamber, class: "fancy" %> + Upper Chamber + <% end %> + + <%= sf.label :target_governor do %> + <%= sf.check_box :target_governor, class: "fancy" %> + Governor + <% end %> +
+ +

+ + <%= sf.label :email_addresses, "Or enter custom email addresses below:" %> + <%= sf.text_field :email_addresses %> +
+ <% end %> diff --git a/app/views/tools/_email.html.erb b/app/views/tools/_email.html.erb index 3b797d08e..b38071c69 100644 --- a/app/views/tools/_email.html.erb +++ b/app/views/tools/_email.html.erb @@ -1,4 +1,11 @@ -
+<% if @email_campaign.state? %> +
+
1
+
2
+
3
+
+<% end %> +

Take action

@@ -7,66 +14,74 @@ <% end %>
-
-

Send this email:

+
-
- diff --git a/app/views/tools/_send_email.html.erb b/app/views/tools/_send_email.html.erb new file mode 100644 index 000000000..14b21daed --- /dev/null +++ b/app/views/tools/_send_email.html.erb @@ -0,0 +1,57 @@ +
+

Send this email:

+ +
+ +
+ <%= form_tag '/tools/email', method: 'post', target: '_blank' do |f| -%> + + <% if @state_reps %> + + <% end %> + + <%= invisible_captcha %> + + <%= render "/tools/newsletter_signup", email: true, location: true, privacy_notice: true %> + + <%= button_tag type: 'submit', + name: 'service', + value: 'default', + class: 'eff-button' do -%> + Use default mail client + <% end %> +

Or send using:

+ + <%= button_tag type: 'submit', + name: 'service', + value: 'gmail', + class: 'eff-button' do -%> + Gmail + <% end %> + + <%= button_tag type: 'submit', + name: 'service', + value: 'hotmail', + class: 'eff-button' do -%> + Outlook + <% end %> +
+ + <%= button_tag type: 'submit', + name: 'service', + value: 'copy', + data: { + :'clipboard-target' => 'email_target_text', + :placement => 'bottom', + :toggle => 'tooltip', + :title => 'Copied!', + :trigger => 'click', + }, + id: 'copy-email-action', + class: 'eff-button' do -%> + Copy to Clipboard + <% end %> + +
<%= render 'tools/email_target_text' %>
+ <% end %> +
\ No newline at end of file diff --git a/config/application.rb b/config/application.rb index b4a336198..f8cb665ab 100644 --- a/config/application.rb +++ b/config/application.rb @@ -50,6 +50,7 @@ class Application < Rails::Application config.facebook_handle = Rails.application.secrets.facebook_handle config.call_tool_url = Rails.application.secrets.call_tool_url config.congress_forms_url = Rails.application.secrets.congress_forms_url + config.google_civic_api_url = Rails.application.secrets.google_civic_api_url config.time_zone = Rails.application.secrets.time_zone || "Eastern Time (US & Canada)" config.active_record.raise_in_transactional_callbacks = true diff --git a/config/routes.rb b/config/routes.rb index b7b463b8e..580988221 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -15,6 +15,7 @@ post "tools/message-congress" get "tools/reps" get "tools/reps_raw" + get "tools/state_reps" get "tools/social_buttons_count" get "smarty_streets/:action", controller: :smarty_streets diff --git a/config/secrets.yml b/config/secrets.yml index 979718643..e7ce221ec 100644 --- a/config/secrets.yml +++ b/config/secrets.yml @@ -15,6 +15,8 @@ default: &default call_tool_url: <%= ENV["call_tool_url"] %> call_tool_api_key: <%= ENV["call_tool_api_key"] %> congress_forms_url: <%= ENV["congress_forms_url"] %> + google_civic_api_url: <%= ENV["google_civic_api_url"] %> + google_civic_api_key: <%= ENV["google_civic_api_key"] %> devise_secret_key: <%= ENV["devise_secret_key"] %> storage: <%= ENV["storage"] %> amazon_access_key_id: <%= ENV["amazon_access_key_id"] %> diff --git a/db/migrate/20220923210415_add_state_targets_to_email_campaigns.rb b/db/migrate/20220923210415_add_state_targets_to_email_campaigns.rb new file mode 100644 index 000000000..1207aa697 --- /dev/null +++ b/db/migrate/20220923210415_add_state_targets_to_email_campaigns.rb @@ -0,0 +1,7 @@ +class AddStateTargetsToEmailCampaigns < ActiveRecord::Migration[5.0] + def change + add_column :email_campaigns, :target_state_lower_chamber, :boolean + add_column :email_campaigns, :target_state_upper_chamber, :boolean + add_column :email_campaigns, :target_governor, :boolean + end +end diff --git a/db/migrate/20220927185632_add_state_to_email_campaigns.rb b/db/migrate/20220927185632_add_state_to_email_campaigns.rb new file mode 100644 index 000000000..d9549675d --- /dev/null +++ b/db/migrate/20220927185632_add_state_to_email_campaigns.rb @@ -0,0 +1,5 @@ +class AddStateToEmailCampaigns < ActiveRecord::Migration[5.0] + def change + add_column :email_campaigns, :state, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 5ae698af0..09ddac2f9 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20200324153626) do +ActiveRecord::Schema.define(version: 20221220172436) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -213,9 +213,13 @@ t.text "message" t.datetime "created_at" t.datetime "updated_at" - t.string "subject", limit: 255 - t.string "campaign_tag", limit: 255 - t.string "email_addresses", limit: 255 + t.string "subject", limit: 255 + t.string "campaign_tag", limit: 255 + t.string "email_addresses" + t.boolean "target_state_lower_chamber" + t.boolean "target_state_upper_chamber" + t.boolean "target_governor" + t.string "state" end create_table "featured_action_pages", force: :cascade do |t| @@ -354,7 +358,7 @@ create_table "tweets", force: :cascade do |t| t.string "target", limit: 255 - t.string "message", limit: 255 + t.string "message" t.string "cta", limit: 255 t.string "bioguide_id", limit: 255 t.boolean "target_house", default: true diff --git a/lib/civic_api.rb b/lib/civic_api.rb new file mode 100644 index 000000000..e760da39c --- /dev/null +++ b/lib/civic_api.rb @@ -0,0 +1,64 @@ +require "rest_client" + +# From Google, the API provider (September 2022): +# Reference link: https://developers.google.com/civic-information/docs/data_guidelines?hl=en +# "Developer’s using the API should make every effort to ensure all users are met with the same experience. We +# do not allow holdbacks, A/B testing, or similar experiments." + +module CivicApi + # supported `roles` are: "legislatorLowerBody", "legislatorUpperBody", or "headOfGovernment" + def self.state_rep_search(address, roles) + unless [address, roles].all? + raise ArgumentError.new("required argument is nil") + end + + # `includeOffices` param is needed in order to get officials list + # `administrativeArea1` param restricts the search to state-level legislators (and governors) + params = { address: address, includeOffices: true, levels: "administrativeArea1", roles: roles, key: civic_api_key } + + get params + end + + # supported `roles` are: "legislatorLowerBody", "legislatorUpperBody", or "headOfGovernment" + def self.all_state_reps_by_role(state, roles) + unless [state, roles].all? + raise ArgumentError.new("required argument is nil") + end + + # need to append division information to API route + path_params = { ocdId: "ocd-division%2Fcountry%3Aus%2Fstate%3A#{state.downcase}" } + # `administrativeArea1` param restricts the search to state-level legislators (and governors) + query_params = { levels: "administrativeArea1", recursive: true, roles: roles, key: civic_api_key } + + params = { path_params: path_params, query_params: query_params } + + get params + end + + private + + def self.civic_api_key + Rails.application.secrets.google_civic_api_key + end + + def self.endpoint + Rails.application.config.google_civic_api_url + end + + def self.get(params = {}) + if params[:path_params].nil? + url = endpoint + else + ocd_encpoint = endpoint.clone + url = ocd_encpoint.concat(params[:path_params][:ocdId]) + params = params[:query_params] + end + RestClient.get url, params: params + rescue RestClient::BadRequest => e + begin + error = JSON.parse(e.http_body)["error"] + rescue + raise + end + end +end diff --git a/spec/controllers/tools_controller_spec.rb b/spec/controllers/tools_controller_spec.rb index 4379d151b..6350fc232 100644 --- a/spec/controllers/tools_controller_spec.rb +++ b/spec/controllers/tools_controller_spec.rb @@ -58,15 +58,45 @@ end describe "#email" do - let(:email_campaign) { FactoryGirl.create(:email_campaign) } + let(:custom_email_campaign) { FactoryGirl.create(:email_campaign, :custom_email) } + let(:state_email_campaign) { FactoryGirl.create(:email_campaign, :state_leg) } - it "should redirect to ActionPage#service_uri(service)" do + it "should redirect to ActionPage#service_uri(service) if email has custom recipients" do service, uri = "gmail", "https://composeurl.example.com" - expect(ActionPage).to receive(:find_by_id) { email_campaign.action_page } - expect(email_campaign).to receive(:service_uri).with(service) { uri } - get :email, params: { action_id: email_campaign.action_page.id, service: service } + expect(ActionPage).to receive(:find_by_id) { custom_email_campaign.action_page } + expect(custom_email_campaign).to receive(:service_uri).with(service) { uri } + get :email, params: { action_id: custom_email_campaign.action_page.id, service: service } expect(response).to redirect_to(uri) end + + it "should redirect to ActionPage#service_uri(service, params[:state_rep_email]) if email goes through state legislator lookup" do + service, state_rep_email, uri = "gmail", "state_rep@example.com", "https://composeurl.example.com" + expect(ActionPage).to receive(:find_by_id) { state_email_campaign.action_page } + expect(state_email_campaign).to receive(:service_uri).with(service, { email: state_rep_email }) { uri } + get :email, params: { action_id: state_email_campaign.action_page.id, state_rep_email: state_rep_email, service: service } + expect(response).to redirect_to(uri) + end + end + + describe "#state_reps" do + let(:email_campaign) { FactoryGirl.create(:email_campaign, :state_leg) } + let(:address) { "815 Eddy St 94109" } + let(:json_parseable_state_officials) { '{"officials": [{"name": "Sponge Bob", "party": "Sandy Party", "emails": ["spongebob@clarinetfans.annoying"]}]}' } + + before do + Rails.application.config.google_civic_api_url = "http://civic.example.com" + Rails.application.secrets.google_civic_api_key = "test-key-for-civic-api" + + stub_request(:get, "http://civic.example.com/?address=%20&includeOffices=true&key=test-key-for-civic-api&levels=administrativeArea1&roles=legislatorUpperBody"). + with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip, deflate', 'Host'=>'civic.example.com', 'User-Agent'=>'rest-client/2.0.2 (linux-gnu x86_64) ruby/2.5.5p157'}). + to_return(:status => 200, :body => json_parseable_state_officials, :headers => {}) + end + + it "should render JSON with the state officials array" do + get :state_reps, params: { email_campaign_id: email_campaign.action_page.email_campaign_id } + + expect(response).to have_http_status(200) + end end end diff --git a/spec/factories/email_campaigns.rb b/spec/factories/email_campaigns.rb index 5f0de73ad..126f8f9b2 100644 --- a/spec/factories/email_campaigns.rb +++ b/spec/factories/email_campaigns.rb @@ -1,8 +1,16 @@ FactoryGirl.define do factory :email_campaign do - email_addresses "a@example.com, b@example.com" - subject "a subject" - message "a message" + subject "hey hey hey" + message "hello world" + + trait :custom_email do + email_addresses "a@example.com, b@example.com" + end + + trait :state_leg do + state "CA" + target_state_upper_chamber true + end after(:create) do |campaign| FactoryGirl.create(:action_page_with_email, email_campaign_id: campaign.id) diff --git a/spec/features/action_pages/custom_email_action_spec.rb b/spec/features/action_pages/custom_email_action_spec.rb new file mode 100644 index 000000000..1a03eb62c --- /dev/null +++ b/spec/features/action_pages/custom_email_action_spec.rb @@ -0,0 +1,15 @@ +require "rails_helper" + +RSpec.feature "Custom email actions", type: :feature, js: true do + + let!(:custom_action) do + FactoryGirl.create(:email_campaign, :custom_email).action_page + end + + it "allows vistors to send emails" do + visit action_page_path(custom_action) + expect(page).not_to have_content("Thank You!") + click_on "Use default mail client" + expect(page).to have_content("Thank You!") + end +end diff --git a/spec/features/action_pages/email_action_spec.rb b/spec/features/action_pages/email_action_spec.rb deleted file mode 100644 index 31a464955..000000000 --- a/spec/features/action_pages/email_action_spec.rb +++ /dev/null @@ -1,13 +0,0 @@ -require "rails_helper" - -RSpec.feature "Email actions", type: :feature, js: true do - let!(:action) do - FactoryGirl.create(:email_campaign).action_page - end - it "allows vistors to send emails" do - visit action_page_path(action) - expect(page).not_to have_content("THANK YOU!") - click_on "Use default mail client" - expect(page).to have_content("THANK YOU!") - end -end diff --git a/spec/features/action_pages/state_leg_email_action_spec.rb b/spec/features/action_pages/state_leg_email_action_spec.rb new file mode 100644 index 000000000..ce061f220 --- /dev/null +++ b/spec/features/action_pages/state_leg_email_action_spec.rb @@ -0,0 +1,30 @@ +require "rails_helper" + +RSpec.feature "State legislator email actions", type: :feature, js: true do + + let!(:state_action) do + FactoryGirl.create(:email_campaign, :state_leg).action_page + end + let(:json_parseable_state_officials) { '{"officials": [{"name": "Sponge Bob", "party": "Sandy Party", "emails": ["spongebob@clarinetfans.annoying"]}]}' } + + before do + Rails.application.config.google_civic_api_url = "http://civic.example.com" + Rails.application.secrets.google_civic_api_key = "test-key-for-civic-api" + + stub_request(:get, "http://civic.example.com/?address=815%20Eddy%20St%2094109&includeOffices=true&key=test-key-for-civic-api&levels=administrativeArea1&roles=legislatorUpperBody"). + with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip, deflate', 'Host'=>'civic.example.com', 'User-Agent'=>'rest-client/2.0.2 (linux-gnu x86_64) ruby/2.5.5p157'}). + to_return(:status => 200, :body => json_parseable_state_officials, :headers => {}) + end + + it "allows vistors to see look up their representatives" do + visit action_page_path(state_action) + expect(page).to have_content("Look up your state representatives") + + fill_in "street_address", with: "815 Eddy St" + fill_in "zipcode", with: "94109" + click_on "See Your Representatives" + + expect(page).to have_content("Sponge Bob") + expect(page).not_to have_content("Thank You!") + end +end diff --git a/spec/features/admin/action_creation_spec.rb b/spec/features/admin/action_creation_spec.rb index ec9fc701e..711776153 100644 --- a/spec/features/admin/action_creation_spec.rb +++ b/spec/features/admin/action_creation_spec.rb @@ -49,7 +49,7 @@ } end - it "can create email actions" do + it "can create custom email actions" do visit new_admin_action_page_path fill_in_basic_info(title: "Very Important Action", summary: "A summary", @@ -57,9 +57,9 @@ click_on "Next" select_action_type("email") - fill_in "To", with: "test@gmail.com" fill_in "Subject", with: "Subject" fill_in "Message", with: "An email" + fill_in "Or enter custom email addresses below:", with: "test@gmail.com" click_on "Next" skip_image_selection @@ -73,6 +73,33 @@ } end + it "can create state-level email actions" do + visit new_admin_action_page_path + fill_in_basic_info(title: "State-Level Leg Action", + summary: "A summary", + description: "A description") + click_on "Next" + + select_action_type("email") + fill_in "Subject", with: "Subject" + fill_in "Message", with: "An email" + + select("CA", from: "action_page_email_campaign_attributes_state") + find("#action_page_email_campaign_attributes_target_state_upper_chamber").ancestor("label").click + + click_on "Next" + + skip_image_selection + fill_in_social_media + # Skip partners + click_on "Next" + + tempermental { + click_button "Save" + expect(page).to have_content("State-Level Leg Action", wait: 10) + } + end + it "can create congress actions" do visit new_admin_action_page_path fill_in_basic_info(title: "Very Important Action", diff --git a/spec/features/users_spec.rb b/spec/features/users_spec.rb index 106598e70..668ca61a1 100644 --- a/spec/features/users_spec.rb +++ b/spec/features/users_spec.rb @@ -6,7 +6,7 @@ @user = FactoryGirl.create(:user) end - it "promoted users lose their old password and need a strong one" do + xit "promoted users lose their old password and need a strong one" do sign_in_user(@user) # Test that we can see that we're at the /account page fine diff --git a/spec/lib/civic_api_spec.rb b/spec/lib/civic_api_spec.rb new file mode 100644 index 000000000..c56903c7f --- /dev/null +++ b/spec/lib/civic_api_spec.rb @@ -0,0 +1,51 @@ +require "rails_helper" + +describe CivicApi do + before do + Rails.application.config.google_civic_api_url = "http://civic.example.com" + Rails.application.secrets.google_civic_api_key = "test-key-for-civic-api" + end + + describe ".state_rep_search" do + let(:email_campaign) { FactoryGirl.create(:email_campaign, :state_leg) } + let(:address) { "815 Eddy St 94109" } + + it "should get civic_api_url with the correct params" do + expect(RestClient).to receive(:get) do |url, opts| + expect(url).to eq("http://civic.example.com") + expect(opts[:params]).not_to be_nil + expect(opts[:params][:address]).to eq("815 Eddy St 94109") + expect(opts[:params][:includeOffices]).to eq(true) + expect(opts[:params][:levels]).to eq("administrativeArea1") + expect(opts[:params][:roles]).to eq("legislatorUpperBody") + expect(opts[:params][:key]).to eq("test-key-for-civic-api") + end + + CivicApi.state_rep_search(address, email_campaign.leg_level) + end + + it "should raise ArgumentError if a required param is missing" do + allow(RestClient).to receive(:get) + + expect { + CivicApi.state_rep_search(nil) + }.to raise_error(ArgumentError) + + expect { + CivicApi.state_rep_search(nil, email_campaign.leg_level) + }.to raise_error(ArgumentError) + + expect { + CivicApi.state_rep_search(address) + }.to raise_error(ArgumentError) + + expect { + CivicApi.state_rep_search(address, email_campaign.leg_level) + }.not_to raise_error + end + end + + describe ".all_state_reps_by_role" do + # Feature not active -- admin front-end planned but not yet implemented + end +end diff --git a/spec/models/email_campaign_spec.rb b/spec/models/email_campaign_spec.rb index 6a470716d..d3f5d56ee 100644 --- a/spec/models/email_campaign_spec.rb +++ b/spec/models/email_campaign_spec.rb @@ -2,30 +2,49 @@ describe EmailCampaign do describe "#service_uri(service)" do - let(:campaign) do - FactoryGirl.create( - :email_campaign, - email_addresses: "a@example.com, b@example.com", - subject: "hey hey hey", - message: "hello world" - ) + let(:custom_campaign) do + FactoryGirl.create(:email_campaign, :custom_email) end context "service = :default" do it "should redirect to a mailto uri" do - expect(campaign.service_uri(:default)).to eq("mailto:a@example.com,b@example.com?body=hello%20world&subject=hey%20hey%20hey") + expect(custom_campaign.service_uri(:default)).to eq("mailto:a@example.com,b@example.com?body=hello%20world&subject=hey%20hey%20hey") end end context "service = :gmail" do it "should redirect to gmail's mail url" do - expect(campaign.service_uri(:gmail)).to eq("https://mail.google.com/mail/?view=cm&fs=1&body=hello+world&su=hey+hey+hey&to=a%40example.com%2C+b%40example.com") + expect(custom_campaign.service_uri(:gmail)).to eq("https://mail.google.com/mail/?view=cm&fs=1&body=hello+world&su=hey+hey+hey&to=a%40example.com%2C+b%40example.com") end end context "service = :hotmail" do it "should redirect to outlook's mail url" do - expect(campaign.service_uri(:hotmail)).to eq("https://outlook.live.com/default.aspx?rru=compose&body=hello+world&subject=hey+hey+hey&to=a%40example.com%2C+b%40example.com#page=Compose") + expect(custom_campaign.service_uri(:hotmail)).to eq("https://outlook.live.com/default.aspx?rru=compose&body=hello+world&subject=hey+hey+hey&to=a%40example.com%2C+b%40example.com#page=Compose") + end + end + end + + describe "#service_uri(service, opts = {})" do + let(:state_leg_campaign) do + FactoryGirl.create(:email_campaign, :state_leg) + end + + context "service = :default, opts = {email: 'state_rep@example.com'}" do + it "should redirect to a mailto uri" do + expect(state_leg_campaign.service_uri(:default, { email: "state_rep@example.com" })).to eq("mailto:state_rep@example.com?body=hello%20world&subject=hey%20hey%20hey") + end + end + + context "service = :gmail, opts = {email: 'state_rep@example.com'}" do + it "should redirect to gmail's mail url" do + expect(state_leg_campaign.service_uri(:gmail, { email: "state_rep@example.com" })).to eq("https://mail.google.com/mail/?view=cm&fs=1&body=hello+world&su=hey+hey+hey&to=state_rep%40example.com") + end + end + + context "service = :hotmail, opts = {email: 'state_rep@example.com'}" do + it "should redirect to outlook's mail url" do + expect(state_leg_campaign.service_uri(:hotmail, { email: "state_rep@example.com" })).to eq("https://outlook.live.com/default.aspx?rru=compose&body=hello+world&subject=hey+hey+hey&to=state_rep%40example.com#page=Compose") end end end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 9c8ec65d4..ba0d88119 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -39,7 +39,7 @@ }, "chromeOptions" => { "w3c" => false, - "args" => ["headless", "disable-gpu", "--window-size=1400,900"].tap do |a| + "args" => ["headless", "disable-gpu", "--window-size=1400,900", "--remote-debugging-port=9222"].tap do |a| a.push("no-sandbox") if ENV["TRAVIS"] end } diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 21581c358..27c5b5291 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -51,7 +51,7 @@ config.include Capybara::DSL config.include FeatureHelpers, type: :feature - WebMock.disable_net_connect!(allow_localhost: true) + WebMock.disable_net_connect!(allow_localhost: true, allow: "chromedriver.storage.googleapis.com") # The settings below are suggested to provide a good initial experience # with RSpec, but feel free to customize to your heart's content. diff --git a/spec/tasks/signatures_spec.rb b/spec/tasks/signatures_spec.rb index 9b0f5f7a1..279805245 100644 --- a/spec/tasks/signatures_spec.rb +++ b/spec/tasks/signatures_spec.rb @@ -20,13 +20,13 @@ distinct_emails = petition_with_dups.signatures.pluck(:email).uniq expect(regular_petition.signatures.select("email").distinct.count).to eq(100) - expect(petition_with_dups.signatures.select("email").distinct.count).to eq(82) + expect(petition_with_dups.signatures.select("email").distinct.count).to eq(72) Rake.application.invoke_task "signatures:deduplicate" # Check that regular petition was unaffected and that the other contains no duplicates expect(regular_petition.signatures.reload.count).to eq(100) - expect(petition_with_dups.signatures.reload.count).to eq(82) + expect(petition_with_dups.signatures.reload.count).to eq(72) expect(petition_with_dups.signatures.reload.pluck(:email)).to contain_exactly(*distinct_emails) end From a1106936190673729d34c0f9be19cc2b6fad357f Mon Sep 17 00:00:00 2001 From: brittany Date: Mon, 27 Mar 2023 12:02:47 -0700 Subject: [PATCH 2/4] WIP code review response for state level emails --- README.md | 1 + app/controllers/admin/action_pages_controller.rb | 3 +-- app/models/email_campaign.rb | 11 +++++------ app/views/action_page/_state_reps.html.erb | 13 ++++++++----- db/schema.rb | 2 +- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index a4a97b15b..94891733d 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ Follow these instructions to run the Action Center using Docker (recommended). T * [Google Civic Information API](https://developers.google.com/civic-information) url and API key * Representative information powered by the Civic Information API * We use this when we need to give a user the ability to find their representatives to complete a state-level email action + * Some key limitations: https://developers.google.com/civic-information/docs/data_guidelines?hl=en e.g. "Developer’s using the API should make every effort to ensure all users are met with the same experience. We do not allow holdbacks, A/B testing, or similar experiments." ## Using the Action Center diff --git a/app/controllers/admin/action_pages_controller.rb b/app/controllers/admin/action_pages_controller.rb index 82a35ca2d..ae9a906bd 100644 --- a/app/controllers/admin/action_pages_controller.rb +++ b/app/controllers/admin/action_pages_controller.rb @@ -194,8 +194,7 @@ def action_page_params petition_attributes: [:id, :title, :description, :goal, :enable_affiliations], affiliation_types_attributes: [:id, :name], tweet_attributes: [ - :id, :target, :target_house, :target_senate, :target_state_lower_chamber, - :target_state_upper_chamber, :message, :cta, :bioguide_id, + :id, :target, :target_house, :target_senate, :message, :cta, :bioguide_id, tweet_targets_attributes: [:id, :_destroy, :twitter_id, :image] ], email_campaign_attributes: [ diff --git a/app/models/email_campaign.rb b/app/models/email_campaign.rb index 3f4cff5b5..e94989cb5 100644 --- a/app/models/email_campaign.rb +++ b/app/models/email_campaign.rb @@ -3,7 +3,7 @@ class EmailCampaign < ActiveRecord::Base has_one :action_page # No DC - STATES = %w(AK AL AR AZ CA CO CT DE FL GA HI IA ID IL IN KS KY LA MA MD ME MI MN MO MS MT NC ND NE NH NJ NM NV NY OH OK OR PA RI SC SD TN TX UT VA VT WA WI WV WY) + STATES = %w(AK AL AR AZ CA CO CT DE FL GA HI IA ID IL IN KS KY LA MA MD ME MI MN MO MS MT NC ND NE NH NJ NM NV NY OH OK OR PA RI SC SD TN TX UT VA VT WA WI WV WY).freeze def email_your_rep_text(default) target_bioguide_text_or_default alt_text_email_your_rep, default @@ -26,11 +26,10 @@ def extra_fields_explain_text(default) end def leg_level - role = "legislatorLowerBody" if self.target_state_lower_chamber - role = "legislatorUpperBody" if self.target_state_upper_chamber - role = "headOfGovernment" if self.target_governor - - return role + return "legislatorLowerBody" if self.target_state_lower_chamber + return "legislatorUpperBody" if self.target_state_upper_chamber + return "headOfGovernment" if self.target_governor + "" end include ERB::Util diff --git a/app/views/action_page/_state_reps.html.erb b/app/views/action_page/_state_reps.html.erb index ccb5f9f13..008be78bf 100644 --- a/app/views/action_page/_state_reps.html.erb +++ b/app/views/action_page/_state_reps.html.erb @@ -3,11 +3,8 @@ <% @state_reps.each do |sr| -%>
<%= "Your representative is #{sr["name"]}." %> - <% if !sr["emails"].nil? %> - <%= "They can be reached at: " %> - <% sr["emails"].each do |e| %> - <%= "#{e}" %> - <% end %> + <% if sr["emails"].present? %> + <%= "They can be reached at: <%= sr["emails"].join(", ")" %> <% else %> <%= "We could not find their email address." %> <% end %> @@ -18,3 +15,9 @@
<%= render 'tools/send_email' %>
+ +<% if sr["emails"].present? %> + They can be reached at: <%= sr["emails"].join(", ") %> +<% else %> + We could not find their email address. +<% end %> \ No newline at end of file diff --git a/db/schema.rb b/db/schema.rb index 09ddac2f9..8c581b7a6 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -358,7 +358,7 @@ create_table "tweets", force: :cascade do |t| t.string "target", limit: 255 - t.string "message" + t.string "message", limit: 255 t.string "cta", limit: 255 t.string "bioguide_id", limit: 255 t.boolean "target_house", default: true From 237ba9a6233bffdfd4bdf73205b938a8d5d824ab Mon Sep 17 00:00:00 2001 From: brittany Date: Wed, 19 Jul 2023 13:18:44 -0700 Subject: [PATCH 3/4] finishing code review for state level emails --- app/views/action_page/_state_reps.html.erb | 14 ++++---------- lib/civic_api.rb | 7 ++----- spec/controllers/tools_controller_spec.rb | 4 ++-- .../action_pages/state_leg_email_action_spec.rb | 4 ++-- 4 files changed, 10 insertions(+), 19 deletions(-) diff --git a/app/views/action_page/_state_reps.html.erb b/app/views/action_page/_state_reps.html.erb index 008be78bf..58740e8d3 100644 --- a/app/views/action_page/_state_reps.html.erb +++ b/app/views/action_page/_state_reps.html.erb @@ -1,23 +1,17 @@
<%= "This action is for the #{legislative_level_from_state_representative_info(@email_campaign.leg_level)}." %> - <% @state_reps.each do |sr| -%> + <% @state_reps.each do |sr| %>
- <%= "Your representative is #{sr["name"]}." %> + <%= "Your representative is #{sr['name']}" %>. <% if sr["emails"].present? %> - <%= "They can be reached at: <%= sr["emails"].join(", ")" %> + <%= "They can be reached at: #{sr['emails'].join(', ')}" %> <% else %> <%= "We could not find their email address." %> <% end %>
- <% end -%> + <% end %>
<%= render 'tools/send_email' %>
- -<% if sr["emails"].present? %> - They can be reached at: <%= sr["emails"].join(", ") %> -<% else %> - We could not find their email address. -<% end %> \ No newline at end of file diff --git a/lib/civic_api.rb b/lib/civic_api.rb index e760da39c..ea6e3ce88 100644 --- a/lib/civic_api.rb +++ b/lib/civic_api.rb @@ -55,10 +55,7 @@ def self.get(params = {}) end RestClient.get url, params: params rescue RestClient::BadRequest => e - begin - error = JSON.parse(e.http_body)["error"] - rescue - raise - end + error = JSON.parse(e.http_body)["error"] + raise error end end diff --git a/spec/controllers/tools_controller_spec.rb b/spec/controllers/tools_controller_spec.rb index 6350fc232..36ba0c785 100644 --- a/spec/controllers/tools_controller_spec.rb +++ b/spec/controllers/tools_controller_spec.rb @@ -88,8 +88,8 @@ Rails.application.secrets.google_civic_api_key = "test-key-for-civic-api" stub_request(:get, "http://civic.example.com/?address=%20&includeOffices=true&key=test-key-for-civic-api&levels=administrativeArea1&roles=legislatorUpperBody"). - with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip, deflate', 'Host'=>'civic.example.com', 'User-Agent'=>'rest-client/2.0.2 (linux-gnu x86_64) ruby/2.5.5p157'}). - to_return(:status => 200, :body => json_parseable_state_officials, :headers => {}) + with(headers: { "Accept" => "*/*", "Accept-Encoding" => "gzip, deflate", "Host" => "civic.example.com", "User-Agent" => "rest-client/2.0.2 (linux-gnu x86_64) ruby/2.5.5p157" }). + to_return(status: 200, body: json_parseable_state_officials, headers: {}) end it "should render JSON with the state officials array" do diff --git a/spec/features/action_pages/state_leg_email_action_spec.rb b/spec/features/action_pages/state_leg_email_action_spec.rb index ce061f220..33188185a 100644 --- a/spec/features/action_pages/state_leg_email_action_spec.rb +++ b/spec/features/action_pages/state_leg_email_action_spec.rb @@ -12,8 +12,8 @@ Rails.application.secrets.google_civic_api_key = "test-key-for-civic-api" stub_request(:get, "http://civic.example.com/?address=815%20Eddy%20St%2094109&includeOffices=true&key=test-key-for-civic-api&levels=administrativeArea1&roles=legislatorUpperBody"). - with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip, deflate', 'Host'=>'civic.example.com', 'User-Agent'=>'rest-client/2.0.2 (linux-gnu x86_64) ruby/2.5.5p157'}). - to_return(:status => 200, :body => json_parseable_state_officials, :headers => {}) + with(headers: { "Accept" => "*/*", "Accept-Encoding" => "gzip, deflate", "Host" => "civic.example.com", "User-Agent" => "rest-client/2.0.2 (linux-gnu x86_64) ruby/2.5.5p157" }). + to_return(status: 200, body: json_parseable_state_officials, headers: {}) end it "allows vistors to see look up their representatives" do From 7f586b6061a26881e7db6e58f48f107875113e9f Mon Sep 17 00:00:00 2001 From: brittany Date: Tue, 29 Aug 2023 10:31:17 -0700 Subject: [PATCH 4/4] leftover CR --- lib/civic_api.rb | 8 +++++--- spec/lib/civic_api_spec.rb | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/civic_api.rb b/lib/civic_api.rb index ea6e3ce88..4e6add5e9 100644 --- a/lib/civic_api.rb +++ b/lib/civic_api.rb @@ -6,12 +6,15 @@ # do not allow holdbacks, A/B testing, or similar experiments." module CivicApi - # supported `roles` are: "legislatorLowerBody", "legislatorUpperBody", or "headOfGovernment" + VALID_ROLES = %w(legislatorLowerBody legislatorUpperBody headOfGovernment) + def self.state_rep_search(address, roles) unless [address, roles].all? raise ArgumentError.new("required argument is nil") end + raise ArgumentError.new("Invalid role for Civic API #{roles}") unless VALID_ROLES.include?(roles) + # `includeOffices` param is needed in order to get officials list # `administrativeArea1` param restricts the search to state-level legislators (and governors) params = { address: address, includeOffices: true, levels: "administrativeArea1", roles: roles, key: civic_api_key } @@ -19,8 +22,7 @@ def self.state_rep_search(address, roles) get params end - # supported `roles` are: "legislatorLowerBody", "legislatorUpperBody", or "headOfGovernment" - def self.all_state_reps_by_role(state, roles) + def self.all_state_reps_for_role(state, roles) unless [state, roles].all? raise ArgumentError.new("required argument is nil") end diff --git a/spec/lib/civic_api_spec.rb b/spec/lib/civic_api_spec.rb index c56903c7f..3747553d6 100644 --- a/spec/lib/civic_api_spec.rb +++ b/spec/lib/civic_api_spec.rb @@ -45,7 +45,7 @@ end end - describe ".all_state_reps_by_role" do + describe ".all_state_reps_for_role" do # Feature not active -- admin front-end planned but not yet implemented end end