pact_broker 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +13 -5
  2. data/.gitignore +2 -0
  3. data/CHANGELOG.md +5 -0
  4. data/README.md +2 -1
  5. data/Rakefile +1 -3
  6. data/db/migrations/07_increase_json_content_length.rb +6 -1
  7. data/db/migrations/08_create_latest_pact_view.rb +7 -5
  8. data/db/migrations/14_add_timestamps_to_pact_views.rb +28 -0
  9. data/lib/pact_broker/api.rb +0 -4
  10. data/lib/pact_broker/api/decorators/latest_pact_decorator.rb +4 -1
  11. data/lib/pact_broker/api/decorators/pact_decorator.rb +2 -2
  12. data/lib/pact_broker/api/decorators/pact_version_decorator.rb +3 -4
  13. data/lib/pact_broker/api/decorators/pact_versions_decorator.rb +4 -4
  14. data/lib/pact_broker/api/decorators/pacticipant_decorator.rb +3 -2
  15. data/lib/pact_broker/api/decorators/tag_decorator.rb +2 -2
  16. data/lib/pact_broker/api/decorators/timestamps.rb +24 -0
  17. data/lib/pact_broker/api/decorators/webhook_decorator.rb +2 -2
  18. data/lib/pact_broker/api/decorators/webhooks_decorator.rb +4 -4
  19. data/lib/pact_broker/api/renderers/html_pact_renderer.rb +16 -4
  20. data/lib/pact_broker/api/resources/base_resource.rb +0 -1
  21. data/lib/pact_broker/api/resources/group.rb +11 -6
  22. data/lib/pact_broker/api/resources/index.rb +5 -6
  23. data/lib/pact_broker/api/resources/latest_pact.rb +8 -5
  24. data/lib/pact_broker/api/resources/latest_pacts.rb +3 -3
  25. data/lib/pact_broker/api/resources/pact.rb +9 -5
  26. data/lib/pact_broker/api/resources/pact_webhooks.rb +9 -5
  27. data/lib/pact_broker/api/resources/pacticipant.rb +10 -5
  28. data/lib/pact_broker/api/resources/relationships.rb +3 -3
  29. data/lib/pact_broker/api/resources/tag.rb +8 -7
  30. data/lib/pact_broker/api/resources/webhook.rb +1 -1
  31. data/lib/pact_broker/api/resources/webhook_execution.rb +1 -1
  32. data/lib/pact_broker/app.rb +8 -57
  33. data/lib/pact_broker/logging.rb +1 -0
  34. data/lib/pact_broker/models/pact.rb +6 -8
  35. data/lib/pact_broker/repositories/pact.rb +29 -0
  36. data/lib/pact_broker/repositories/pact_content.rb +0 -0
  37. data/lib/pact_broker/repositories/pact_repository.rb +66 -21
  38. data/lib/pact_broker/repositories/tag_repository.rb +1 -1
  39. data/lib/pact_broker/repositories/version_repository.rb +2 -1
  40. data/lib/pact_broker/services/pact_service.rb +14 -16
  41. data/lib/pact_broker/ui.rb +2 -0
  42. data/lib/pact_broker/ui/app.rb +81 -0
  43. data/lib/pact_broker/ui/controllers/groups.rb +1 -1
  44. data/lib/pact_broker/version.rb +1 -1
  45. data/lib/rack/pact_broker/convert_file_extension_to_accept_header.rb +44 -0
  46. data/pact_broker.gemspec +1 -0
  47. data/public/javascripts/highlight.pack.js +1 -0
  48. data/public/stylesheets/github-json.css +127 -0
  49. data/public/stylesheets/pact.css +4 -0
  50. data/spec/fixtures/consumer-provider.json +21 -0
  51. data/spec/integration/app_spec.rb +165 -0
  52. data/spec/integration/endpoints/pact_put_spec.rb +43 -0
  53. data/spec/lib/pact_broker/api/decorators/latest_pact_decorator_spec.rb +42 -0
  54. data/spec/lib/pact_broker/api/decorators/pact_version_decorator_spec.rb +5 -0
  55. data/spec/lib/pact_broker/api/decorators/pacticipant_decorator_spec.rb +2 -1
  56. data/spec/lib/pact_broker/api/decorators/webhook_decorator_spec.rb +9 -2
  57. data/spec/lib/pact_broker/api/renderers/html_pact_renderer_spec.rb +14 -3
  58. data/spec/lib/pact_broker/repositories/pact_repository_spec.rb +88 -4
  59. data/spec/lib/pact_broker/repositories/tag_repository_spec.rb +2 -0
  60. data/spec/lib/pact_broker/repositories/version_repository_spec.rb +20 -2
  61. data/spec/lib/pact_broker/services/pacticipant_service_spec.rb +1 -1
  62. data/spec/service_consumers/pact_helper.rb +2 -17
  63. data/spec/spec_helper.rb +4 -22
  64. data/spec/support/database_cleaner.rb +18 -0
  65. data/spec/support/fixture_helpers.rb +10 -0
  66. data/spec/support/provider_state_builder.rb +4 -15
  67. metadata +82 -47
@@ -17,17 +17,20 @@ module PactBroker
17
17
  end
18
18
 
19
19
  def resource_exists?
20
- @pact = pact_service.find_latest_pact(identifier_from_path)
21
- @pact != nil
20
+ pact
22
21
  end
23
22
 
24
23
  def to_json
25
- response.headers['X-Pact-Consumer-Version'] = @pact.consumer_version_number
26
- PactBroker::Api::Decorators::PactDecorator.new(@pact).to_json(base_url: base_url)
24
+ response.headers['X-Pact-Consumer-Version'] = pact.consumer_version_number
25
+ PactBroker::Api::Decorators::PactDecorator.new(pact).to_json(base_url: base_url)
27
26
  end
28
27
 
29
28
  def to_html
30
- PactBroker.configuration.html_pact_renderer.call(@pact)
29
+ PactBroker.configuration.html_pact_renderer.call(pact)
30
+ end
31
+
32
+ def pact
33
+ @pact ||= pact_service.find_latest_pact(identifier_from_path)
31
34
  end
32
35
 
33
36
  end
@@ -15,11 +15,11 @@ module PactBroker
15
15
  end
16
16
 
17
17
  def to_json
18
- generate_json(pact_service.find_latest_pacts)
18
+ PactBroker::Api::Decorators::PactCollectionDecorator.new(pacts).to_json(base_url: base_url)
19
19
  end
20
20
 
21
- def generate_json pacts
22
- PactBroker::Api::Decorators::PactCollectionDecorator.new(pacts).to_json(base_url: base_url)
21
+ def pacts
22
+ pact_service.find_latest_pacts
23
23
  end
24
24
 
25
25
  end
@@ -35,18 +35,22 @@ module PactBroker
35
35
  end
36
36
 
37
37
  def resource_exists?
38
- @pact = pact_service.find_pact(identifier_from_path)
39
- @pact != nil
38
+ pact
40
39
  end
41
40
 
42
41
  def from_json
43
- @pact, created = pact_service.create_or_update_pact(identifier_from_path.merge(:json_content => request_body))
44
- response.headers["Location"] = pact_url(base_url, @pact) if created
42
+ response_code = pact ? 200 : 201
43
+ @pact = pact_service.create_or_update_pact(identifier_from_path.merge(:json_content => request_body))
45
44
  response.body = to_json
45
+ response_code
46
46
  end
47
47
 
48
48
  def to_json
49
- PactBroker::Api::Decorators::PactDecorator.new(@pact).to_json(base_url: base_url)
49
+ PactBroker::Api::Decorators::PactDecorator.new(pact).to_json(base_url: base_url)
50
+ end
51
+
52
+ def pact
53
+ @pact ||= pact_service.find_pact(identifier_from_path)
50
54
  end
51
55
 
52
56
  end
@@ -23,8 +23,7 @@ module PactBroker
23
23
  end
24
24
 
25
25
  def resource_exists?
26
- (@consumer = find_pacticipant(identifier_from_path[:consumer_name], "consumer")) &&
27
- (@provider = find_pacticipant(identifier_from_path[:provider_name], "provider"))
26
+ consumer && provider
28
27
  end
29
28
 
30
29
  def malformed_request?
@@ -45,7 +44,6 @@ module PactBroker
45
44
  def from_json
46
45
  saved_webhook = webhook_service.create next_uuid, webhook, consumer, provider
47
46
  response.body = Decorators::WebhookDecorator.new(saved_webhook).to_json(base_url: base_url)
48
- true
49
47
  end
50
48
 
51
49
  def to_json
@@ -54,8 +52,6 @@ module PactBroker
54
52
 
55
53
  private
56
54
 
57
- attr_reader :consumer, :provider
58
-
59
55
  def webhooks
60
56
  webhook_service.find_by_consumer_and_provider consumer, provider
61
57
  end
@@ -68,6 +64,14 @@ module PactBroker
68
64
  @next_uuid ||= webhook_service.next_uuid
69
65
  end
70
66
 
67
+ def consumer
68
+ @consumer ||= find_pacticipant(identifier_from_path[:consumer_name], "consumer")
69
+ end
70
+
71
+ def provider
72
+ @provider ||= find_pacticipant(identifier_from_path[:provider_name], "provider")
73
+ end
74
+
71
75
  def find_pacticipant name, role
72
76
  pacticipant_service.find_pacticipant_by_name(name).tap do | pacticipant |
73
77
  set_json_error_message("No #{role} with name '#{name}' found") if pacticipant.nil?
@@ -31,18 +31,17 @@ module PactBroker
31
31
  end
32
32
 
33
33
  def from_json
34
- if @pacticipant
34
+ if pacticipant
35
35
  @pacticipant = pacticipant_service.update params.merge(name: pacticipant_name)
36
36
  else
37
37
  @pacticipant = pacticipant_service.create params.merge(name: pacticipant_name)
38
- response.headers["Location"] = pacticipant_url(base_url, @pacticipant)
38
+ response.headers["Location"] = pacticipant_url(base_url, pacticipant)
39
39
  end
40
40
  response.body = to_json
41
41
  end
42
42
 
43
43
  def resource_exists?
44
- @pacticipant = pacticipant_service.find_pacticipant_by_name(pacticipant_name)
45
- @pacticipant != nil
44
+ pacticipant
46
45
  end
47
46
 
48
47
  def delete_resource
@@ -51,7 +50,13 @@ module PactBroker
51
50
  end
52
51
 
53
52
  def to_json
54
- PactBroker::Api::Decorators::PacticipantRepresenter.new(@pacticipant).to_json(base_url: base_url)
53
+ PactBroker::Api::Decorators::PacticipantRepresenter.new(pacticipant).to_json(base_url: base_url)
54
+ end
55
+
56
+ private
57
+
58
+ def pacticipant
59
+ @pacticipant ||= pacticipant_service.find_pacticipant_by_name(pacticipant_name)
55
60
  end
56
61
 
57
62
  def pacticipant_name
@@ -16,11 +16,11 @@ module PactBroker
16
16
  end
17
17
 
18
18
  def to_csv
19
- generate_csv(pact_service.find_latest_pacts)
19
+ PactBroker::Api::Decorators::RelationshipsCsvDecorator.new(pacts).to_csv
20
20
  end
21
21
 
22
- def generate_csv pacts
23
- PactBroker::Api::Decorators::RelationshipsCsvDecorator.new(pacts).to_csv
22
+ def pacts
23
+ pact_service.find_latest_pacts
24
24
  end
25
25
 
26
26
  end
@@ -19,23 +19,24 @@ module PactBroker
19
19
  end
20
20
 
21
21
  def from_json
22
- unless @tag
22
+ unless tag
23
23
  @tag = tag_service.create identifier_from_path
24
- response.headers["Location"] = tag_url(base_url, @tag)
24
+ # Make it return a 201 by setting the Location header
25
+ response.headers["Location"] = tag_url(base_url, tag)
25
26
  end
26
- response.body = generate_json @tag
27
+ response.body = to_json
27
28
  end
28
29
 
29
30
  def resource_exists?
30
- @tag = tag_service.find identifier_from_path
31
+ tag
31
32
  end
32
33
 
33
34
  def to_json
34
- generate_json(@tag)
35
+ PactBroker::Api::Decorators::TagDecorator.new(tag).to_json(base_url: base_url)
35
36
  end
36
37
 
37
- def generate_json tag
38
- PactBroker::Api::Decorators::TagDecorator.new(tag).to_json(base_url: base_url)
38
+ def tag
39
+ @tag ||= tag_service.find identifier_from_path
39
40
  end
40
41
 
41
42
  end
@@ -16,7 +16,7 @@ module PactBroker
16
16
  end
17
17
 
18
18
  def resource_exists?
19
- !webhook.nil?
19
+ webhook
20
20
  end
21
21
 
22
22
  def to_json
@@ -19,7 +19,7 @@ module PactBroker
19
19
  end
20
20
 
21
21
  def resource_exists?
22
- !webhook.nil?
22
+ webhook
23
23
  end
24
24
 
25
25
  private
@@ -2,6 +2,7 @@ require 'pact_broker/configuration'
2
2
  require 'pact_broker/db'
3
3
  require 'pact_broker/project_root'
4
4
  require 'rack/hal_browser'
5
+ require 'rack/pact_broker/convert_file_extension_to_accept_header'
5
6
 
6
7
  module PactBroker
7
8
 
@@ -42,53 +43,23 @@ module PactBroker
42
43
  def build_app
43
44
  @app = Rack::Builder.new
44
45
 
45
- @app.use Rack::Static, :urls => ["/stylesheets", "/images", "/css", "/fonts", "/js", "/javascripts"], :root => PactBroker.project_root.join("public")
46
-
47
- logger.info "Mounting UI"
48
- require 'pact_broker/ui/controllers/relationships'
49
- require 'pact_broker/ui/controllers/groups'
50
- require 'pact_broker/doc/controllers/app'
51
-
52
- ui = Rack::Builder.new {
53
-
54
- use HtmlFilter
55
-
56
- map "/ui/relationships" do
57
- run PactBroker::UI::Controllers::Relationships
58
- end
59
-
60
- map "/groups" do
61
- run PactBroker::UI::Controllers::Groups
62
- end
63
-
64
- map "/doc" do
65
- run PactBroker::Doc::Controllers::App
66
- end
67
-
68
- map "/" do
69
- run lambda { |env|
70
- # A request for the root path in the browser (not the json index) should
71
- # redirect to ui/relationships
72
- if (env['PATH_INFO'].chomp("/") == "")
73
- [303, {'Location' => 'ui/relationships'},[]]
74
- else
75
- [404, {},[]]
76
- end
77
- }
78
- end
79
- }
46
+ @app.use Rack::Static, :urls => ["/stylesheets", "/css", "/fonts", "/js", "/javascripts"], :root => PactBroker.project_root.join("public")
47
+ @app.use Rack::PactBroker::ConvertFileExtensionToAcceptHeader
80
48
 
81
49
  if configuration.use_hal_browser
82
50
  logger.info "Mounting HAL browser"
83
- @app.use Rack::HalBrowser::Redirect, :exclude => ['/trace', '/network-graph', '/ui']
51
+ @app.use Rack::HalBrowser::Redirect
84
52
  else
85
53
  logger.info "Not mounting HAL browser"
86
54
  end
87
55
 
56
+ logger.info "Mounting UI"
57
+ require 'pact_broker/ui/app'
58
+
88
59
  logger.info "Mounting PactBroker::API"
89
60
  require 'pact_broker/api'
90
61
 
91
- apps = [ui, PactBroker::API]
62
+ apps = [PactBroker::UI::App.new, PactBroker::API]
92
63
 
93
64
  @app.map "/" do
94
65
  run Rack::Cascade.new(apps)
@@ -96,26 +67,6 @@ module PactBroker
96
67
 
97
68
  end
98
69
 
99
- class HtmlFilter
100
-
101
- def initialize app
102
- @app = app
103
- end
104
-
105
- def call env
106
- if accepts_html_and_not_json_or_csv env
107
- @app.call(env)
108
- else
109
- [404, {},[]]
110
- end
111
- end
112
-
113
- def accepts_html_and_not_json_or_csv env
114
- accept = env['HTTP_ACCEPT'] || ''
115
- accept.include?("html") && !accept.include?("json") && !accept.include?("csv")
116
- end
117
-
118
- end
119
70
  end
120
71
 
121
72
  end
@@ -1,4 +1,5 @@
1
1
  require 'logger'
2
+ require 'pathname'
2
3
 
3
4
  module PactBroker
4
5
 
@@ -3,15 +3,14 @@ require 'pact_broker/db'
3
3
  module PactBroker
4
4
 
5
5
  module Models
6
- class Pact < Sequel::Model
6
+ class Pact
7
7
 
8
- set_primary_key :id
9
- associate(:many_to_one, :provider, :class => "PactBroker::Models::Pacticipant", :key => :provider_id, :primary_key => :id)
10
- associate(:many_to_one, :consumer_version, :class => "PactBroker::Models::Version", :key => :version_id, :primary_key => :id)
8
+ attr_accessor :id, :provider, :consumer_version, :consumer, :updated_at, :created_at, :json_content, :consumer_version_number
11
9
 
12
- #Need to work out how to do this properly!
13
- def consumer_version_number
14
- values[:consumer_version_number]
10
+ def initialize attributes
11
+ attributes.each_pair do | key, value |
12
+ self.send(key.to_s + "=", value)
13
+ end
15
14
  end
16
15
 
17
16
  def consumer
@@ -35,6 +34,5 @@ module PactBroker
35
34
  end
36
35
  end
37
36
 
38
- Pact.plugin :timestamps, :update_on_create=>true
39
37
  end
40
38
  end
@@ -0,0 +1,29 @@
1
+ require 'pact_broker/models/pact'
2
+
3
+ module PactBroker
4
+ module Repositories
5
+
6
+ class Pact < Sequel::Model
7
+
8
+ set_primary_key :id
9
+ associate(:many_to_one, :provider, :class => "PactBroker::Models::Pacticipant", :key => :provider_id, :primary_key => :id)
10
+ associate(:many_to_one, :consumer_version, :class => "PactBroker::Models::Version", :key => :version_id, :primary_key => :id)
11
+
12
+ Pact.plugin :timestamps, :update_on_create=>true
13
+
14
+ def to_model
15
+ PactBroker::Models::Pact.new(
16
+ id: id,
17
+ provider: provider,
18
+ consumer: consumer_version.pacticipant,
19
+ consumer_version_number: consumer_version.number,
20
+ consumer_version: consumer_version,
21
+ json_content: json_content,
22
+ updated_at: updated_at,
23
+ created_at: created_at
24
+ )
25
+ end
26
+ end
27
+
28
+ end
29
+ end
File without changes
@@ -1,6 +1,7 @@
1
1
  require 'sequel'
2
2
  require 'pact_broker/logging'
3
3
  require 'ostruct'
4
+ require 'pact_broker/repositories/pact'
4
5
 
5
6
  module PactBroker
6
7
  module Repositories
@@ -9,62 +10,98 @@ module PactBroker
9
10
  include PactBroker::Logging
10
11
 
11
12
  def find_all_pacts_between consumer_name, options
12
- pact_finder(consumer_name, options.fetch(:and))
13
- .left_outer_join(:tags, {:version_id => :id}, {implicit_qualifier: :versions})
14
- .reverse_order(:order).all
13
+ to_models do
14
+ pact_finder(consumer_name, options.fetch(:and))
15
+ .left_outer_join(:tags, {:version_id => :id}, {implicit_qualifier: :versions})
16
+ .reverse_order(:order)
17
+ end
15
18
  end
16
19
 
17
20
  def find_by_version_and_provider version_id, provider_id
18
- PactBroker::Models::Pact.where(version_id: version_id, provider_id: provider_id).single_record
21
+ to_model do
22
+ PactBroker::Repositories::Pact.where(version_id: version_id, provider_id: provider_id).single_record
23
+ end
19
24
  end
20
25
 
21
26
  def find_latest_pacts
22
- # Need to use aliases because sqlite returns row with `` in the column name, mysql does not
27
+ # Need to use aliases because sqlite returns row with `` in the column name,
28
+ # mysql does not
23
29
  db[:latest_pacts].select(:id,
24
30
  :consumer_id___consumer_id, :consumer_name___consumer_name,
25
31
  :provider_id___provider_id, :provider_name___provider_name,
26
- :consumer_version_number___consumer_version_number).all.collect do | row |
32
+ :consumer_version_number___consumer_version_number,
33
+ :created_at___created_at,
34
+ :updated_at___updated_at).all.collect do | row |
27
35
  row_to_pact row
28
36
  end
29
37
 
30
38
  end
31
39
 
32
40
  def find_latest_pact(consumer_name, provider_name, tag = nil)
33
- finder = pact_finder(consumer_name, provider_name)
34
- finder = add_tag_criteria(finder, tag) unless tag.nil?
35
- finder.order(:order).last
41
+ to_model do
42
+ finder = pact_finder(consumer_name, provider_name)
43
+ finder = add_tag_criteria(finder, tag) unless tag.nil?
44
+ finder.order(:order).last
45
+ end
36
46
  end
37
47
 
38
48
  def find_pact consumer_name, consumer_version, provider_name
39
- pact_finder(consumer_name, provider_name).where('versions.number = ?', consumer_version).single_record
49
+ to_model do
50
+ pact_finder(consumer_name, provider_name)
51
+ .where('versions.number = ?', consumer_version)
52
+ .single_record
53
+ end
40
54
  end
41
55
 
42
56
  def create params
43
- PactBroker::Models::Pact.new(version_id: params[:version_id], provider_id: params[:provider_id], json_content: params[:json_content]).save
57
+ to_model do
58
+ Pact.new(
59
+ version_id: params[:version_id],
60
+ provider_id: params[:provider_id],
61
+ json_content: params[:json_content]
62
+ ).save
63
+ end
44
64
  end
45
65
 
46
- def create_or_update params
47
- if pact = find_by_version_and_provider(params[:version_id], params[:provider_id])
48
- pact.update_fields(json_content: params[:json_content])
49
- else
50
- create params
66
+ def update id, params
67
+ to_model do
68
+ Pact.find(id: id).tap do | pact |
69
+ pact.update(json_content: params[:json_content])
70
+ end
51
71
  end
52
72
  end
53
73
 
54
74
  def find_previous_pact pact
55
- previous_pact = db[:all_pacts].where(:consumer_id => pact.consumer.id, :provider_id => pact.provider.id).where('consumer_version_order < ?', pact.consumer_version.order).order(:consumer_version_order).last
75
+ previous_pact = db[:all_pacts]
76
+ .where(
77
+ :consumer_id => pact.consumer.id,
78
+ :provider_id => pact.provider.id)
79
+ .where('consumer_version_order < ?', pact.consumer_version.order)
80
+ .order(:consumer_version_order)
81
+ .last
56
82
  previous_pact ? row_to_pact(previous_pact) : nil
57
83
  end
58
84
 
59
85
  private
60
86
 
87
+ def to_model
88
+ database_model = yield
89
+ database_model ? database_model.to_model : nil
90
+ end
91
+
92
+ def to_models
93
+ database_models = yield
94
+ database_models.collect(&:to_model)
95
+ end
96
+
61
97
  def db
62
98
  PactBroker::Models::Version.new.db
63
99
  end
64
100
 
65
101
  def pact_finder consumer_name, provider_name
66
- PactBroker::Models::Pact.select(
67
- :pacts__id, :pacts__json_content, :pacts__version_id, :pacts__provider_id, :pacts__created_at, :pacts__updated_at,
102
+ PactBroker::Repositories::Pact.select(
103
+ :pacts__id, :pacts__json_content, :pacts__version_id, :pacts__provider_id,
104
+ :pacts__created_at, :pacts__updated_at,
68
105
  :versions__number___consumer_version_number).
69
106
  join(:versions, {:id => :version_id}, {implicit_qualifier: :pacts}).
70
107
  join(:pacticipants, {:id => :pacticipant_id}, {:table_alias => :consumers, implicit_qualifier: :versions}).
@@ -86,8 +123,16 @@ module PactBroker
86
123
  consumer.id = row[:consumer_id]
87
124
  provider = Models::Pacticipant.new(name: row[:provider_name])
88
125
  provider.id = row[:provider_id]
89
- consumer_version = OpenStruct.new(number: row[:consumer_version_number], pacticipant: consumer)
90
- pact = OpenStruct.new(id: row[:id], consumer: consumer, consumer_version: consumer_version, provider: provider, consumer_version_number: row[:consumer_version_number])
126
+ consumer_version = OpenStruct.new(
127
+ number: row[:consumer_version_number],
128
+ pacticipant: consumer)
129
+ pact = Models::Pact.new(id: row[:id],
130
+ consumer: consumer,
131
+ consumer_version: consumer_version,
132
+ provider: provider,
133
+ consumer_version_number: row[:consumer_version_number],
134
+ created_at: row[:created_at],
135
+ updated_at: row[:updated_at])
91
136
  end
92
137
 
93
138
  end