Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

236 simple api #241

Open
wants to merge 34 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
1ad6247
Update minitest to version 5.8.2
sferik Oct 29, 2015
900eae4
Update webmock to version 1.22.2
sferik Oct 29, 2015
43f7697
Create Adopted controller for API, and wrote initial auth/authorizati…
May 1, 2017
3b65084
Add API route, and setting to allow for basic_auth
May 1, 2017
69d6af7
Forgot to add the helper file
May 1, 2017
b1b4305
Edited authentication controller so that a session isn't created on t…
May 2, 2017
91b0e3b
Adopted Drains API controller test
May 2, 2017
7371587
Trying to fix Travis CI errors
May 2, 2017
15fae71
Added formatting for different content types, and edited fields to be…
May 2, 2017
7287643
(Finally realized I can run Rubocop on my local machine)
May 2, 2017
160a045
More controller tests
May 2, 2017
0b4dab3
Add Faker gem in order to make sample users
Jun 20, 2017
ceca853
Adding some user seeds
Jun 20, 2017
b8fff22
Added a rake task for DEV so mock users can automatically adopt drain…
Jul 24, 2017
88a33a7
Passing rubocop
Jul 24, 2017
b893346
Changing !nil to nil... !nil evaluates to true
Jul 25, 2017
973efac
Initial comments for cursor funcionality
Jul 26, 2017
5f868d1
Adding Kaminari gem for pagination
Aug 14, 2017
5772058
added pagination functionality to API
Aug 14, 2017
da2f4b1
Made is so that a CSV template is rendered with all results rather th…
Aug 14, 2017
01c0719
auto_adopt task comment for further clarification
Aug 14, 2017
343a600
Updated dev-envt config file
Dec 15, 2017
78c0c75
temporarily changed asset host
Dec 15, 2017
cfeba19
Added a total_pages variable, changed default page to 1, and a adopte…
Dec 15, 2017
417c95f
Updating versions
Dec 16, 2017
2953cf3
Removed format options - JSON is the only format available now
Dec 16, 2017
e24cea2
Updated gem versions
Dec 16, 2017
dcc6277
Change local_time to local-time... (I saw it was already changed, but…
Dec 16, 2017
e66a624
Updated pagination defaults
Dec 16, 2017
4032d8d
Uncommenting so I could add drains
Dec 16, 2017
c375680
Making this clearer, and printing out info
Dec 16, 2017
ea959a8
Response data tests for pagination counts and drain data
Dec 16, 2017
57f6f42
Fixed a lot of merge conflicts from prod... And made changes to pass …
Dec 16, 2017
739d86c
Merge remote-tracking branch 'origin/master' into 236-simple-api
jszwedko Jan 6, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ ruby '2.2.3'

gem 'airbrake', '~> 7.1'
gem 'devise', '~> 3.0'
gem 'faker', '~> 1.7.0'
gem 'geokit', '~> 1.0'
gem 'haml', '~> 5.0'
gem 'http_accept_language', '~> 2.0'
gem 'kaminari', '~> 1.0'
gem 'local_time', '~> 2.0'
gem 'obscenity', '~> 1.0', '>= 1.0.2'
gem 'pg'
Expand Down
4 changes: 4 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ GEM
railties (>= 3.2, < 5.2)
erubis (2.7.0)
execjs (2.7.0)
faker (1.7.3)
i18n (~> 0.5)
ffi (1.9.18)
font-awesome-rails (4.7.0.2)
railties (>= 3.2, < 5.2)
Expand Down Expand Up @@ -257,9 +259,11 @@ DEPENDENCIES
coveralls
devise (~> 3.0)
dotenv-rails
faker (~> 1.7.0)
geokit (~> 1.0)
haml (~> 5.0)
http_accept_language (~> 2.0)
kaminari (~> 1.0)
local_time (~> 2.0)
obscenity (~> 1.0, >= 1.0.2)
paranoia (~> 2.4)
Expand Down
2 changes: 2 additions & 0 deletions app/assets/javascripts/adopted.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.
3 changes: 3 additions & 0 deletions app/assets/stylesheets/adopted.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Place all the styles related to the Adopted controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
46 changes: 46 additions & 0 deletions app/controllers/adopted_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
class AdoptedController < ApplicationController
before_action :authenticate
before_action :adopted_things, :make_cur_page, :make_other_pages, only: [:index]

# GET /api/v1/drains/adopted
# Optional params:
#
# page
def index
@results = {next_page: @next_page, prev_page: @prev_page, total_pages: @adopted_things.page(1).total_pages, drains: @things}
render json: @results
end

private

def adopted_things
@adopted_things = Thing.where.not(user_id: nil)
end

# Determine if the user supplied a valid page number, if not they get first page
def make_cur_page
page = params[:page].blank? || params[:page].to_i.zero? ? 1 : params[:page]
@cur_page = @adopted_things.page(page)
@things = format_fields(@cur_page)
end

# Determine next and previous pages, so the user can navigate if needed
def make_other_pages
@next_page = @cur_page.next_page.nil? ? -1 : @cur_page.next_page
@prev_page = @cur_page.prev_page.nil? ? -1 : @cur_page.prev_page
end

def format_fields(obj)
obj.map { |thing| {latitude: thing.lat, longitude: thing.lng, city_id: 'N-' + thing.city_id.to_s} }
end

def authenticate
authenticate_or_request_with_http_basic('Administration') do |username, password|
user = User.find_by(email: username)
if user && user.valid_password?(password)
return true if user.admin?
render html: '<div> You must be an admin to access this page </div>'
end
end
end
end
2 changes: 2 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require 'csv'

class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
Expand Down
2 changes: 2 additions & 0 deletions app/helpers/adopted_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module AdoptedHelper
end
5 changes: 5 additions & 0 deletions app/views/adopted/index.csv.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<%- headers = ['Lat', 'Lng', 'City ID'] -%>
<%= CSV.generate_line headers %>
<%- @allthings.each do |thing| -%>
<%= CSV.generate_line([thing.lat, thing.lng, "N-" + thing.city_id.to_s]) -%>
<%- end -%>
10 changes: 9 additions & 1 deletion config/environments/development.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,15 @@
config.cache_classes = false

# asset host
config.action_controller.asset_host = 'http://' + Socket.ip_address_list[1].ip_address + ':3000'

# There are two cases for dev asset_host to be aware of:
#
# "localhost"/127.0.0.1 ... when developing locally
# remote domainname/IP ... when developing on a remote server
this_server = Socket.ip_address_list[1]
this_server_hostname = this_server.getnameinfo[0] == 'localhost' ? 'localhost' : this_server.ip_address
config.action_controller.asset_host = 'http://' + this_server_hostname + ':3000'

config.action_mailer.asset_host = config.action_controller.asset_host

# Do not eager load code on boot.
Expand Down
2 changes: 1 addition & 1 deletion config/initializers/devise.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
# given strategies, for example, `config.http_authenticatable = [:database]` will
# enable it only for database authentication. The supported strategies are:
# :database = Support basic authentication with authentication key + password
# config.http_authenticatable = false
config.http_authenticatable = false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this uncommented?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never mind, I see that the API is locked to authenticated users.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool


# If http headers should be returned for AJAX requests. True by default.
# config.http_authenticatable_on_xhr = true
Expand Down
10 changes: 10 additions & 0 deletions config/initializers/kaminari_config.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Kaminari.configure do |config|
config.default_per_page = 100
config.max_per_page = 1000
# config.window = 4
# config.outer_window = 0
# config.left = 0
# config.right = 0
# config.page_method_name = :page
# config.param_name = :page
end
9 changes: 9 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,13 @@
resource :things
mount RailsAdmin::Engine => '/admin', :as => 'rails_admin'
root to: 'main#index'

# API
scope '/api' do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 to the URL pathing here

scope '/v1' do
scope '/drains' do
get '/adopted' => 'adopted#index'
end
end
end
end
13 changes: 12 additions & 1 deletion db/seeds.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,21 @@

500.times do |i|
Thing.where(city_id: i).first_or_initialize.tap do |thing|
thing.name = "Some Drain #{i}"
thing.name = 'Some Drain #{i}'
thing.lat = r.rand(37.75..37.78)
thing.lng = r.rand(-122.43..-122.41)
thing.system_use_code = ['MS4', 'STORM', 'COMB', 'UNK'].sample
thing.save!
end
end

1000.times do |i|
first_name = Faker::Name.first_name
last_name = Faker::Name.last_name
email = 'user-#{i+1}@usertest.org'
password = 'pass1234'
User.create!(first_name: first_name,
last_name: last_name,
email: email,
password: password)
end
15 changes: 15 additions & 0 deletions lib/tasks/data.rake
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,18 @@ namespace :data do
end
end
end

namespace :change_data do
task auto_adopt: :environment do
if Rails.env.production?
puts "Can't run this in production"
else
Thing.first(1000).each do |t|
if t.user_id.blank?
t.user_id = User.order('RANDOM()').limit(1).first.id
t.save
end
end
end
end
end
58 changes: 58 additions & 0 deletions test/controllers/adopted_controller_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
require 'test_helper'

class AdoptedControllerTest < ActionController::TestCase
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like checking the content-type, but could we make a few assertions on the response content to make sure the right records are returned? It'd also be nice to exercise the paging logic a little here.

setup do
request.env['devise.mapping'] = Devise.mappings[:user]
@user = users(:erik)
@user2 = users(:dan)
@admin = users(:admin)
@thing = things(:thing_1)
@thing2 = things(:thing_2)
@thing.user_id = @user.id
@thing2.user_id = @user2.id
@thing.save
@thing2.save
end

test 'should get index' do
@request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(@user.email, 'correct')

get :index
assert_response :success
end

test 'should get json' do
@request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(@admin.email, 'correct')

get :index
assert_equal 'application/json', @response.content_type
end

test 'only admins get access' do
@request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(@user.email, 'correct')

get :index
assert_equal 'text/html', @response.content_type # If user were an admin, content_type would be JSON, since that is default
end

test 'drain data is correct' do
@request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(@admin.email, 'correct')

get :index
random_drain = JSON.parse(@response.body)['drains'].first
drain = Thing.find_by(city_id: random_drain['city_id'].gsub('N-', ''))
assert_not_nil drain
assert_equal drain.lat.to_s, random_drain['latitude']
assert_equal drain.lng.to_s, random_drain['longitude']
end

test 'page counts' do
@request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(@admin.email, 'correct')

get :index
json = JSON.parse(@response.body)
assert_equal json['next_page'], -1
assert_equal json['prev_page'], -1
assert_equal json['total_pages'], 1
end
end