diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..276cbf9 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +2.3.0 diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..99bab44 --- /dev/null +++ b/Gemfile @@ -0,0 +1,24 @@ +source "https://rubygems.org" + +# Frameworks +gem "grape", "~> 0.14.0" +gem "grape-rabl", "~> 0.4.1" +gem "grape-kaminari", "~> 0.1.8" +gem "sinatra", "~> 1.4.6" + +# Database +gem "pg", "~> 0.18.4" +gem "activerecord", "~> 4.2.5" +gem "activerecord-postgis-adapter", "~> 3.1.3" + +# Support +gem "rake", "~> 10.5.0" +gem "activesupport", "~> 4.2.5" + +# Testing +gem "minitest" +gem "minitest-around", "~> 0.3.2" +gem "database_cleaner", "~> 1.5.1" +gem "factory_girl", "~> 4.0" +gem "rack-test", "~> 0.6.3" + diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..885a6c9 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,142 @@ +GEM + remote: https://rubygems.org/ + specs: + actionpack (4.2.5) + actionview (= 4.2.5) + activesupport (= 4.2.5) + rack (~> 1.6) + rack-test (~> 0.6.2) + rails-dom-testing (~> 1.0, >= 1.0.5) + rails-html-sanitizer (~> 1.0, >= 1.0.2) + actionview (4.2.5) + activesupport (= 4.2.5) + builder (~> 3.1) + erubis (~> 2.7.0) + rails-dom-testing (~> 1.0, >= 1.0.5) + rails-html-sanitizer (~> 1.0, >= 1.0.2) + activemodel (4.2.5) + activesupport (= 4.2.5) + builder (~> 3.1) + activerecord (4.2.5) + activemodel (= 4.2.5) + activesupport (= 4.2.5) + arel (~> 6.0) + activerecord-postgis-adapter (3.1.3) + activerecord (~> 4.2) + rgeo-activerecord (>= 4.0.4) + activesupport (4.2.5) + i18n (~> 0.7) + json (~> 1.7, >= 1.7.7) + minitest (~> 5.1) + thread_safe (~> 0.3, >= 0.3.4) + tzinfo (~> 1.1) + arel (6.0.3) + axiom-types (0.1.1) + descendants_tracker (~> 0.0.4) + ice_nine (~> 0.11.0) + thread_safe (~> 0.3, >= 0.3.1) + builder (3.2.2) + coercible (1.0.0) + descendants_tracker (~> 0.0.1) + database_cleaner (1.5.1) + descendants_tracker (0.0.4) + thread_safe (~> 0.3, >= 0.3.1) + equalizer (0.0.11) + erubis (2.7.0) + factory_girl (4.5.0) + activesupport (>= 3.0.0) + grape (0.14.0) + activesupport + builder + hashie (>= 2.1.0) + multi_json (>= 1.3.2) + multi_xml (>= 0.5.2) + rack (>= 1.3.0) + rack-accept + rack-mount + virtus (>= 1.0.0) + grape-kaminari (0.1.8) + grape + kaminari + grape-rabl (0.4.1) + grape + i18n + rabl + tilt + hashie (3.4.3) + i18n (0.7.0) + ice_nine (0.11.1) + json (1.8.3) + kaminari (0.16.3) + actionpack (>= 3.0.0) + activesupport (>= 3.0.0) + loofah (2.0.3) + nokogiri (>= 1.5.9) + mini_portile2 (2.0.0) + minitest (5.8.3) + minitest-around (0.3.2) + minitest (~> 5.0) + multi_json (1.11.2) + multi_xml (0.5.5) + nokogiri (1.6.7.1) + mini_portile2 (~> 2.0.0.rc2) + pg (0.18.4) + rabl (0.11.8) + activesupport (>= 2.3.14) + rack (1.6.4) + rack-accept (0.4.5) + rack (>= 0.4) + rack-mount (0.8.3) + rack (>= 1.0.0) + rack-protection (1.5.3) + rack + rack-test (0.6.3) + rack (>= 1.0) + rails-deprecated_sanitizer (1.0.3) + activesupport (>= 4.2.0.alpha) + rails-dom-testing (1.0.7) + activesupport (>= 4.2.0.beta, < 5.0) + nokogiri (~> 1.6.0) + rails-deprecated_sanitizer (>= 1.0.1) + rails-html-sanitizer (1.0.2) + loofah (~> 2.0) + rake (10.5.0) + rgeo (0.5.2) + rgeo-activerecord (4.0.5) + activerecord (~> 4.2) + rgeo (~> 0.3) + sinatra (1.4.6) + rack (~> 1.4) + rack-protection (~> 1.4) + tilt (>= 1.3, < 3) + thread_safe (0.3.5) + tilt (2.0.2) + tzinfo (1.2.2) + thread_safe (~> 0.1) + virtus (1.0.5) + axiom-types (~> 0.1) + coercible (~> 1.0) + descendants_tracker (~> 0.0, >= 0.0.3) + equalizer (~> 0.0, >= 0.0.9) + +PLATFORMS + ruby + +DEPENDENCIES + activerecord (~> 4.2.5) + activerecord-postgis-adapter (~> 3.1.3) + activesupport (~> 4.2.5) + database_cleaner (~> 1.5.1) + factory_girl (~> 4.0) + grape (~> 0.14.0) + grape-kaminari (~> 0.1.8) + grape-rabl (~> 0.4.1) + minitest + minitest-around (~> 0.3.2) + pg (~> 0.18.4) + rack-test (~> 0.6.3) + rake (~> 10.5.0) + sinatra (~> 1.4.6) + +BUNDLED WITH + 1.11.2 diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..0190f60 --- /dev/null +++ b/Rakefile @@ -0,0 +1,8 @@ +require 'rake/testtask' + +Rake::TestTask.new do |t| + t.libs += ["test", "."] + t.pattern = "test/**/*_test.rb" +end + +task :default => [:test] diff --git a/api/routes.rb b/api/routes.rb new file mode 100644 index 0000000..88b6804 --- /dev/null +++ b/api/routes.rb @@ -0,0 +1,15 @@ +module API; end +module API::V3; end + +Dir["#{File.dirname(__FILE__)}/**/*.rb"].each {|f| require f} + +module API + class Routes < Grape::API + format :json + formatter :json, Grape::Formatter::Rabl + + version "v3" + mount API::V3::ProtectedAreas + mount API::V3::Countries + end +end diff --git a/api/v3/countries.rb b/api/v3/countries.rb new file mode 100644 index 0000000..5f4c686 --- /dev/null +++ b/api/v3/countries.rb @@ -0,0 +1,24 @@ +require 'models/country' + +class API::V3::Countries < Grape::API + include Grape::Kaminari + + paginate per_page: 25, max_per_page: 50 + params { optional :with_geometry, default: false, type: Boolean } + get "/countries", rabl: "v3/views/countries" do + collection = Country + collection = collection.without_geometry unless params[:with_geometry] + + @with_geometry = params[:with_geometry] + @countries = paginate(collection) + end + + params { optional :with_geometry, default: true, type: Boolean } + get "/countries/:iso_3", rabl: "v3/views/country" do + @with_geometry = params[:with_geometry] + @country = Country.find_by_iso_3( + params[:iso_3] + ) or error!(:not_found, 404) + end +end + diff --git a/api/v3/protected_areas.rb b/api/v3/protected_areas.rb new file mode 100644 index 0000000..4e7ee7d --- /dev/null +++ b/api/v3/protected_areas.rb @@ -0,0 +1,23 @@ +require 'models/protected_area' + +class API::V3::ProtectedAreas < Grape::API + include Grape::Kaminari + + paginate per_page: 25, max_per_page: 50 + params { optional :with_geometry, default: false, type: Boolean } + get "/protected_areas", rabl: "v3/views/protected_areas" do + collection = ProtectedArea + collection = collection.without_geometry unless params[:with_geometry] + + @with_geometry = params[:with_geometry] + @protected_areas = paginate(collection) + end + + params { optional :with_geometry, default: true, type: Boolean } + get "/protected_areas/:wdpa_id", rabl: "v3/views/protected_area" do + @with_geometry = params[:with_geometry] + @protected_area = ProtectedArea.find_by_wdpa_id( + params[:wdpa_id] + ) or error!(:not_found, 404) + end +end diff --git a/api/v3/search_controller.rb b/api/v3/search_controller.rb new file mode 100644 index 0000000..5623414 --- /dev/null +++ b/api/v3/search_controller.rb @@ -0,0 +1,43 @@ +class API::V3::Search < Grape::API + def points + results = Search.search( + params[:q], + search_options(size: ProtectedArea.count) + ).results + + render json: results.with_coords + end + + def by_point + dirty_query = """ + SELECT p.id, p.wdpa_id, p.name, p.the_geom_latitude, p.the_geom_longitude + FROM protected_areas p + WHERE ST_DWithin(p.the_geom, ST_GeomFromText('POINT(? ?)',4326), 0.0000001) + LIMIT 1; + """.squish + + query = ActiveRecord::Base.send(:sanitize_sql_array, [ + dirty_query, params[:lon].to_f, params[:lat].to_f + ]) + + results = db.execute(query) + + render json: results + end + + private + + def db + ActiveRecord::Base.connection + end + + def search_options extra_options + options = {filters: {'type' => 'protected_area'}.merge(filters || {})} + options[:page] = params[:page].to_i if params[:page].present? + options.merge(extra_options) + end + + def filters + params.stringify_keys.slice(*Search::ALLOWED_FILTERS) + end +end diff --git a/api/v3/views/countries.rabl b/api/v3/views/countries.rabl new file mode 100644 index 0000000..91d67a9 --- /dev/null +++ b/api/v3/views/countries.rabl @@ -0,0 +1,2 @@ +collection @countries, root: :countries, object_root: false +extends "v3/views/country" diff --git a/api/v3/views/country.rabl b/api/v3/views/country.rabl new file mode 100644 index 0000000..8756021 --- /dev/null +++ b/api/v3/views/country.rabl @@ -0,0 +1,21 @@ +object @country + +# Basic +attributes :name, :iso_3 +attributes :iso_3 => :id + +# Geometry +attribute :geojson, if: -> (_) { @with_geometry } + +# Relations +child :country_statistic => :statistics do + attributes :pa_area, :percentage_cover_pas, :eez_area, + :ts_area, :pa_land_area, :pa_marine_area, :percentage_pa_land_cover, + :percentage_pa_eez_cover, :percentage_pa_ts_cover, :land_area, :percentage_pa_cover, + :pa_eez_area, :pa_ts_area, :percentage_pa_marine_cover, :marine_area, + :polygons_count, :points_count +end + +child :region do + attributes :name, :iso +end diff --git a/api/v3/views/protected_area.rabl b/api/v3/views/protected_area.rabl new file mode 100644 index 0000000..104c813 --- /dev/null +++ b/api/v3/views/protected_area.rabl @@ -0,0 +1,27 @@ +object @protected_area + +# Basic +attribute :wdpa_id => :id +attributes :name, :original_name, :wdpa_id + +node :marine do |pa| + pa.marine == "t" +end + +# Geometry +attribute :geojson, if: -> (_) { @with_geometry } + +# Relations +child :countries, object_root: false do + attributes :name, :iso_3 + attribute :iso_3 => :id +end +child :sublocations, object_root: false do + attributes :id, :english_name +end +child :iucn_category, object_root: false do + attributes :id, :name +end +child :designation, object_root: false do + attributes :id, :name +end diff --git a/api/v3/views/protected_areas.rabl b/api/v3/views/protected_areas.rabl new file mode 100644 index 0000000..8971b7a --- /dev/null +++ b/api/v3/views/protected_areas.rabl @@ -0,0 +1,3 @@ +collection @protected_areas, root: :protected_areas, object_root: false + +extends "v3/views/protected_area" diff --git a/config.ru b/config.ru new file mode 100644 index 0000000..b821efe --- /dev/null +++ b/config.ru @@ -0,0 +1,22 @@ +$LOAD_PATH.unshift("#{File.dirname(__FILE__)}") + +require 'sinatra' +require 'grape' +require 'grape-rabl' +require 'grape-kaminari' +require 'active_record' +require 'active_support' + +require 'config/rabl' +require 'config/active_record' + +Dir["#{File.dirname(__FILE__)}/models/**/*.rb"].each {|f| require f} + +require 'api/routes' +require 'web/routes' + +use Rack::Config do |env| + env['api.tilt.root'] = "#{File.dirname(__FILE__)}/api" +end + +run Rack::Cascade.new [Web::Routes, API::Routes] diff --git a/config/active_record.rb b/config/active_record.rb new file mode 100644 index 0000000..7e8bef8 --- /dev/null +++ b/config/active_record.rb @@ -0,0 +1,8 @@ +$environment = ENV["API_ENV"] || "development" +db_config = YAML.load_file("config/database.yml")[$environment] + +ActiveRecord::Base.default_timezone = :utc +ActiveRecord::Base.establish_connection(db_config) + +use ActiveRecord::ConnectionAdapters::ConnectionManagement + diff --git a/config/database.yml b/config/database.yml new file mode 100644 index 0000000..2bb6242 --- /dev/null +++ b/config/database.yml @@ -0,0 +1,23 @@ +default: &default + host: localhost + encoding: unicode + adapter: postgis + pool: 5 + timeout: 5000 + username: postgres + database: pp_development + +development: + <<: *default + +test: + <<: *default + database: pp_test + +production: + <<: *default + database: pp_production + +staging: + <<: *default + database: pp_staging diff --git a/config/environment.rb b/config/environment.rb new file mode 100644 index 0000000..27bc260 --- /dev/null +++ b/config/environment.rb @@ -0,0 +1,12 @@ +require 'sinatra' +require 'grape' +require 'grape-rabl' +require 'grape-kaminari' +require 'active_record' +require 'active_support' + +require 'config/rabl' +require 'config/active_record' + +Dir["#{File.dirname(__FILE__)}/../models/**/*.rb"].each {|f| require f} + diff --git a/config/rabl.rb b/config/rabl.rb new file mode 100644 index 0000000..0c59e00 --- /dev/null +++ b/config/rabl.rb @@ -0,0 +1,5 @@ +Rabl.configure do |config| + config.include_json_root = true + config.include_child_root = false +end + diff --git a/models/concerns/geometry_concern.rb b/models/concerns/geometry_concern.rb new file mode 100644 index 0000000..37c3614 --- /dev/null +++ b/models/concerns/geometry_concern.rb @@ -0,0 +1,67 @@ +module GeometryConcern + extend ActiveSupport::Concern + + included do + scope :without_geometry, -> { select(column_names - self.geometry_columns) } + end + + module ClassMethods + def geometry_columns + columns_hash.select { |_,v| v.type == :spatial }.keys + end + end + + def bounds + rgeo_factory = RGeo::Geos.factory srid: 4326 + bounds = RGeo::Cartesian::BoundingBox.new rgeo_factory + bounds.add bounding_box + + [ + [bounds.min_y, bounds.min_x], + [bounds.max_y, bounds.max_x] + ] + end + + def geojson + geojson = ActiveRecord::Base.connection.select_value(""" + SELECT ST_AsGeoJSON(ST_SimplifyPreserveTopology(#{main_geom_column}, 0.003), 3) + FROM #{self.class.table_name} + WHERE id = #{id} + """.squish) + + return nil unless geojson.present? + geometry = JSON.parse(geojson) + + { + "type" => "Feature", + "properties" => geometry_properties, + "geometry" => geometry + } + end + + private + + def geometry_properties + if self.respond_to?(:marine) && marine + { + "fill-opacity" => 0.7, + "stroke-width" => 0.05, + "stroke" => "#2E5387", + "fill" => "#3E7BB6", + "marker-color" => "#2B3146" + } + else + { + "fill-opacity" => 0.7, + "stroke-width" => 0.05, + "stroke" => "#40541b", + "fill" => "#83ad35", + "marker-color" => "#2B3146" + } + end + end + + def main_geom_column + self.respond_to?(:the_geom) ? 'the_geom' : 'bounding_box' + end +end diff --git a/models/country.rb b/models/country.rb new file mode 100644 index 0000000..2082343 --- /dev/null +++ b/models/country.rb @@ -0,0 +1,10 @@ +require 'models/concerns/geometry_concern' + +class Country < ActiveRecord::Base + include GeometryConcern + + belongs_to :region + has_one :country_statistic + has_many :sub_locations + has_many :protected_areas +end diff --git a/models/country_statistic.rb b/models/country_statistic.rb new file mode 100644 index 0000000..3fb5930 --- /dev/null +++ b/models/country_statistic.rb @@ -0,0 +1,3 @@ +class CountryStatistic < ActiveRecord::Base + belongs_to :country +end diff --git a/models/designation.rb b/models/designation.rb new file mode 100644 index 0000000..d4a585f --- /dev/null +++ b/models/designation.rb @@ -0,0 +1,4 @@ +class Designation < ActiveRecord::Base + belongs_to :jurisdiction + has_many :protected_areas +end diff --git a/models/governance.rb b/models/governance.rb new file mode 100644 index 0000000..3d23dda --- /dev/null +++ b/models/governance.rb @@ -0,0 +1,3 @@ +class Governance < ActiveRecord::Base + has_many :protected_areas +end diff --git a/models/iucn_category.rb b/models/iucn_category.rb new file mode 100644 index 0000000..d8fb46a --- /dev/null +++ b/models/iucn_category.rb @@ -0,0 +1,3 @@ +class IucnCategory < ActiveRecord::Base + has_many :protected_areas +end diff --git a/models/jurisdiction.rb b/models/jurisdiction.rb new file mode 100644 index 0000000..14854c6 --- /dev/null +++ b/models/jurisdiction.rb @@ -0,0 +1,3 @@ +class Jurisdiction < ActiveRecord::Base + has_many :designations +end diff --git a/models/legal_status.rb b/models/legal_status.rb new file mode 100644 index 0000000..6273b0d --- /dev/null +++ b/models/legal_status.rb @@ -0,0 +1,3 @@ +class LegalStatus < ActiveRecord::Base + has_many :protected_areas +end diff --git a/models/protected_area.rb b/models/protected_area.rb new file mode 100644 index 0000000..2a5326a --- /dev/null +++ b/models/protected_area.rb @@ -0,0 +1,14 @@ +require 'models/concerns/geometry_concern' + +class ProtectedArea < ActiveRecord::Base + include GeometryConcern + + belongs_to :iucn_category + belongs_to :designation + belongs_to :legal_status + belongs_to :governance + has_and_belongs_to_many :countries, -> { select(:id, :name, :iso_3) } + has_and_belongs_to_many :sub_locations + + delegate :jurisdiction, to: :designation, allow_nil: true +end diff --git a/models/region.rb b/models/region.rb new file mode 100644 index 0000000..25eb71f --- /dev/null +++ b/models/region.rb @@ -0,0 +1,3 @@ +class Region < ActiveRecord::Base + has_many :countries +end diff --git a/models/sub_location.rb b/models/sub_location.rb new file mode 100644 index 0000000..5bd2d8d --- /dev/null +++ b/models/sub_location.rb @@ -0,0 +1,5 @@ +class SubLocation < ActiveRecord::Base + has_and_belongs_to_many :protected_areas + + belongs_to :country +end diff --git a/models/templates/country_index.sql.erb b/models/templates/country_index.sql.erb new file mode 100644 index 0000000..924e02a --- /dev/null +++ b/models/templates/country_index.sql.erb @@ -0,0 +1,11 @@ +SELECT + countries.name, + countries.iso_3, + regions.name AS region_name, + regions.iso AS region_iso, + country_statistics.* +FROM countries + LEFT JOIN country_statistics ON country_statistics.country_id = countries.id + LEFT JOIN regions ON regions.id = countries.region_id +OFFSET <%= page * per_page %> +LIMIT <%= per_page %> diff --git a/models/templates/country_show.sql.erb b/models/templates/country_show.sql.erb new file mode 100644 index 0000000..a09d3c9 --- /dev/null +++ b/models/templates/country_show.sql.erb @@ -0,0 +1,11 @@ +SELECT + countries.name, + countries.iso_3, + country_statistics.*, + regions.name AS region_name, + regions.iso AS region_iso +FROM countries + LEFT JOIN regions ON regions.id = countries.region_id + LEFT JOIN country_statistics ON country_statistics.country_id = countries.id +WHERE countries.iso_3 = '<%= iso_3 %>' +LIMIT 1 diff --git a/models/templates/protected_area_index.sql.erb b/models/templates/protected_area_index.sql.erb new file mode 100644 index 0000000..338787f --- /dev/null +++ b/models/templates/protected_area_index.sql.erb @@ -0,0 +1,23 @@ +SELECT + protected_areas.wdpa_id AS wdpa_id, + protected_areas.name AS name, + ST_AsGeoJSON(ST_SimplifyPreserveTopology(protected_areas.the_geom, 0.00003), 3) AS geojson, + protected_areas.original_name AS original_name, + protected_areas.marine AS marine, + countries.name AS country_name, + countries.iso_3 AS country_iso_3, + sub_locations.id AS sub_location_id, + sub_locations.english_name AS sub_location_english_name, + iucn_categories.id AS iucn_category_id, + iucn_categories.name AS iucn_category_name, + designations.id AS designation_id, + designations.name AS designation_name +FROM protected_areas + LEFT JOIN countries_protected_areas ON protected_areas.id = countries_protected_areas.protected_area_id + LEFT JOIN countries ON countries.id = countries_protected_areas.country_id + LEFT JOIN protected_areas_sub_locations ON protected_areas.id = protected_areas_sub_locations.protected_area_id + LEFT JOIN sub_locations ON sub_locations.id = protected_areas_sub_locations.sub_location_id + LEFT JOIN iucn_categories ON iucn_categories.id = protected_areas.iucn_category_id + LEFT JOIN designations ON designations.id = protected_areas.designation_id +OFFSET <%= page * per_page %> +LIMIT <%= per_page %> diff --git a/models/templates/protected_area_show.sql.erb b/models/templates/protected_area_show.sql.erb new file mode 100644 index 0000000..aabffb8 --- /dev/null +++ b/models/templates/protected_area_show.sql.erb @@ -0,0 +1,23 @@ +SELECT + protected_areas.wdpa_id AS wdpa_id, + protected_areas.name AS name, + ST_AsGeoJSON(ST_SimplifyPreserveTopology(protected_areas.the_geom, 0.00003), 3) AS geojson, + protected_areas.original_name AS original_name, + protected_areas.marine AS marine, + countries.name AS country_name, + countries.iso_3 AS country_iso_3, + sub_locations.id AS sub_location_id, + sub_locations.english_name AS sub_location_english_name, + iucn_categories.id AS iucn_category_id, + iucn_categories.name AS iucn_category_name, + designations.id AS designation_id, + designations.name AS designation_name +FROM protected_areas + LEFT JOIN countries_protected_areas ON protected_areas.id = countries_protected_areas.protected_area_id + LEFT JOIN countries ON countries.id = countries_protected_areas.country_id + LEFT JOIN protected_areas_sub_locations ON protected_areas.id = protected_areas_sub_locations.protected_area_id + LEFT JOIN sub_locations ON sub_locations.id = protected_areas_sub_locations.sub_location_id + LEFT JOIN iucn_categories ON iucn_categories.id = protected_areas.iucn_category_id + LEFT JOIN designations ON designations.id = protected_areas.designation_id +WHERE protected_areas.wdpa_id = '<%= wdpa_id %>' +LIMIT 1 diff --git a/test/api/v3/countries_test.rb b/test/api/v3/countries_test.rb new file mode 100644 index 0000000..8137934 --- /dev/null +++ b/test/api/v3/countries_test.rb @@ -0,0 +1,50 @@ +require "test_helper" +require "api/routes" + +class API::V3::CountriesTest < MiniTest::Test + include Rack::Test::Methods + + EXPECTED_GEOJSON = { + "type" => "Feature", + "properties" => {"fill-opacity" => 0.7, "stroke-width" => 0.05, "stroke" => "#40541b", "fill" => "#83ad35", "marker-color" => "#2B3146"}, + "geometry" => {"type" => "Point", "coordinates" => [-122, 47]} + } + + def app + API::V3::Countries + end + + def test_get_countries_returns_all_countries_as_JSON + 3.times { create(:country) } + get_with_rabl "/v3/countries" + + assert last_response.ok? + assert_equal 3, @json_response["countries"].size + end + + def test_get_countries_with_geometry_true_returns_all_countries_with_geojson + 3.times { create(:country, bounding_box: "POINT(-122 47)") } + get_with_rabl "/v3/countries", {with_geometry: true} + + assert last_response.ok? + assert_equal(EXPECTED_GEOJSON, @json_response["countries"][0]["geojson"]) + end + + def test_get_countries_WHO_returns_country_with_iso_3_WHO + create(:country, name: "Gallifrey", iso_3: "WHO") + get_with_rabl "/v3/countries/WHO" + + assert last_response.ok? + assert_equal("WHO", @json_response["country"]["id"]) + assert_equal("WHO", @json_response["country"]["iso_3"]) + assert_equal("Gallifrey", @json_response["country"]["name"]) + end + + def test_get_countries_WHO_with_geometry_returns_country_WHO_with_geojson + create(:country, name: "Gallifrey", iso_3: "WHO", bounding_box: "POINT(-122 47)") + get_with_rabl "/v3/countries/WHO" + + assert last_response.ok? + assert_equal(EXPECTED_GEOJSON, @json_response["country"]["geojson"]) + end +end diff --git a/test/api/v3/protected_areas_test.rb b/test/api/v3/protected_areas_test.rb new file mode 100644 index 0000000..64bafd7 --- /dev/null +++ b/test/api/v3/protected_areas_test.rb @@ -0,0 +1,51 @@ +require "test_helper" +require "api/routes" + +class API::V3::ProtectedAreasTest < MiniTest::Test + include Rack::Test::Methods + + EXPECTED_GEOJSON = { + "type" => "Feature", + "properties" => {"fill-opacity" => 0.7, "stroke-width" => 0.05, "stroke" => "#40541b", "fill" => "#83ad35", "marker-color" => "#2B3146"}, + "geometry" => {"type" => "Point", "coordinates" => [-122, 47]} + } + + def app + API::V3::ProtectedAreas + end + + def test_get_protected_areas_returns_all_protected_areas_as_JSON + 3.times { create(:protected_area) } + get_with_rabl "/v3/protected_areas" + + assert last_response.ok? + assert_equal 3, @json_response["protected_areas"].size + end + + def test_get_protected_areas_with_geometry_true_returns_all_protected_areas_with_geojson + 3.times { create(:protected_area, the_geom: "POINT(-122 47)") } + get_with_rabl "/v3/protected_areas", {with_geometry: true} + + assert last_response.ok? + assert_equal(EXPECTED_GEOJSON, @json_response["protected_areas"][0]["geojson"]) + end + + def test_get_protected_areas_123_returns_protected_area_with_wdpa_id_123 + create(:protected_area, name: "Bad Wolf", wdpa_id: 123) + get_with_rabl "/v3/protected_areas/123" + + assert last_response.ok? + assert_equal(123, @json_response["protected_area"]["id"]) + assert_equal(123, @json_response["protected_area"]["wdpa_id"]) + assert_equal("Bad Wolf", @json_response["protected_area"]["name"]) + end + + def test_get_protected_areas_123_with_geometry_returns_protected_area_123_with_geojson + create(:protected_area, name: "Bad Wolf", wdpa_id: 123, the_geom: "POINT(-122 47)") + get_with_rabl "/v3/protected_areas/123" + + assert last_response.ok? + assert_equal(EXPECTED_GEOJSON, @json_response["protected_area"]["geojson"]) + end +end + diff --git a/test/factories/countries.rb b/test/factories/countries.rb new file mode 100644 index 0000000..6737f9c --- /dev/null +++ b/test/factories/countries.rb @@ -0,0 +1,9 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :country do + name "MyText" + iso "MyString" + iso_3 "MyString" + end +end diff --git a/test/factories/country_statistic.rb b/test/factories/country_statistic.rb new file mode 100644 index 0000000..0756013 --- /dev/null +++ b/test/factories/country_statistic.rb @@ -0,0 +1,6 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :country_statistic do + end +end \ No newline at end of file diff --git a/test/factories/designations.rb b/test/factories/designations.rb new file mode 100644 index 0000000..3ee8ada --- /dev/null +++ b/test/factories/designations.rb @@ -0,0 +1,8 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :designation do + name "MyString" + association :jurisdiction, factory: :jurisdiction, name: 'My jurisdiction' + end +end diff --git a/test/factories/governances.rb b/test/factories/governances.rb new file mode 100644 index 0000000..c801123 --- /dev/null +++ b/test/factories/governances.rb @@ -0,0 +1,7 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :governance do + name "MyString" + end +end diff --git a/test/factories/iucn_categories.rb b/test/factories/iucn_categories.rb new file mode 100644 index 0000000..981836b --- /dev/null +++ b/test/factories/iucn_categories.rb @@ -0,0 +1,7 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :iucn_category do + name "MyString" + end +end diff --git a/test/factories/jurisdictions.rb b/test/factories/jurisdictions.rb new file mode 100644 index 0000000..7000d57 --- /dev/null +++ b/test/factories/jurisdictions.rb @@ -0,0 +1,7 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :jurisdiction do + name "MyString" + end +end diff --git a/test/factories/legal_statuses.rb b/test/factories/legal_statuses.rb new file mode 100644 index 0000000..2fd9cd2 --- /dev/null +++ b/test/factories/legal_statuses.rb @@ -0,0 +1,7 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :legal_status do + name "MyString" + end +end diff --git a/test/factories/protected_areas.rb b/test/factories/protected_areas.rb new file mode 100644 index 0000000..76e906d --- /dev/null +++ b/test/factories/protected_areas.rb @@ -0,0 +1,12 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :protected_area do + sequence(:wdpa_id) { |n| n } + legal_status_updated_at Date.new(2014,1,1) + association :designation, factory: :designation, name: 'My designation' + association :iucn_category, factory: :iucn_category, name: 'My IUCN category' + association :legal_status, factory: :legal_status, name: 'My legal status' + association :governance, factory: :governance, name: 'My governance' + end +end diff --git a/test/factories/region.rb b/test/factories/region.rb new file mode 100644 index 0000000..1be83d4 --- /dev/null +++ b/test/factories/region.rb @@ -0,0 +1,8 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :region do + name 'Global' + iso 'GLB' + end +end diff --git a/test/factories/sub_locations.rb b/test/factories/sub_locations.rb new file mode 100644 index 0000000..2e52db3 --- /dev/null +++ b/test/factories/sub_locations.rb @@ -0,0 +1,10 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :sub_location do + english_name "MyString" + local_name "MyString" + alternate_name "MyString" + iso "MyString" + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb new file mode 100644 index 0000000..10c7bcf --- /dev/null +++ b/test/test_helper.rb @@ -0,0 +1,30 @@ +ENV["RACK_ENV"] = "test" + +require "minitest/autorun" +require "rack/test" +require "factory_girl" +require "database_cleaner" +require "config/environment" + + +DatabaseCleaner.clean_with :truncation +DatabaseCleaner.strategy = :transaction + +class Minitest::Test + include FactoryGirl::Syntax::Methods + + def setup + DatabaseCleaner.start + end + + def teardown + DatabaseCleaner.clean + end + + def get_with_rabl path, params={} + get path, params, {"api.tilt.root" => "api"} + @json_response = JSON.parse(last_response.body) rescue nil + end +end + +FactoryGirl.find_definitions diff --git a/web/routes.rb b/web/routes.rb new file mode 100644 index 0000000..6053b30 --- /dev/null +++ b/web/routes.rb @@ -0,0 +1,7 @@ +module Web + class Routes < Sinatra::Base + get '/' do + 'Hello world.' + end + end +end