flapjack-diner 2.0.0.pre.alpha.3 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +6 -10
  3. data/README.md +165 -272
  4. data/flapjack-diner.gemspec +1 -1
  5. data/lib/flapjack-diner.rb +54 -25
  6. data/lib/flapjack-diner/argument_validator.rb +0 -17
  7. data/lib/flapjack-diner/configuration.rb +417 -0
  8. data/lib/flapjack-diner/log_formatter.rb +22 -0
  9. data/lib/flapjack-diner/query.rb +114 -0
  10. data/lib/flapjack-diner/relationships.rb +180 -0
  11. data/lib/flapjack-diner/request.rb +280 -0
  12. data/lib/flapjack-diner/resources.rb +64 -0
  13. data/lib/flapjack-diner/response.rb +91 -0
  14. data/lib/flapjack-diner/tools.rb +46 -456
  15. data/lib/flapjack-diner/utility.rb +16 -0
  16. data/lib/flapjack-diner/version.rb +1 -1
  17. data/spec/flapjack-diner_spec.rb +9 -18
  18. data/spec/{resources/relationships_spec.rb → relationships_spec.rb} +75 -29
  19. data/spec/resources/checks_spec.rb +7 -7
  20. data/spec/resources/contacts_spec.rb +21 -19
  21. data/spec/resources/events_spec.rb +13 -13
  22. data/spec/resources/maintenance_periods_spec.rb +3 -3
  23. data/spec/resources/media_spec.rb +3 -3
  24. data/spec/resources/metrics_spec.rb +1 -1
  25. data/spec/resources/rules_spec.rb +278 -0
  26. data/spec/resources/states_spec.rb +1 -1
  27. data/spec/resources/statistics_spec.rb +1 -1
  28. data/spec/resources/tags_spec.rb +75 -19
  29. data/spec/support/fixture_data.rb +57 -98
  30. metadata +21 -29
  31. data/.rubocop.yml +0 -21
  32. data/.rubocop_todo.yml +0 -135
  33. data/lib/flapjack-diner/resources/acceptors.rb +0 -77
  34. data/lib/flapjack-diner/resources/checks.rb +0 -52
  35. data/lib/flapjack-diner/resources/contacts.rb +0 -54
  36. data/lib/flapjack-diner/resources/events.rb +0 -54
  37. data/lib/flapjack-diner/resources/maintenance_periods.rb +0 -76
  38. data/lib/flapjack-diner/resources/media.rb +0 -75
  39. data/lib/flapjack-diner/resources/metrics.rb +0 -23
  40. data/lib/flapjack-diner/resources/rejectors.rb +0 -77
  41. data/lib/flapjack-diner/resources/relationships.rb +0 -314
  42. data/lib/flapjack-diner/resources/states.rb +0 -24
  43. data/lib/flapjack-diner/resources/statistics.rb +0 -24
  44. data/lib/flapjack-diner/resources/tags.rb +0 -47
  45. data/spec/resources/acceptors_spec.rb +0 -278
  46. data/spec/resources/rejectors_spec.rb +0 -278
@@ -0,0 +1,22 @@
1
+ module Flapjack
2
+ module Diner
3
+ class LogFormatter #:nodoc:
4
+ TAG_NAME = Flapjack::Diner.name
5
+
6
+ attr_accessor :level, :logger, :current_time
7
+
8
+ def initialize(logger, level)
9
+ @logger = logger
10
+ @level = level.to_sym
11
+ end
12
+
13
+ def format(request, response)
14
+ current_time = Time.now.strftime("%Y-%m-%d %H:%M:%S %z")
15
+ http_method = request.http_method.name.split("::").last.upcase
16
+ path = request.path.to_s
17
+ content_length = response.respond_to?(:headers) ? response.headers['Content-Length'] : response['Content-Length']
18
+ @logger.send @level, "[#{TAG_NAME}] [#{current_time}] #{response.code} \"#{http_method} #{path}\" #{content_length || '-'} "
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,114 @@
1
+ require 'flapjack-diner/utility'
2
+
3
+ module Flapjack
4
+ module Diner
5
+ module Query
6
+
7
+ def self.validate_params(query = {}, &validation)
8
+ return unless block_given?
9
+ query = {} if [].eql?(query)
10
+ case query
11
+ when Array
12
+ query.each do |q|
13
+ ArgumentValidator.new(q).instance_eval(&validation)
14
+ end
15
+ else
16
+ ArgumentValidator.new(query).instance_eval(&validation)
17
+ end
18
+ end
19
+
20
+ # copied from Rack::Utils -- builds the query string for GETs
21
+ def self.build_nested(value, prefix = nil)
22
+ case value
23
+ when Array
24
+ build_array(value, prefix)
25
+ when Hash
26
+ build_hash(value, prefix)
27
+ else
28
+ build_data(value, prefix)
29
+ end
30
+ end
31
+
32
+ # used for the JSON data hashes in POST, PUT, DELETE
33
+ def self.prepare_nested(value)
34
+ case value
35
+ when Array
36
+ prepare_array(value)
37
+ when Hash
38
+ prepare_hash(value)
39
+ else
40
+ prepare_data(value)
41
+ end
42
+ end
43
+
44
+ def self.unwrap_uuids(*args)
45
+ ids = args.select {|a| a.is_a?(String) }
46
+ raise "IDs must be RFC 4122-compliant UUIDs" unless ids.all? {|id|
47
+ id =~ /^#{Flapjack::Diner::UUID_RE}$/i
48
+ }
49
+ ids
50
+ end
51
+
52
+ def self.unwrap_data(*args)
53
+ data = args.reject {|a| a.is_a?(String) || a.is_a?(Integer) }
54
+ unless data.all? {|a| a.is_a?(Hash) }
55
+ raise 'Data must be passed as a Hash, or multiple Hashes'
56
+ end
57
+ return Flapjack::Diner::Utility.symbolize(data.first) if data.size == 1
58
+ data.each_with_object([]) {|d, o| o << Flapjack::Diner::Utility.symbolize(d) }
59
+ end
60
+
61
+ private
62
+
63
+ def self.build_array(value, prefix)
64
+ value.map {|v| build_nested(v, "#{prefix}[]") }.join('&')
65
+ end
66
+
67
+ def self.build_hash(value, prefix)
68
+ value.map do |k, v|
69
+ data = prefix ? "#{prefix}[#{k}]" : k
70
+ build_nested(v, data)
71
+ end.join('&')
72
+ end
73
+
74
+ def self.build_data(value, prefix)
75
+ if value.respond_to?(:iso8601)
76
+ raise(ArgumentError, 'Value must be a Hash') if prefix.nil?
77
+ "#{escape(prefix)}=#{escape(value.iso8601)}"
78
+ elsif value.is_a?(String) || value.is_a?(Integer)
79
+ raise(ArgumentError, 'Value must be a Hash') if prefix.nil?
80
+ "#{escape(prefix)}=#{escape(value.to_s)}"
81
+ else
82
+ prefix
83
+ end
84
+ end
85
+
86
+ def self.escape(s)
87
+ URI.encode_www_form_component(s)
88
+ end
89
+
90
+ def self.prepare_array(value)
91
+ value.map {|v| prepare_nested(v) }
92
+ end
93
+
94
+ def self.prepare_hash(value)
95
+ value.each_with_object({}) do |(k, v), a|
96
+ a[k] = prepare_nested(v)
97
+ end
98
+ end
99
+
100
+ def self.prepare_data(value)
101
+ if value.respond_to?(:iso8601)
102
+ value.iso8601
103
+ else
104
+ case value
105
+ when Integer, TrueClass, FalseClass, NilClass
106
+ value
107
+ else
108
+ value.to_s
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,180 @@
1
+ require 'flapjack-diner/query'
2
+
3
+ module Flapjack
4
+ module Diner
5
+ module Relationships
6
+
7
+ # FIXME include option for GET should check config settings for includable
8
+
9
+ def self.included(base)
10
+ # base.extend ClassMethods
11
+ base.class_eval do
12
+
13
+ ::Flapjack::Diner::Configuration::RESOURCES.each_pair do |resource, config|
14
+ mappings = config[:relationships]
15
+ next if mappings.nil?
16
+
17
+ res = config[:resource]
18
+ resource_id_validator = :uuid
19
+
20
+ # # NB: no non-GET singular link methods, so commented out for now
21
+ # mappings.select {|n, a| a[:post] && a[:link] && :singular.eql?(a[:number]) }.each do |linked, assoc|
22
+ # linked_id_validator = :singular_link_uuid
23
+
24
+ # define_singleton_method("create_#{res}_link_#{linked}") do |resource_id, linked_id|
25
+ # validations_proc = proc do
26
+ # Flapjack::Diner::Query.validate_params(:resource_id => resource_id, :linked_id => linked_id) do
27
+ # validate :query => :resource_id, :as => resource_id_validator
28
+ # validate :query => :linked_id, :as => linked_id_validator
29
+ # end
30
+ # end
31
+
32
+ # resp = Flapjack::Diner::Request.new(
33
+ # linked, "/#{resource}/#{resource_id}/relationships/#{linked}",
34
+ # :ids => linked_id, :validations_proc => validations_proc
35
+ # ).post_links
36
+ # @response = Flapjack::Diner::Response.new(resp)
37
+ # @response.process
38
+ # @response.output
39
+ # end
40
+ # end
41
+
42
+ mappings.select {|n, a| a[:post] && a[:link] && :multiple.eql?(a[:number]) }.each do |linked, assoc|
43
+ type = ::Flapjack::Diner::Configuration::RESOURCES[linked][:resource]
44
+ linked_ids_validator = :multiple_link_uuid
45
+
46
+ define_singleton_method("create_#{res}_link_#{linked}") do |resource_id, *linked_ids|
47
+ validations_proc = proc do
48
+ Flapjack::Diner::Query.validate_params(:resource_id => resource_id, :linked_ids => linked_ids) do
49
+ validate :query => :resource_id, :as => resource_id_validator
50
+ validate :query => :linked_ids, :as => linked_ids_validator
51
+ end
52
+ end
53
+
54
+ resp = Flapjack::Diner::Request.new(
55
+ type, "/#{resource}/#{resource_id}/relationships/#{linked}",
56
+ :ids => linked_ids, :validations_proc => validations_proc
57
+ ).post_links
58
+ @response = Flapjack::Diner::Response.new(resp)
59
+ @response.process
60
+ @response.output
61
+ end
62
+ end
63
+
64
+ mappings.select {|n, a| a[:get] && a[:link] }.each do |linked, assoc|
65
+ define_singleton_method("#{res}_link_#{linked}") do |resource_id, opts = {}|
66
+ validations_proc = proc do |data|
67
+ Flapjack::Diner::Query.validate_params(:resource_id => resource_id) do
68
+ validate :query => :resource_id, :as => resource_id_validator
69
+ end
70
+
71
+ Flapjack::Diner::Query.validate_params(opts) do
72
+ validate :query => [:fields, :sort, :include], :as => :string_or_array_of_strings
73
+ validate :query => :filter, :as => :hash
74
+ validate :query => [:page, :per_page], :as => :positive_integer
75
+ end
76
+ end
77
+
78
+ resp = Flapjack::Diner::Request.new(
79
+ linked, "/#{resource}/#{resource_id}/#{linked}", :data => [opts],
80
+ :assoc => linked, :validations_proc => validations_proc
81
+ ).get
82
+ @response = Flapjack::Diner::Response.new(resp)
83
+ @response.process
84
+ @response.output
85
+ end
86
+ end
87
+
88
+ # # NB: no non-GET singular link methods, so commented out for now
89
+ # mappings.select {|n, a| a[:patch] && a[:link] && :singular.eql?(a[:number]) }.each do |linked, assoc|
90
+ # linked_id_validator = :singular_link_uuid
91
+
92
+ # define_singleton_method("update_#{res}_link_#{linked}") do |resource_id, linked_id|
93
+ # validations_proc = proc do
94
+ # Flapjack::Diner::Query.validate_params(:resource_id => resource_id, :linked_id => linked_id) do
95
+ # validate :query => :resource_id, :as => resource_id_validator
96
+ # validate :query => :linked_id, :as => linked_id_validator
97
+ # end
98
+ # end
99
+
100
+ # resp = Flapjack::Diner::Request.new(
101
+ # linked, "/#{resource}/#{resource_id}/relationships/#{linked}",
102
+ # :ids => linked_id, :validations_proc => validation_proc, :single => true
103
+ # ).patch_links
104
+ # @response = Flapjack::Diner::Response.new(resp)
105
+ # @response.process
106
+ # @response.output
107
+ # end
108
+ # end
109
+
110
+ mappings.select {|n, a| a[:patch] && a[:link] && :multiple.eql?(a[:number]) }.each do |linked, assoc|
111
+ type = ::Flapjack::Diner::Configuration::RESOURCES[linked][:resource]
112
+ linked_ids_validator = :multiple_link_uuid
113
+
114
+ define_singleton_method("update_#{res}_link_#{linked}") do |resource_id, *linked_ids|
115
+ validations_proc = proc do
116
+ Flapjack::Diner::Query.validate_params(:resource_id => resource_id, :linked_ids => linked_ids) do
117
+ validate :query => :resource_id, :as => resource_id_validator
118
+ validate :query => :linked_id, :as => linked_ids_validator
119
+ end
120
+ end
121
+
122
+ resp = Flapjack::Diner::Request.new(
123
+ type, "/#{resource}/#{resource_id}/relationships/#{linked}",
124
+ :ids => linked_ids, :validations_proc => validations_proc
125
+ ).patch_links
126
+ @response = Flapjack::Diner::Response.new(resp)
127
+ @response.process
128
+ @response.output
129
+ end
130
+ end
131
+
132
+ # # NB: no non-GET singular link methods, so commented out for now
133
+ # mappings.select {|n, a| a[:delete] && a[:link] && :singular.eql?(a[:number]) }.each do |linked, assoc|
134
+ # linked_id_validator = :singular_link_uuid
135
+
136
+ # define_singleton_method("delete_#{res}_link_#{linked}") do |resource_id, linked_id|
137
+ # validations_proc = proc do
138
+ # Flapjack::Diner::Query.validate_params(:resource_id => resource_id, :linked_id => linked_id) do
139
+ # validate :query => :resource_id, :as => resource_id_validator
140
+ # validate :query => :linked_id, :as => linked_id_validator
141
+ # end
142
+ # end
143
+
144
+ # resp = Flapjack::Diner::Request.new(
145
+ # linked, "/#{resource}/#{resource_id}/relationships/#{linked}",
146
+ # :ids => linked_ids :validations_proc => validations_proc
147
+ # ).delete_links
148
+ # @response = Flapjack::Diner::Response.new(resp)
149
+ # @response.process
150
+ # @response.output
151
+ # end
152
+ # end
153
+
154
+ mappings.select {|n, a| a[:delete] && a[:link] && :multiple.eql?(a[:number]) }.each do |linked, assoc|
155
+ type = ::Flapjack::Diner::Configuration::RESOURCES[linked][:resource]
156
+ linked_ids_validator = :uuid_or_array_of_uuids
157
+
158
+ define_singleton_method("delete_#{res}_link_#{linked}") do |resource_id, *linked_ids|
159
+ validations_proc = proc do
160
+ Flapjack::Diner::Query.validate_params(:resource_id => resource_id, :linked_ids => linked_ids) do
161
+ validate :query => :resource_id, :as => resource_id_validator
162
+ validate :query => :linked_ids, :as => linked_ids_validator
163
+ end
164
+ end
165
+
166
+ resp = Flapjack::Diner::Request.new(
167
+ type, "/#{resource}/#{resource_id}/relationships/#{linked}",
168
+ :ids => linked_ids, :validations_proc => validations_proc
169
+ ).delete_links
170
+ @response = Flapjack::Diner::Response.new(resp)
171
+ @response.process
172
+ @response.output
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,280 @@
1
+ require 'set'
2
+ require 'uri'
3
+
4
+ require 'flapjack-diner/query'
5
+
6
+ module Flapjack
7
+ module Diner
8
+ class Request
9
+
10
+ def initialize(type, path, opts = {})
11
+ @type = type
12
+ @path = path
13
+ @ids = opts.key?(:ids) ? opts[:ids] :
14
+ (opts[:data].nil? ? nil : Flapjack::Diner::Query.unwrap_uuids(*opts[:data]))
15
+ @data = opts[:data].nil? ? nil : Flapjack::Diner::Query.unwrap_data(*opts[:data])
16
+ @assoc = opts[:assoc]
17
+
18
+ unless @data.nil?
19
+ validations = opts[:validations]
20
+ unless validations.nil?
21
+ Flapjack::Diner::Query.validate_params(@data) do
22
+ validations.each_pair { |k,v| validate :query => k, :as => v }
23
+ end
24
+ end
25
+ end
26
+
27
+ validations_proc = opts[:validations_proc]
28
+ validations_proc.call unless validations_proc.nil?
29
+ end
30
+
31
+ def post
32
+ req_data = nil
33
+ jsonapi_ext = ""
34
+
35
+ case @data
36
+ when Array
37
+ req_data = @data.collect {|d| record_data(d, :post) }
38
+ jsonapi_ext = "; ext=bulk"
39
+ when Hash
40
+ req_data = record_data(@data, :post)
41
+ end
42
+ req_uri = build_uri
43
+
44
+ # TODO send current character encoding in content-type ?
45
+ Flapjack::Diner.post(req_uri.request_uri,
46
+ :body => Flapjack::Diner::Query.prepare_nested(:data => req_data).to_json,
47
+ :headers => {'Content-Type' => "application/vnd.api+json#{jsonapi_ext}"})
48
+ end
49
+
50
+ def post_links
51
+ @data = @ids.collect {|id| {:type => @type, :id => id}}
52
+ @ids = nil
53
+ req_uri = build_uri
54
+ Flapjack::Diner.post(req_uri.request_uri,
55
+ :body => Flapjack::Diner::Query.prepare_nested(:data => @data).to_json,
56
+ :headers => {'Content-Type' => 'application/vnd.api+json'})
57
+ end
58
+
59
+ def get
60
+ @data = @data.reduce({}, &:merge) if @data.is_a?(Array)
61
+ if !@ids.nil? && (@ids.size > 1)
62
+ if @data[:filter].nil?
63
+ @data[:filter] = {:id => @ids}
64
+ elsif !data[:filter].key?(:id)
65
+ @data[:filter][:id] = @ids
66
+ else
67
+ @data[:filter][:id] |= @ids
68
+ end
69
+ end
70
+
71
+ filt = @data.delete(:filter)
72
+ unless filt.nil?
73
+ @data[:filter] = filt.each_with_object([]) do |(k, v), memo|
74
+ value = case v
75
+ when Array, Set
76
+ v.to_a.join("|")
77
+ else
78
+ v.to_s
79
+ end
80
+ memo << (@assoc.nil? ? "#{k}:#{value}" : "#{@assoc}.#{k}:#{value}")
81
+ end
82
+ end
83
+
84
+ incl = @data[:include]
85
+ unless incl.nil?
86
+ case incl
87
+ when Array
88
+ raise ArgumentError.new("Include parameters must not contain commas") if incl.any? {|i| i =~ /,/}
89
+ @data[:include] = if @assoc.nil?
90
+ incl.join(",")
91
+ else
92
+ incl.map {|i|
93
+ if i.eql?(@assoc.to_s) || (i =~ /^#{@assoc}\./)
94
+ i
95
+ else
96
+ "#{@assoc}.#{i}"
97
+ end
98
+ }.join(",")
99
+ end
100
+ when String
101
+ raise ArgumentError.new("Include parameters must not contain commas") if incl =~ /,/
102
+ unless @assoc.nil? || (incl.eql?(@assoc.to_s)) || (incl =~ /^#{@assoc}\./)
103
+ @data[:include] = "#{@assoc}.#{incl}"
104
+ end
105
+ end
106
+ end
107
+
108
+ req_uri = build_uri(@data)
109
+ Flapjack::Diner.get(req_uri.request_uri)
110
+ end
111
+
112
+ def patch
113
+ req_uri = nil
114
+ req_data = nil
115
+
116
+ jsonapi_ext = ""
117
+ case @data
118
+ when Hash
119
+ raise "Update data does not contain :id" unless @data[:id]
120
+ req_data = record_data(@data, :patch)
121
+ @ids = [@data[:id]]
122
+ req_uri = build_uri
123
+ when Array
124
+ ids = []
125
+ req_data = []
126
+ @data.each do |d|
127
+ d_id = d.key?(:id) ? d[:id] : nil
128
+ @ids << d_id unless d_id.nil? || d_id.empty?
129
+ req_data << record_data(d, :patch)
130
+ end
131
+ raise "Update data must each contain :id" unless @ids.size == @data.size
132
+ req_uri = build_uri
133
+ jsonapi_ext = "; ext=bulk"
134
+ end
135
+
136
+ opts = if req_data.nil?
137
+ {}
138
+ else
139
+ {:body => Flapjack::Diner::Query.prepare_nested(:data => req_data).to_json,
140
+ :headers => {'Content-Type' => "application/vnd.api+json#{jsonapi_ext}"}}
141
+ end
142
+ Flapjack::Diner.patch(req_uri.request_uri, opts)
143
+ end
144
+
145
+ def patch_links
146
+ # data = if opts[:single]
147
+ # raise "Must provide one ID for a singular link" unless @ids.size == 1
148
+ # [nil].eql?(@ids) ? nil : {:type => @type, :id => @ids.first}
149
+ # else
150
+ data = [[]].eql?(@ids) ? [] : @ids.collect {|id| {:type => @type, :id => id}}
151
+ # end
152
+ @ids = nil
153
+
154
+ req_uri = build_uri
155
+
156
+ opts = {:body => Flapjack::Diner::Query.prepare_nested(:data => data).to_json,
157
+ :headers => {'Content-Type' => 'application/vnd.api+json'}}
158
+ Flapjack::Diner.patch(req_uri.request_uri, opts)
159
+ end
160
+
161
+ def delete
162
+ type = Flapjack::Diner::Configuration::RESOURCES[@type][:resource]
163
+
164
+ req_uri = build_uri
165
+ opts = if @ids.size == 1
166
+ {}
167
+ else
168
+ data = @ids.collect {|id| {:type => type, :id => id} }
169
+ {:body => Flapjack::Diner::Query.prepare_nested(:data => data).to_json,
170
+ :headers => {'Content-Type' => 'application/vnd.api+json; ext=bulk'}}
171
+ end
172
+ Flapjack::Diner.delete(req_uri.request_uri, opts)
173
+ end
174
+
175
+ def delete_links
176
+ data = @ids.collect {|id| {:type => @type, :id => id}}
177
+ @ids = nil
178
+ req_uri = build_uri
179
+ opts = {:body => Flapjack::Diner::Query.prepare_nested(:data => data).to_json,
180
+ :headers => {'Content-Type' => 'application/vnd.api+json'}}
181
+ Flapjack::Diner.delete(req_uri.request_uri, opts)
182
+ end
183
+
184
+ private
185
+
186
+ def record_data(source, method)
187
+ res = Flapjack::Diner::Configuration::RESOURCES[@type]
188
+ r_type = res[:resource]
189
+
190
+ req_data = {}
191
+ ['id', :id].each do |i|
192
+ req_data[:id] = source[i] if source.key?(i)
193
+ end
194
+ req_data[:type] = r_type
195
+
196
+ assocs = res[:relationships] || {}
197
+
198
+ rel_singular = assocs.inject([]) do |memo, (assoc_name, assoc)|
199
+ if assoc[method].is_a?(TrueClass) && :singular.eql?(assoc[:number])
200
+ memo << assoc_name
201
+ end
202
+ memo
203
+ end
204
+
205
+ rel_multiple = assocs.inject([]) do |memo, (assoc_name, assoc)|
206
+ if assoc[method].is_a?(TrueClass) && :multiple.eql?(assoc[:number])
207
+ memo << assoc_name
208
+ end
209
+ memo
210
+ end
211
+
212
+ excluded = [:id, :type] + rel_singular + rel_multiple
213
+ attrs = source.reject do |k, _|
214
+ excluded.include?(k.to_sym)
215
+ end
216
+
217
+ req_data[:attributes] = attrs unless attrs.empty?
218
+
219
+ rel_data = {}
220
+
221
+ rel_singular.each do |singular_link|
222
+ converted = false
223
+ [singular_link, singular_link.to_s].each do |sl|
224
+ next if converted || !source.key?(sl)
225
+ rel_data[singular_link] = {
226
+ :data => {
227
+ :type => singular_link.to_s,
228
+ :id => source[sl]
229
+ }
230
+ }
231
+ converted = true
232
+ end
233
+ end
234
+
235
+ rel_multiple.each do |multiple_link|
236
+ converted = false
237
+ ml_type = Flapjack::Diner::Configuration::RESOURCES[multiple_link][:resource]
238
+ [multiple_link, multiple_link.to_s].each do |ml|
239
+ next if converted || !source.key?(ml)
240
+ rel_data[multiple_link] = {
241
+ :data => source[ml].map {|id| {:type => ml_type, :id => id} }
242
+ }
243
+ converted = true
244
+ end
245
+ end
246
+
247
+ req_data[:relationships] = rel_data unless rel_data.empty?
248
+
249
+ req_data
250
+ end
251
+
252
+ def normalise_port(port_str, protocol)
253
+ if port_str.nil? || port_str.to_i < 1 || port_str.to_i > 65_535
254
+ 'https'.eql?(protocol) ? 443 : 80
255
+ else
256
+ port_str.to_i
257
+ end
258
+ end
259
+
260
+ def protocol_host_port
261
+ %r{^(?:(?<protocol>https?)://)
262
+ (?<host>[a-zA-Z0-9][a-zA-Z0-9\.\-]*[a-zA-Z0-9])
263
+ (?::(?<port>\d+))?
264
+ }ix =~ Flapjack::Diner.base_uri
265
+
266
+ protocol = protocol.nil? ? 'http' : protocol.downcase
267
+ [protocol, host, normalise_port(port, protocol)]
268
+ end
269
+
270
+ def build_uri(params = [])
271
+ pr, ho, po = protocol_host_port
272
+ @path += "/#{URI.escape(@ids.first.to_s)}" if !@ids.nil? && @ids.size == 1
273
+ params = params.reduce({}, &:merge) if params.is_a?(Array)
274
+ query = params.empty? ? nil : Flapjack::Diner::Query.build_nested(params)
275
+ URI::HTTP.build(:protocol => pr, :host => ho, :port => po,
276
+ :path => @path, :query => query)
277
+ end
278
+ end
279
+ end
280
+ end