justimmo_client 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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