immobilienscout 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4b25d3436d988c560e5b5cbc17fe0e4ab6dc5b5d
4
+ data.tar.gz: '059a52d68bcaf1cc768681b00546e4e9c3024c46'
5
+ SHA512:
6
+ metadata.gz: e848fdd1d25c07d6923a3a47ef31c87c100f05a24ad5dc8ab21d3f997b94e93924081714a749687851b3923c18c64cef4f50e306b53fc7e4ef845f7d0f50c560
7
+ data.tar.gz: 07c04b9b76c7715a62c9673c18f1c18f2992eacc83f171bf62d365efd68a50e667aa591e1877f214306c4de91c197a0375e93897d9aea6ec28f55200ece71ab5
data/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # Immobilienscout gem
2
+
3
+ This is an interface for Immobilienscout API
4
+
5
+ ## Example
6
+ Set up your keys in `immobilienscout.rb` inside initializers.
7
+ ```ruby
8
+ Immobilienscout.configure do |config|
9
+ config.consumer_key = 'consumer_key'
10
+ config.consumer_secret = 'consumer_secret'
11
+ config.access_token = 'access_token'
12
+ config.access_token_secret = 'access_token_secret'
13
+ config.use_sandbox = true
14
+ end
15
+
16
+ ```
17
+
18
+ ## Methods
19
+
20
+ #### Property
21
+ - Create property
22
+ ```ruby
23
+ Immobilienscout::API::Property.create({params})
24
+ ```
25
+
26
+ - Publish property
27
+ ```ruby
28
+ Immobilienscout::API::Property.publish({params})
29
+ ```
30
+
31
+ - Delete property
32
+ ```ruby
33
+ Immobilienscout::API::Property.destroy(is24_id)
34
+ ```
35
+
36
+ #### Attachment
37
+ - Add attachments to property
38
+ ```ruby
39
+ Immobilienscout::API::Attachment.add(is24_id, binary_file, {metadata})
40
+ ```
41
+
42
+ #### Report
43
+ - Get scout report
44
+ ```ruby
45
+ Immobilienscout::API::Report.retrieve(is24_id, date_from, date_to)
46
+ ```
47
+
48
+ #### IMPORTANT: Check Immobilienscout API Documentation about the needed params.
49
+
50
+
51
+ ## Immobilienscout API
52
+
53
+ https://api.immobilienscout24.de/our-apis/import-export.html
@@ -0,0 +1,27 @@
1
+ require 'byebug'
2
+ require 'timecop'
3
+ require 'active_support'
4
+ require 'active_support/core_ext'
5
+ require 'immobilienscout/authenticator'
6
+ require 'immobilienscout/request'
7
+ require 'immobilienscout/response_parser'
8
+ require 'immobilienscout/client'
9
+ require 'immobilienscout/api/property'
10
+ require 'immobilienscout/api/attachment'
11
+ require 'immobilienscout/api/report'
12
+ require 'immobilienscout/errors/invalid_request'
13
+
14
+ module Immobilienscout
15
+ class << self
16
+ attr_accessor :configuration
17
+ end
18
+
19
+ def self.configure
20
+ self.configuration ||= Configuration.new
21
+ yield(configuration)
22
+ end
23
+
24
+ class Configuration
25
+ attr_accessor :consumer_key, :consumer_secret, :access_token, :access_token_secret, :use_sandbox
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ module Immobilienscout
2
+ module API
3
+ class Attachment
4
+ class << self
5
+ def add(is24_id, binary_file, metadata)
6
+ raise ArgumentError unless is24_id.present? && binary_file.present? && metadata.present?
7
+
8
+ url = add_url(is24_id)
9
+ metadata_file = create_metadata_file(metadata)
10
+ params = { attachment: binary_file, metadata: metadata_file }
11
+ parsed_response = Immobilienscout::Request.new(url, params).post_with_multipart
12
+
13
+ raise Immobilienscout::Errors::InvalidRequest, parsed_response.messages.map(&:messages) unless parsed_response.success?
14
+
15
+ parsed_response
16
+ end
17
+
18
+ private
19
+
20
+ def create_metadata_file(params)
21
+ metadata_object = StringIO.new(params.to_json)
22
+ UploadIO.new(metadata_object, 'application/json')
23
+ end
24
+
25
+ def add_url(is24_id)
26
+ "#{Immobilienscout::Client.api_url}/restapi/api/offer/v1.0/user/me/realestate/#{is24_id}/attachment"
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,55 @@
1
+ module Immobilienscout
2
+ module API
3
+ module Property
4
+ class << self
5
+ def create(params)
6
+ raise ArgumentError unless params.present?
7
+
8
+ execute_post_request(create_url, params)
9
+ end
10
+
11
+ def publish(params)
12
+ raise ArgumentError unless params.present?
13
+
14
+ execute_post_request(publish_url, params)
15
+ end
16
+
17
+ def destroy(is24_id)
18
+ raise ArgumentError unless is24_id.present?
19
+
20
+ destroy_url = destroy_url(is24_id)
21
+
22
+ execute_delete_request(destroy_url)
23
+ end
24
+
25
+ private
26
+
27
+ def execute_post_request(url, params)
28
+ parsed_response = Immobilienscout::Request.new(url, params).post
29
+ raise Immobilienscout::Errors::InvalidRequest, parsed_response.messages.map(&:messages) unless parsed_response.success?
30
+
31
+ parsed_response
32
+ end
33
+
34
+ def execute_delete_request(url)
35
+ parsed_response = Immobilienscout::Request.new(url).delete
36
+ raise Immobilienscout::Errors::InvalidRequest, parsed_response.messages.map(&:messages) unless parsed_response.success?
37
+
38
+ parsed_response
39
+ end
40
+
41
+ def create_url
42
+ "#{Immobilienscout::Client.api_url}/restapi/api/offer/v1.0/user/me/realestate"
43
+ end
44
+
45
+ def publish_url
46
+ "#{Immobilienscout::Client.api_url}/restapi/api/offer/v1.0/publish"
47
+ end
48
+
49
+ def destroy_url(is24_id)
50
+ "#{Immobilienscout::Client.api_url}/restapi/api/offer/v1.0/user/me/realestate/#{is24_id}"
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,36 @@
1
+ module Immobilienscout
2
+ module API
3
+ class Report
4
+ class << self
5
+ def retrieve(is24_id, date_from, date_to)
6
+ raise ArgumentError unless is24_id.present? && date_from.present? && date_to.present?
7
+
8
+ query_params = query_params(date_from, date_to)
9
+ url = retrieve_url(is24_id)
10
+
11
+ execute_get_request(url, query_params)
12
+ end
13
+
14
+ private
15
+
16
+ def execute_get_request(url, query_params)
17
+ parsed_response = Immobilienscout::Request.new(url, query_params).get
18
+ raise Immobilienscout::Errors::InvalidRequest, parsed_response.messages.map(&:messages) unless parsed_response.success?
19
+
20
+ parsed_response
21
+ end
22
+
23
+ def query_params(date_from, date_to)
24
+ {
25
+ dateFrom: date_from.to_s,
26
+ dateTo: date_to.to_s
27
+ }
28
+ end
29
+
30
+ def retrieve_url(is24_id)
31
+ "#{Immobilienscout::Client.api_url}/restapi/api/offer/v1.0/user/me/realestate/#{is24_id}/dailyreport"
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,89 @@
1
+ require 'open-uri'
2
+
3
+ module Immobilienscout
4
+ class Authenticator
5
+ ALLOWED_METHODS = %w(POST GET DELETE).freeze
6
+ OAUTH_SIGNATURE_METHOD = 'HMAC-SHA1'.freeze
7
+ OAUTH_VERSION = '1.0'.freeze
8
+ HASH_DIGEST = 'sha1'.freeze
9
+
10
+ def initialize(url, method, query_params = nil)
11
+ @url = url
12
+ @method = method
13
+ @query_params = query_params
14
+
15
+ raise ArgumentError unless valid?
16
+ end
17
+
18
+ def call
19
+ header_params.merge!(@query_params) if @query_params
20
+ auth_headers = auth_headers(header_params)
21
+
22
+ auth_header_string(auth_headers)
23
+ end
24
+
25
+ private
26
+
27
+ def valid?
28
+ @url.present? && @method.present? && ALLOWED_METHODS.include?(@method)
29
+ end
30
+
31
+ def header_params
32
+ @header_params ||= {
33
+ oauth_consumer_key: Immobilienscout.configuration.consumer_key,
34
+ oauth_nonce: generate_nonce,
35
+ oauth_signature_method: OAUTH_SIGNATURE_METHOD,
36
+ oauth_timestamp: generate_timestamp,
37
+ oauth_token: Immobilienscout.configuration.access_token,
38
+ oauth_version: OAUTH_VERSION
39
+ }
40
+ end
41
+
42
+ def signing_key
43
+ "#{url_encode(Immobilienscout.configuration.consumer_secret)}&#{url_encode(Immobilienscout.configuration.access_token_secret)}"
44
+ end
45
+
46
+ def generate_nonce
47
+ Base64.encode64(random_bytes).gsub(/\W/, '')
48
+ end
49
+
50
+ def random_bytes(size=7)
51
+ OpenSSL::Random.random_bytes(size)
52
+ end
53
+
54
+ def generate_timestamp
55
+ Time.now.utc.to_i.to_s
56
+ end
57
+
58
+ def signature_base_string(params)
59
+ "#{@method}&#{url_encode(@url)}&#{url_encode(params.to_query)}"
60
+ end
61
+
62
+ def generate_signature(signature_base_string)
63
+ url_encode(sign(signing_key, signature_base_string))
64
+ end
65
+
66
+ def url_encode(string)
67
+ CGI.escape(string)
68
+ end
69
+
70
+ def sign(key, base_string)
71
+ digest = OpenSSL::Digest.new(HASH_DIGEST)
72
+ hmac = OpenSSL::HMAC.digest(digest, key, base_string)
73
+ Base64.encode64(hmac).chomp.delete(' ')
74
+ end
75
+
76
+ def auth_headers(header_params)
77
+ signature_base_string = signature_base_string(header_params)
78
+ header_params['oauth_signature'] = generate_signature(signature_base_string)
79
+ header_params
80
+ end
81
+
82
+ def auth_header_string(params)
83
+ header_params = params.each_with_object('OAuth ') do |(key, value), header|
84
+ header << "#{key}=#{value},"
85
+ end
86
+ header_params.chop
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,12 @@
1
+ module Immobilienscout
2
+ class Client
3
+ SANDBOX_URL = 'https://rest.sandbox-immobilienscout24.de'.freeze
4
+ LIVE_URL = 'https://rest.immobilienscout24.de'.freeze
5
+
6
+ def self.api_url
7
+ return SANDBOX_URL if Immobilienscout.configuration.use_sandbox
8
+
9
+ LIVE_URL
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ module Immobilienscout
2
+ module Errors
3
+ class InvalidRequest < StandardError
4
+ def initialize(msg = message)
5
+ super
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,86 @@
1
+ require 'net/http/post/multipart'
2
+ require 'uri'
3
+
4
+ module Immobilienscout
5
+ class Request
6
+ def initialize(url, params = nil)
7
+ @url = url
8
+ @params = params
9
+
10
+ raise ArgumentError unless valid?
11
+ end
12
+
13
+ def get
14
+ auth_header = generate_auth_header('GET', @params)
15
+ headers = generate_headers(auth_header)
16
+
17
+ url_with_query = "#{@url}?#{@params.to_query}"
18
+ uri = URI.parse(url_with_query)
19
+
20
+ request = Net::HTTP::Get.new(uri)
21
+ request.initialize_http_header(headers)
22
+
23
+ execute_request(uri, request)
24
+ end
25
+
26
+ def post
27
+ auth_header = generate_auth_header('POST')
28
+ headers = generate_headers(auth_header)
29
+
30
+ uri = URI.parse(@url)
31
+
32
+ request = Net::HTTP::Post.new(uri)
33
+ request.body = @params.to_json
34
+ request.initialize_http_header(headers)
35
+
36
+ execute_request(uri, request)
37
+ end
38
+
39
+ def post_with_multipart
40
+ auth_header = generate_auth_header('POST')
41
+
42
+ uri = URI.parse(@url)
43
+
44
+ request = Net::HTTP::Post::Multipart.new(uri, @params)
45
+ request.add_field('Authorization', auth_header)
46
+ request.add_field('Accept', 'application/json')
47
+
48
+ execute_request(uri, request)
49
+ end
50
+
51
+ def delete
52
+ auth_header = generate_auth_header('DELETE')
53
+ headers = generate_headers(auth_header)
54
+
55
+ uri = URI.parse(@url)
56
+
57
+ request = Net::HTTP::Delete.new(uri)
58
+ request.initialize_http_header(headers)
59
+
60
+ execute_request(uri, request)
61
+ end
62
+
63
+ private
64
+
65
+ def valid?
66
+ @url.present?
67
+ end
68
+
69
+ def generate_auth_header(method_type, query_params = nil)
70
+ Immobilienscout::Authenticator.new(@url, method_type, query_params).call
71
+ end
72
+
73
+ def generate_headers(auth_header)
74
+ {
75
+ 'Authorization' => auth_header,
76
+ 'Content-Type' => 'application/json',
77
+ 'Accept' => 'application/json'
78
+ }
79
+ end
80
+
81
+ def execute_request(uri, request)
82
+ response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request request }
83
+ Immobilienscout::ResponseParser.new(response).call
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,48 @@
1
+ module Immobilienscout
2
+ class ResponseParser
3
+ HTTP_OK = %w(200 201).freeze
4
+
5
+ Message = Struct.new(:success?, :code, :messages, :id)
6
+
7
+ def initialize(response)
8
+ @response = response
9
+
10
+ raise ArgumentError unless valid?
11
+ end
12
+
13
+ def call
14
+ response_body = JSON.parse(@response.body)
15
+
16
+ Message.new(success?, code, messages(response_body), id(response_body))
17
+ end
18
+
19
+ private
20
+
21
+ def valid?
22
+ @response.present?
23
+ end
24
+
25
+ def success?
26
+ HTTP_OK.include? @response.code
27
+ end
28
+
29
+ def code
30
+ @response.code
31
+ end
32
+
33
+ def messages(response_body)
34
+ return response_body unless response_body['common.messages']
35
+
36
+ messages = response_body['common.messages'].first['message']
37
+ messages = [messages] if messages.is_a? Hash
38
+ messages.map { |msg| Message.new(nil, msg['messageCode'], msg['message'], msg['id']) }
39
+ end
40
+
41
+ def id(response_body)
42
+ return unless response_body['common.messages']
43
+
44
+ messages = response_body['common.messages'].first['message']
45
+ messages['id'] if messages.is_a? Hash
46
+ end
47
+ end
48
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: immobilienscout
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Angela Guette
8
+ - Wiebke Frey
9
+ - Laura Garcia
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2019-03-11 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: json
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - ">="
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '0'
29
+ - !ruby/object:Gem::Dependency
30
+ name: multipart-post
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ - !ruby/object:Gem::Dependency
44
+ name: activesupport
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ type: :runtime
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ - !ruby/object:Gem::Dependency
58
+ name: webmock
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ type: :runtime
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ description: This gem provides HTTP endpoints to connect your app with Immobilienscout
72
+ API
73
+ email: backend-team@homeday.de
74
+ executables: []
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - README.md
79
+ - lib/immobilienscout.rb
80
+ - lib/immobilienscout/api/attachment.rb
81
+ - lib/immobilienscout/api/property.rb
82
+ - lib/immobilienscout/api/report.rb
83
+ - lib/immobilienscout/authenticator.rb
84
+ - lib/immobilienscout/client.rb
85
+ - lib/immobilienscout/errors/invalid_request.rb
86
+ - lib/immobilienscout/request.rb
87
+ - lib/immobilienscout/response_parser.rb
88
+ homepage: https://github.com/homeday-de/immobilienscout
89
+ licenses:
90
+ - MIT
91
+ metadata: {}
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 2.6.14.3
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: A ruby gem to handle Immobilienscout API
112
+ test_files: []