Browse Source

WIP: get places details from bragi, not postgres

tags/0.3.6^2
Bèr Kessels 4 months ago
parent
commit
9b18d3935e

+ 2
- 12
app/models/place.rb View File

@@ -15,18 +15,10 @@ module Hours
ITERATOR = OpeningHoursConverter::Iterator.new
PARSER = OpeningHoursConverter::OpeningHoursParser.new

attr_accessor :id, :location, :place_id, :name,
attr_accessor :id, :geometry, :place_id, :name,
:region_slug, :distance, :website
attr_writer :address, :opening_hours

def lat
location.lat
end

def lon
location.lon
end

def status
return :unknown if opening_hours&.empty?

@@ -43,9 +35,7 @@ module Hours
end

def address
return unless raw_address

OpenStruct.new(Sequel::Postgres::HStore.parse(raw_address).to_hash)
OpenStruct.new(raw_address)
end

def region

+ 12
- 3
lib/app.rb View File

@@ -136,10 +136,19 @@ module Hours
erb :region
end

get %r{/places/(?<uuid>#{UUID_REGEX})}, provides: 'html' do
handle_get_place do
erb :place
get %r{/places/(?<id>[\w:]+)}, provides: 'html' do
# TODO: move into query.
faraday = Faraday.new(url: ENV['BRAGI_URL']) do |conn|
conn.response :raise_error
conn.adapter Faraday.default_adapter
end
response = faraday.get("features/#{params[:id]}")
@place = Hours::Models::Place.from_geojson_feature(
RGeo::GeoJSON.decode(response.body).first
)
erb :place
rescue Faraday::ResourceNotFound
404
end

get %r{/places/(?<uuid>#{UUID_REGEX})}, provides: 'json' do

+ 34
- 0
test/fixtures/vcr_cassettes/bragi_hm_nijmegen.yml View File

@@ -0,0 +1,34 @@
---
http_interactions:
- request:
method: get
uri: http://localhost:4000/features/poi:osm:node:989225414
body:
encoding: US-ASCII
string: ''
headers:
User-Agent:
- Faraday v1.0.0
response:
status:
code: 200
message: OK
headers:
content-length:
- '1186'
connection:
- close
content-type:
- application/json
cache-control:
- max-age=3600
date:
- Wed, 18 Mar 2020 16:14:01 GMT
body:
encoding: UTF-8
string: '{"type":"FeatureCollection","geocoding":{"version":"0.1.0","query":""},"features":[{"type":"Feature","geometry":{"coordinates":[5.865323399999999,51.8473397],"type":"Point"},"properties":{"geocoding":{"id":"poi:osm:node:989225414","type":"poi","label":"H&M","name":"H&M","postcode":"6511RA","city":null,"citycode":null,"administrative_regions":[],"poi_types":[{"id":"poi_type:shop:clothes","name":"Kledingwinkel"}],"properties":[{"key":"addr:city","value":"Nijmegen"},{"key":"addr:country","value":"NL"},{"key":"addr:housenumber","value":"1"},{"key":"addr:postcode","value":"6511RA"},{"key":"addr:street","value":"Burchtstraat"},{"key":"name","value":"H&M"},{"key":"opening_hours","value":"Mo-We
10:00-18:00; Th 10:00-21:00; Fr 10:00-18:00; Sa 09:30-17:30; Su 12:00-17:30"},{"key":"shop","value":"clothes"},{"key":"shop:number","value":"NL0050"},{"key":"source","value":"BAG;http://www.hm.com/nl/"},{"key":"source:date","value":"2014-05-07"},{"key":"wheelchair","value":"yes"}],"address":{"id":"addr:5.8652752;51.8473134:4","type":"house","label":"Gapershof
4","name":"Burchtstraat 1","housenumber":"1","street":"Burchtstraat","postcode":"6511RA","city": "Nijmegen","citycode":null,"administrative_regions":[]}}}}]}'
http_version: null
recorded_at: Thu, 09 Nov 1989 17:57:00 GMT
recorded_with: VCR 5.1.0

+ 34
- 0
test/fixtures/vcr_cassettes/bragi_marienburg_nijmegen.yml View File

@@ -0,0 +1,34 @@
---
http_interactions:
- request:
method: get
uri: http://localhost:4000/features/poi:osm:node:989371189
body:
encoding: US-ASCII
string: ''
headers:
User-Agent:
- Faraday v1.0.0
response:
status:
code: 200
message: OK
headers:
content-length:
- '1186'
connection:
- close
content-type:
- application/json
cache-control:
- max-age=3600
date:
- Wed, 26 Mar 2020 16:14:01 GMT
body:
encoding: UTF-8
string: '{"type":"FeatureCollection","geocoding":{"version":"0.1.0","query":""},"features":[{"type":"Feature","geometry":{"coordinates":[5.865323399999999,51.8473397],"type":"Point"},"properties":{"geocoding":{"id":"poi:osm:node:989225414","type":"poi","label":"H&M","name":"H&M","postcode":"6511RA","city":null,"citycode":null,"administrative_regions":[],"poi_types":[{"id":"poi_type:shop:clothes","name":"Kledingwinkel"}],"properties":[{"key":"addr:city","value":"Nijmegen"},{"key":"addr:country","value":"NL"},{"key":"addr:housenumber","value":"1"},{"key":"addr:postcode","value":"6511RA"},{"key":"addr:street","value":"Burchtstraat"},{"key":"name","value":"H&M"},
{"key":"shop","value":"clothes"},{"key":"source:date","value":"2014-05-07"},{"key":"wheelchair","value":"yes"}],"address":{"id":"addr:5.8652752;51.8473134:4","type":"house","label":"Gapershof
4","name":null,"housenumber":null,"street":"Mariënburg","postcode":null,"city":null,"citycode":null,"administrative_regions":[]}}}}]}'
http_version: null
recorded_at: Thu, 09 Nov 1989 17:57:00 GMT
recorded_with: VCR 5.1.0

+ 63
- 0
test/fixtures/vcr_cassettes/hacked.yml View File

@@ -0,0 +1,63 @@
---
http_interactions:
- request:
method: get
uri: http://localhost:4000/features/poi:osm:node:1337
body:
encoding: US-ASCII
string: ''
headers:
User-Agent:
- Faraday v1.0.0
response:
status:
code: 200
message: OK
headers:
content-length:
- '1186'
connection:
- close
content-type:
- application/json
cache-control:
- max-age=3600
date:
- Wed, 26 Mar 2020 16:14:01 GMT
body:
encoding: UTF-8
string:
'{"type":"FeatureCollection", "geocoding":{"version":"0.1.0", "query":""},
"features":[
{"type":"Feature",
"geometry":{"coordinates":[5.865323399999999, 51.8473397], "type":"Point"},
"properties":{
"geocoding":{"id":"poi:osm:node:989225414",
"type":"poi",
"label":"H&M",
"name":"H@ckedM",
"postcode":"6511RA",
"city":null,
"citycode":null,
"administrative_regions":[],
"poi_types":[{"id":"poi_type:shop:clothes",
"name":"Kledingwinkel"}],
"properties":[],
"address":{
"id":"addr:5.8652752;51.8473134:4",
"type":"house",
"label": "",
"name":null,
"housenumber":null,
"street":"Mariënburg",
"postcode":"<script>alert(\"1000AB\")</script>",
"city":null,
"citycode":null,
"administrative_regions":[]
}
}
}
}]}'
http_version: null
recorded_at: Thu, 09 Nov 1989 17:57:00 GMT
recorded_with: VCR 5.1.0

+ 30
- 0
test/fixtures/vcr_cassettes/no_node_found.yml View File

@@ -0,0 +1,30 @@
---
http_interactions:
- request:
method: get
uri: http://localhost:4000/features/poi:osm:node:42
body:
encoding: US-ASCII
string: ''
headers:
User-Agent:
- Faraday v1.0.0
response:
status:
code: 404
message: Not Found
headers:
content-length:
- '54'
connection:
- close
content-type:
- application/json
date:
- Thu, 26 Mar 2020 18:06:44 GMT
body:
encoding: UTF-8
string: '{"short":"query error","long":"Unable to find object"}'
http_version: null
recorded_at: Thu, 26 Mar 2020 18:06:44 GMT
recorded_with: VCR 5.1.0

+ 61
- 64
test/integration/web/view_places_test.rb View File

@@ -6,100 +6,97 @@ require 'timecop'
require_relative Hours.base_path.join('test/support/workflows/add_place.rb')

describe 'web view places' do
include Workflows::AddPlace
include WebTestHelpers

describe 'GET /places/:uuid' do
let(:input) { [json_fixtures('input/hm_burchtstraat.json')] }

describe 'GET /places/poi:osm:node:<id>' do
let(:place_id) { 'poi:osm:node:989225414' }
it 'returns a node detail page' do
Timecop.travel(die_wende) do
visit "/places/#{hm_id}"
assert_selector page, 'html[lang=nl]'

# SEO and title
assert_title page, 'Openingstijden'
assert_selector page, 'h1.title', text: 'Openingstijden'

# Breadcrumb
assert_selector page, 'nav ol.breadcrumb a', text: 'Openingstijden'
assert_selector page, 'nav ol.breadcrumb a', text: 'in Nijmegen'
assert_selector page, 'nav ol.breadcrumb li.active', text: 'H&M'

# Name and Address
assert_selector page, 'h2', text: 'H&M'
assert_selector page,
'address',
text: 'Burchtstraat 1 , 6511RA Nijmegen'

# A badge with class success means open.
assert_selector page, 'span.badge-success', text: 'Open'

within('table#opening_hours') do
assert_selector page, 'tr', text: 'Ma 10:00 18:00'
assert_selector page, 'tr', text: 'Zo 12:00 17:30'

# Current day is Thursday. Current Day is bold
assert_equal 4, die_wende.wday
assert_selector page, 'tr>th>strong', text: 'Do'
assert_selector page, 'tr>td>strong', text: '10:00'
assert_selector page, 'tr>td>strong', text: '21:00'
VCR.use_cassette('bragi_hm_nijmegen') do
Timecop.travel(die_wende) do
visit "/places/#{place_id}"
assert_selector page, 'html[lang=nl]'

# SEO and title
assert_title page, 'Openingstijden'
assert_selector page, 'h1.title', text: 'Openingstijden'

# Breadcrumb
assert_selector page, 'nav ol.breadcrumb a', text: 'Openingstijden'
assert_selector page, 'nav ol.breadcrumb a', text: 'in Nijmegen'
assert_selector page, 'nav ol.breadcrumb li.active', text: 'H&M'

# Name and Address
assert_selector page, 'h2', text: 'H&M'
assert_selector page,
'address',
text: 'Burchtstraat 1 , 6511RA Nijmegen'

# A badge with class success means open.
assert_selector page, 'span.badge-success', text: 'Open'

within('table#opening_hours') do
assert_selector page, 'tr', text: 'Ma 10:00 18:00'
assert_selector page, 'tr', text: 'Zo 12:00 17:30'

# Current day is Thursday. Current Day is bold
assert_equal 4, die_wende.wday
assert_selector page, 'tr>th>strong', text: 'Do'
assert_selector page, 'tr>td>strong', text: '10:00'
assert_selector page, 'tr>td>strong', text: '21:00'
end
end
end
end

it 'on a thursday, after 21:00 it is closed' do
after_hours = die_wende + (60 * 60 * 4)
Timecop.travel(after_hours) do
visit "/places/#{hm_id}"
# A badge with class danger means closed.
assert_selector page, 'span.badge-danger', text: 'Gesloten'
VCR.use_cassette('bragi_hm_nijmegen') do
Timecop.travel(after_hours) do
visit "/places/#{place_id}"
# A badge with class danger means closed.
assert_selector page, 'span.badge-danger', text: 'Gesloten'
end
end
end
end

describe 'GET /places/:uuid with unknown Opening Hours' do
let(:input) { [json_fixtures('input/marienburg_parking.json')] }

let(:place_id) { 'poi:osm:node:989371189' }
it 'on a thursday, after 21:00 it is closed' do
assert_nil input.first[:properties][:opening_hours]
Timecop.travel(die_wende) do
visit "/places/#{hm_id}"
# A badge with class secondary means unknown.
assert_selector page, 'span.badge-secondary', text: 'Onbekend'
VCR.use_cassette('bragi_marienburg_nijmegen') do
Timecop.travel(die_wende) do
visit "/places/#{place_id}"
# A badge with class secondary means unknown.
assert_selector page, 'span.badge-secondary', text: 'Onbekend'
end
end
end
end

describe 'GET /places/:uuid with unescaped values' do
let(:input) { [json_fixtures('input/hacked.json')] }
let(:place_id) { 'poi:osm:node:1337' }

it 'escapes ugly attributes' do
visit "/places/#{hm_id}"
assert_selector page, 'nav ol.breadcrumb li.active', text: 'H@ckedM'
assert_selector page, 'h2', text: 'H@ckedM'
assert_no_selector page, 'address script', visible: false
VCR.use_cassette('hacked') do
visit "/places/#{place_id}"
assert_selector page, 'nav ol.breadcrumb li.active', text: 'H@ckedM'
assert_selector page, 'h2', text: 'H@ckedM'
assert_no_selector page, 'address script', visible: false
end
end
end

describe 'GET nonexisting place' do
let(:input) { [] }

it 'gives a 404 for invalid UUID' do
visit '/places/1337'
visit '/places/+1337'
assert_equal 404, page.driver.status_code
end

it 'gives a 404 for nonexisting but valid UUID' do
nonexisting_uuid = SecureRandom.uuid

# When we seed with more places, we should determine that
# our UUID does not exist in the database. This scenario is hardly
# possible, though.
refute_equal nonexisting_uuid, hm_id

visit "/places/#{nonexisting_uuid}"
assert_equal 404, page.driver.status_code
it 'gives a 404 for nonexisting but valid id' do
VCR.use_cassette('no_node_found') do
visit '/places/poi:osm:node:42'
assert_equal 404, page.driver.status_code
end
end
end
end

+ 18
- 13
test/models/base_test.rb View File

@@ -24,32 +24,37 @@ describe Hours::Models::Base do
describe '.from_geojson_feature' do
let(:feature) do
OpenStruct.new(
properties: { 'geocoding' => {
'name' => 'Hi',
'fakemethod' => 'val',
'website' => 'example.org',
'properties' => [
{ 'key' => 'opening_hours', 'value' => '24/7' },
{ 'key' => 'shop:number', 'value' => '1337' },
{ 'key' => 'website', 'value' => 'example.com' }
]
} }
geometry: {
'coordinates' => [5.865323399999999, 51.8473397],
'type' => 'Point'
},
properties: {
'geocoding' => {
'name' => 'Hi',
'fakemethod' => 'val',
'website' => 'example.org',
'properties' => [
{ 'key' => 'opening_hours', 'value' => '24/7' },
{ 'key' => 'shop:number', 'value' => '1337' },
{ 'key' => 'website', 'value' => 'example.com' }
]
}
}
)
end

subject { MockModel.from_geojson_feature(feature) }

it 'ignores undefined attributes' do
subject = MockModel.from_geojson_feature(feature)
# Does not raise NoMethodError
assert_equal 'Hi', subject.name
end

it 'sets attributes from geocoding.properties' do
subject = MockModel.from_geojson_feature(feature)
assert_equal '24/7', subject.opening_hours
end

it 'prefers main properties over geocoding.properties' do
subject = MockModel.from_geojson_feature(feature)
assert_equal 'example.org', subject.website
end
end

+ 3
- 17
test/models/place_test.rb View File

@@ -18,11 +18,11 @@ describe Hours::Models::Place do

describe 'address' do
before do
subject.address = hstore_address
subject.address = { city: 'Nijmegen' }
end

it 'has a raw address' do
assert_equal hstore_address, subject.raw_address
assert_equal({ city: 'Nijmegen' }, subject.raw_address)
end

it 'has a city' do
@@ -37,7 +37,7 @@ describe Hours::Models::Place do

describe 'region' do
describe 'with city' do
before { subject.address = hstore_address }
before { subject.address = { city: 'Nijmegen' } }

it 'uses the city' do
assert_equal 'Nijmegen', subject.region
@@ -50,20 +50,6 @@ describe Hours::Models::Place do
end
end

describe 'location' do
let(:lat) { 20.1 }
let(:lon) { 20.2 }
let(:point) { GeoRuby::SimpleFeatures::Point.from_x_y(lon, lat) }

it 'has a lat' do
assert_equal lat, Hours::Models::Place.new(location: point).lat
end

it 'has a lon' do
assert_equal lon, Hours::Models::Place.new(location: point).lon
end
end

describe 'opening_hours' do
let(:opening_hours) { '1989 Mo-Fr 10:00-12:30,13:00-19:00' }


Loading…
Cancel
Save