justimmo_client 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +15 -0
  3. data/.gitignore +15 -0
  4. data/.gitlab-ci.yml +67 -0
  5. data/.rspec +2 -0
  6. data/.rubocop.yml +90 -0
  7. data/.ruby-version +1 -0
  8. data/Gemfile +21 -0
  9. data/LICENSE +21 -0
  10. data/README.md +72 -0
  11. data/Rakefile +14 -0
  12. data/bin/console +21 -0
  13. data/bin/setup +8 -0
  14. data/examples/client.rb +16 -0
  15. data/justimmo_client.gemspec +41 -0
  16. data/lib/justimmo_client/api/v1/models/city.rb +16 -0
  17. data/lib/justimmo_client/api/v1/models/country.rb +44 -0
  18. data/lib/justimmo_client/api/v1/models/employee.rb +45 -0
  19. data/lib/justimmo_client/api/v1/models/federal_state.rb +15 -0
  20. data/lib/justimmo_client/api/v1/models/file.rb +64 -0
  21. data/lib/justimmo_client/api/v1/models/geo_location.rb +47 -0
  22. data/lib/justimmo_client/api/v1/models/image.rb +10 -0
  23. data/lib/justimmo_client/api/v1/models/justimmo_base.rb +15 -0
  24. data/lib/justimmo_client/api/v1/models/realty.rb +82 -0
  25. data/lib/justimmo_client/api/v1/models/realty_area.rb +49 -0
  26. data/lib/justimmo_client/api/v1/models/realty_category.rb +13 -0
  27. data/lib/justimmo_client/api/v1/models/realty_marketing.rb +12 -0
  28. data/lib/justimmo_client/api/v1/models/realty_price.rb +94 -0
  29. data/lib/justimmo_client/api/v1/models/realty_room_count.rb +35 -0
  30. data/lib/justimmo_client/api/v1/models/realty_type.rb +12 -0
  31. data/lib/justimmo_client/api/v1/models/realty_usage.rb +24 -0
  32. data/lib/justimmo_client/api/v1/models/region.rb +12 -0
  33. data/lib/justimmo_client/api/v1/representers/json/attachment_image_representer.rb +16 -0
  34. data/lib/justimmo_client/api/v1/representers/json/attachment_representer.rb +15 -0
  35. data/lib/justimmo_client/api/v1/representers/json/contact_representer.rb +30 -0
  36. data/lib/justimmo_client/api/v1/representers/json/justimmo_representer.rb +13 -0
  37. data/lib/justimmo_client/api/v1/representers/json/location_representer.rb +14 -0
  38. data/lib/justimmo_client/api/v1/representers/json/realty_category_representer.rb +21 -0
  39. data/lib/justimmo_client/api/v1/representers/json/realty_detail_representer.rb +69 -0
  40. data/lib/justimmo_client/api/v1/representers/json.rb +10 -0
  41. data/lib/justimmo_client/api/v1/representers/xml/city_representer.rb +18 -0
  42. data/lib/justimmo_client/api/v1/representers/xml/contact_representer.rb +15 -0
  43. data/lib/justimmo_client/api/v1/representers/xml/country_representer.rb +15 -0
  44. data/lib/justimmo_client/api/v1/representers/xml/employee_list_representer.rb +15 -0
  45. data/lib/justimmo_client/api/v1/representers/xml/employee_representer.rb +46 -0
  46. data/lib/justimmo_client/api/v1/representers/xml/federal_state_representer.rb +17 -0
  47. data/lib/justimmo_client/api/v1/representers/xml/geo_location_representer.rb +24 -0
  48. data/lib/justimmo_client/api/v1/representers/xml/justimmo_representer.rb +13 -0
  49. data/lib/justimmo_client/api/v1/representers/xml/realty_area_representer.rb +28 -0
  50. data/lib/justimmo_client/api/v1/representers/xml/realty_category_representer.rb +15 -0
  51. data/lib/justimmo_client/api/v1/representers/xml/realty_detail_representer.rb +92 -0
  52. data/lib/justimmo_client/api/v1/representers/xml/realty_list_representer.rb +85 -0
  53. data/lib/justimmo_client/api/v1/representers/xml/realty_price_representer.rb +53 -0
  54. data/lib/justimmo_client/api/v1/representers/xml/realty_representer.rb +32 -0
  55. data/lib/justimmo_client/api/v1/representers/xml/realty_room_count_representer.rb +22 -0
  56. data/lib/justimmo_client/api/v1/representers/xml/realty_type_representer.rb +14 -0
  57. data/lib/justimmo_client/api/v1/representers/xml/region_representer.rb +14 -0
  58. data/lib/justimmo_client/api/v1/representers/xml.rb +11 -0
  59. data/lib/justimmo_client/api/v1/requests/employee_request.rb +24 -0
  60. data/lib/justimmo_client/api/v1/requests/justimmo_request.rb +64 -0
  61. data/lib/justimmo_client/api/v1/requests/realty_request.rb +209 -0
  62. data/lib/justimmo_client/api/v1.rb +16 -0
  63. data/lib/justimmo_client/autoload.rb +17 -0
  64. data/lib/justimmo_client/core/caching.rb +55 -0
  65. data/lib/justimmo_client/core/config.rb +46 -0
  66. data/lib/justimmo_client/core/logging.rb +46 -0
  67. data/lib/justimmo_client/core/utils.rb +38 -0
  68. data/lib/justimmo_client/employee.rb +39 -0
  69. data/lib/justimmo_client/errors.rb +58 -0
  70. data/lib/justimmo_client/misc.rb +10 -0
  71. data/lib/justimmo_client/option_parser.rb +107 -0
  72. data/lib/justimmo_client/realty.rb +150 -0
  73. data/lib/justimmo_client/version.rb +5 -0
  74. data/lib/justimmo_client.rb +22 -0
  75. data/notes.md +19 -0
  76. metadata +271 -0
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JustimmoClient::V1
4
+ module XML
5
+ class GeoLocationRepresenter < JustimmoRepresenter
6
+ property :zip_code, as: :plz
7
+ property :location, as: :ort
8
+ property :federal_state, as: :bundesland
9
+ property :floor, as: :etage
10
+
11
+ nested :land do
12
+ property :country, as: :iso_land, attribute: true
13
+ end
14
+
15
+ property :latitude,
16
+ as: :user_defined_simplefield,
17
+ parse_filter: ->(_fragment, options) { options[:doc].css("user_defined_simplefield[feldname=geokoordinaten_breitengrad_exakt]").text }
18
+
19
+ property :longitude,
20
+ as: :user_defined_simplefield,
21
+ parse_filter: ->(_fragment, options) { options[:doc].css("user_defined_simplefield[feldname=geokoordinaten_laengengrad_exakt]").text }
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "representable/xml"
4
+
5
+ module JustimmoClient::V1
6
+ module XML
7
+ class JustimmoRepresenter < Representable::Decorator
8
+ include Representable::XML
9
+
10
+ defaults render_nil: true
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JustimmoClient::V1
4
+ module XML
5
+ class RealtyAreaRepresenter < JustimmoRepresenter
6
+ { balcony_terrace: :balkon_terrasse_flaeche,
7
+ balcony: :balkons_flaeche,
8
+ office: :bueroflaeche,
9
+ garage: :garagen_flaeche,
10
+ garden: :gartenflaeche,
11
+ total: :gesamtflaeche,
12
+ surface: :grundflaeche,
13
+ property: :grundstuecksflaeche,
14
+ basement: :kellerflaeche,
15
+ storage: :lagerflaeche,
16
+ loggia: :loggias_flaeche,
17
+ floor: :nutzflaeche,
18
+ parking: :stellplatz_flaeche,
19
+ terrace: :terrassen_flaeche,
20
+ buildable: :verbaubare_flaeche,
21
+ sales: :verkaufsflaeche,
22
+ living: :wohnflaeche
23
+ }.each do |key, api|
24
+ property key, as: api
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JustimmoClient::V1
4
+ module XML
5
+ class RealtyCategoryRepresenter < JustimmoRepresenter
6
+ self.representation_wrap = :objektkategorie
7
+
8
+ property :id
9
+ property :name
10
+ property :sortablerank
11
+
12
+ collection_representer class: RealtyCategory
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JustimmoClient::V1
4
+ module XML
5
+ class RealtyDetailItemRepresenter < RealtyRepresenter
6
+ nested :verwaltung_techn do
7
+ property :id, as: :objektnr_intern
8
+ property :number, as: :objektnr_extern
9
+ property :openimmo_id, as: :openimmo_obid
10
+ end
11
+
12
+ nested :verwaltung_objekt do
13
+ property :available, as: :verfuegbar_ab
14
+ property :status_id
15
+ property :created_at,
16
+ as: :user_defined_simplefield,
17
+ parse_filter: ->(_fragment, options) { options[:doc].css("user_defined_simplefield[feldname=erstellt_am]").text }
18
+ property :updated_at,
19
+ as: :user_defined_simplefield,
20
+ parse_filter: ->(_fragment, options) { options[:doc].css("user_defined_simplefield[feldname=aktualisiert_am]").text }
21
+ end
22
+
23
+ property :geo,
24
+ decorator: GeoLocationRepresenter,
25
+ class: GeoLocation
26
+
27
+ property :contact,
28
+ as: :kontaktperson,
29
+ decorator: ContactRepresenter,
30
+ class: Employee
31
+
32
+ nested :freitexte do
33
+ property :title, as: :objekttitel
34
+ property :description,
35
+ as: :objektbeschreibung,
36
+ parse_filter: ->(frag, _opt) { frag.gsub("\r\n", "\n") }
37
+ property :description_furniture,
38
+ as: :ausstatt_beschr,
39
+ parse_filter: ->(frag, _opt) { frag.split(",").map(&:strip) }
40
+ nested :user_defined_anyfield do
41
+ property :furniture, as: :justimmo_moeblierung
42
+ end
43
+ end
44
+
45
+ property :price,
46
+ as: :preise,
47
+ decorator: RealtyPriceRepresenter,
48
+ class: RealtyPrice
49
+
50
+ collection :images,
51
+ as: :anhang,
52
+ wrap: :anhaenge,
53
+ class: Image do
54
+ property :category, as: :gruppe, attribute: true
55
+ property :origin, as: :location, attribute: true
56
+ property :title, as: :anhangtitel
57
+ nested :daten do
58
+ %i[pfad small medium big2 medium2 s220x155 fullhd].each do |size|
59
+ property size,
60
+ setter: ->(represented:, fragment:, **) do
61
+ represented.add_url(fragment, default: :big)
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ # FIXME: Get rid of duplicate code!
68
+ # Duplicates in RealtyListRepresenter
69
+ property :area,
70
+ as: :flaechen,
71
+ decorator: RealtyAreaRepresenter,
72
+ class: RealtyArea
73
+
74
+ property :room_count,
75
+ as: :flaechen,
76
+ decorator: RealtyRoomCountRepresenter,
77
+ class: RealtyRoomCount
78
+
79
+ property :documents, as: :dokumente
80
+ property :images360, as: :bilder360
81
+ property :videos
82
+ property :links
83
+ end
84
+
85
+ class RealtyDetailRepresenter < JustimmoRepresenter
86
+ property :realty,
87
+ as: :immobilie,
88
+ decorator: RealtyDetailItemRepresenter,
89
+ class: Realty
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JustimmoClient::V1
4
+ module XML
5
+ class RealtyListItemRepresenter < RealtyRepresenter
6
+ property :id
7
+ property :number, as: :objektnummer
8
+
9
+ property :created_at, as: :erstellt_am
10
+ property :updated_at, as: :aktualisiert_am
11
+ property :status_id
12
+
13
+ property :title, as: :titel
14
+ property :teaser, as: :dreizeiler
15
+ property :description,
16
+ as: :objektbeschreibung,
17
+ parse_filter: ->(frag, _opt) { frag.gsub("\r\n", "\n") }
18
+
19
+ property :floor, as: :etage
20
+
21
+ # Move some attributes into "sub-objects"
22
+
23
+ { first_image: :erstes_bild, second_image: :zweites_bild }.each do |key, api|
24
+ property api, setter: ->(represented:, fragment:, **) { represented.add_image(fragment) }
25
+ end
26
+
27
+ { location: :ort,
28
+ zip_code: :plz,
29
+ proximity: :naehe
30
+ }.each do |key, api|
31
+ property api, setter: ->(represented:, fragment:, **) { represented.geo.send("#{key}=", fragment) }
32
+ end
33
+
34
+ { balcony_terrace: :balkon_terrasse_flaeche,
35
+ balcony: :balkons_flaeche,
36
+ office: :bueroflaeche,
37
+ garage: :garagen_flaeche,
38
+ garden: :gartenflaeche,
39
+ total: :gesamtflaeche,
40
+ surface: :grundflaeche,
41
+ property: :grundstuecksflaeche,
42
+ basement: :kellerflaeche,
43
+ storage: :lagerflaeche,
44
+ loggia: :loggias_flaeche,
45
+ floor: :nutzflaeche,
46
+ parking: :stellplatz_flaeche,
47
+ terrace: :terrassen_flaeche,
48
+ buildable: :verbaubare_flaeche,
49
+ sales: :verkaufsflaeche,
50
+ living: :wohnflaeche
51
+ }.each do |key, api|
52
+ property key,
53
+ as: api,
54
+ setter: ->(fragment:, represented:, **) { represented.area[key] = fragment },
55
+ getter: ->(represented:, **) { represented.area[key] }
56
+ end
57
+
58
+ { store: :anzahl_abstellraum,
59
+ bathroom: :anzahl_badezimmer,
60
+ balcony_terrace: :anzahl_balkon_terrassen,
61
+ balcony: :anzahl_balkone,
62
+ garden: :anzahl_garten,
63
+ garage: :anzahl_garagen,
64
+ loggia: :anzahl_loggias,
65
+ basement: :anzahl_keller,
66
+ toilet: :anzahl_sep_wc,
67
+ parking_space: :anzahl_stellplaetze,
68
+ total: :anzahl_zimmer
69
+ }.each do |key, api|
70
+ property key,
71
+ as: api,
72
+ setter: ->(fragment:, represented:, **) { represented.room_count[key] = fragment },
73
+ getter: ->(represented:, **) { represented.room_count[key] }
74
+ end
75
+ end
76
+
77
+ class RealtyListRepresenter < JustimmoRepresenter
78
+ collection :realties,
79
+ as: :immobilie,
80
+ wraps: :immobilie,
81
+ decorator: RealtyListItemRepresenter,
82
+ class: Realty
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JustimmoClient::V1
4
+ module XML
5
+ class RealtyPriceRepresenter < JustimmoRepresenter
6
+ nested :waehrung do
7
+ property :currency, as: :iso_waehrung, attribute: true
8
+ end
9
+
10
+ property :purcase, as: :kaufpreis
11
+ property :purcase_net, as: :kaufpreisnetto
12
+ nested :kaufpreis do
13
+ property :on_demand, as: :auf_anfrage, attribute: true
14
+ end
15
+
16
+ nested :miete do
17
+ property :rent, as: :brutto
18
+ property :rent_net, as: :netto
19
+ property :rent_vat, as: :ust
20
+ end
21
+
22
+ property :deposit, as: :kaution
23
+ property :rent_cold, as: :kaltmiete
24
+ property :rent_including_heating, as: :warmmiete
25
+ property :rent_per_sqm, as: :mietpreis_pro_qm
26
+ property :operating_cost_per_sqm, as: :betriebskosten_pro_qm
27
+
28
+ property :provision, as: :provisionspflichtig
29
+ property :including_vat, as: :zzg_mehrwertsteuer
30
+ property :commission, as: :aussen_courtage
31
+
32
+ property :real_estate_taxes,
33
+ as: :user_defined_simplefield,
34
+ parse_filter: ->(_fragment, options) do
35
+ options[:doc].css("user_defined_simplefield[feldname=grunderwerbssteuer]").text
36
+ end
37
+
38
+ property :land_registry,
39
+ as: :user_defined_simplefield,
40
+ parse_filter: ->(_fragment, options) do
41
+ options[:doc].css("user_defined_simplefield[feldname=grundbucheintragung]").text
42
+ end
43
+
44
+ nested :zusatzkosten do
45
+ nested :betriebskosten do
46
+ property :operating_cost, as: :brutto
47
+ property :operating_cost_net, as: :netto
48
+ property :operating_cost_vat, as: :ust
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JustimmoClient::V1
4
+ module XML
5
+ class RealtyRepresenter < JustimmoRepresenter
6
+ nested :category, as: :objektkategorie do
7
+ property :usage, as: :nutzungsart, class: RealtyUsage do
8
+ property :living, as: :WOHNEN, attribute: true
9
+ property :business, as: :GEWERBE, attribute: true
10
+ property :investment, as: :ANLAGE, attribute: true
11
+ end
12
+
13
+ property :marketing, as: :vermarktungsart, class: RealtyMarketing do
14
+ property :buy, as: :KAUF, attribute: true
15
+ property :rent, as: :MIETE_PACHT, attribute: true
16
+ end
17
+
18
+ property :type_id,
19
+ as: :user_defined_simplefield,
20
+ parse_filter: ->(_fragment, options) do
21
+ options[:doc].css("user_defined_simplefield[feldname=objektart_id]").text
22
+ end
23
+
24
+ property :sub_type_id,
25
+ as: :user_defined_simplefield,
26
+ parse_filter: ->(_fragment, options) do
27
+ options[:doc].css("user_defined_simplefield[feldname=sub_objektart_id]").text
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JustimmoClient::V1
4
+ module XML
5
+ class RealtyRoomCountRepresenter < JustimmoRepresenter
6
+ { store: :anzahl_abstellraum,
7
+ bathroom: :anzahl_badezimmer,
8
+ balcony_terrace: :anzahl_balkon_terrassen,
9
+ balcony: :anzahl_balkone,
10
+ garden: :anzahl_garten,
11
+ garage: :anzahl_garagen,
12
+ loggia: :anzahl_loggias,
13
+ basement: :anzahl_keller,
14
+ toilet: :anzahl_sep_wc,
15
+ parking_space: :anzahl_stellplaetze,
16
+ total: :anzahl_zimmer
17
+ }.each do |key, api|
18
+ property key, as: api
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JustimmoClient::V1
4
+ module XML
5
+ class RealtyTypeRepresenter < JustimmoRepresenter
6
+ self.representation_wrap = :objektart
7
+
8
+ property :id
9
+ property :name
10
+
11
+ collection_representer class: RealtyType
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JustimmoClient::V1
4
+ module XML
5
+ class RegionRepresenter < JustimmoRepresenter
6
+ self.representation_wrap = :region
7
+
8
+ property :id
9
+ property :name
10
+
11
+ collection_representer class: Region
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JustimmoClient::V1
4
+ # @api private
5
+ # @!visibility hidden
6
+ module XML
7
+ extend JustimmoClient::Utils
8
+
9
+ autoload_dir "#{__dir__}/xml/*_representer.rb"
10
+ end
11
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module JustimmoClient::V1
6
+ # @api private
7
+ module EmployeeRequest
8
+ extend JustimmoRequest
9
+
10
+ module_function
11
+
12
+ def list
13
+ get("team/list")
14
+ end
15
+
16
+ def detail(id)
17
+ get("team/detail", id: id)
18
+ end
19
+
20
+ def ids
21
+ get("team/ids")
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rest_client"
4
+ require "digest"
5
+ require "retriable"
6
+
7
+ module JustimmoClient::V1
8
+ # @api private
9
+ module JustimmoRequest
10
+ include JustimmoClient::Logging
11
+ include JustimmoClient::Caching
12
+
13
+ def get(path, params = {})
14
+ with_cache(cache_key(path, params)) { request(path, params) }
15
+ end
16
+
17
+ def request(path, params = {})
18
+ uri = "#{JustimmoClient::Config.url}/#{path}"
19
+
20
+ options = {
21
+ params: params,
22
+ Authorization: "Basic #{JustimmoClient::Config.credentials}"
23
+ }
24
+
25
+ with_retries do
26
+ with_error_handler do
27
+ log.debug("Requesting #{uri} with params #{options[:params]}")
28
+ response = RestClient.get(uri, options)
29
+ response.body
30
+ end
31
+ end
32
+ end
33
+
34
+ def with_error_handler
35
+ yield
36
+ rescue RestClient::Unauthorized
37
+ log.error("Authentication failed, check your configuration.")
38
+ raise JustimmoClient::AuthenticationFailed
39
+ rescue RestClient::BadRequest, RestClient::NotFound, RestClient::InternalServerError => e
40
+ raise JustimmoClient::RetrievalFailed, e.message
41
+ end
42
+
43
+ def with_retries
44
+ options = {
45
+ base_interval: 2.0,
46
+ tries: JustimmoClient::Config.request_retries,
47
+ on_retry: proc do |exception, try, elapsed_time, next_interval|
48
+ log.error("#{exception.class}: #{exception}")
49
+ log.error("Try #{try} in #{elapsed_time} seconds, retrying in #{next_interval} seconds.")
50
+ end,
51
+ on: JustimmoClient::RetrievalFailed
52
+ }
53
+
54
+ Retriable.retriable(options) { yield }
55
+ end
56
+
57
+ def cache_key(path, params)
58
+ key = Digest::SHA256.new
59
+ key << path
60
+ key << params.to_s
61
+ key.hexdigest
62
+ end
63
+ end
64
+ end